npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

wrap-data

v0.3.18

Published

wrap-data

Downloads

14

Readme

wrap-data

Wrap data object into reactive streams, with helpers like unwrap, get, set, unset etc.

Build Status NPM Version Join the chat at https://gitter.im/wrap-data/Lobby

Install

NPM

npm i -S wrap-data

Browser

<script src="https://unpkg.com/wrap-data"></script>
<script>
    // wrapData is a global
    wrapData(...)
</script>

Usage

First you need a stream helper function or library, which conforms to the fantasy land applicative specification, flyd is recommended.

- Convert existing data and use wrapped data

const flyd = require('flyd')
const wrapData = require('wrap-data')
const data = {
    firstName: 'Hello',
    lastName: 'World'
}
const model = wrapData(flyd.stream)(data)
// model, and everything inside model is a stream!

// manually access a data
model().firstName  // stream(Hello)
model().lastName  // stream(World)

model.set('address', {city: 'Mercury'})  // set model.address

model().address().city()  // get value: 'Mercury'
model().address().city('Mars')  // set value: 'Mars'

const city = model.get('address.city')  //stream(Mars)
city()  // get value: 'Mars'
city('Earth')  // set value: 'Earth'

model.unwrap('address')  // {city: 'Earth'}
model.unset('address')   // unset model.address

model.unwrap() // {firstName: 'Hello', lastName: 'World'}

- Observe data changes

The root model has a change stream, you can get callback from every data changes.

// start observe model changes
const update = model.change.map(({value, type})=>{
    console.log('data mutated:', value.path, type, value.unwrap())
})

model.set('address.city', 'Mars')
// [console] data mutated: [ 'address', 'city' ] add Mars
model.get('address.city')('Earth')
// [console] data mutated: [ 'address', 'city' ] change Earth
model.unset('address.city')
// [console] data mutated: [ 'address', 'city' ] delete Earth

// stop observe model changes
update.end(true)

- Define data relations

You can define data relations using combine, scan etc., and unwrap will unwrap them automatically, you can nest any level of streams.

const firstName = model.get('firstName')
const lastName = model.get('lastName')
const fullName = flyd.combine(
  (a, b) => a() + ' ' + b(),
  [firstName, lastName]
)
model.set('fullName', fullName)
fullName.map(console.log)   // [console] Hello World
firstName('Green')          // [console] Green World

model.set('age', flyd.stream(flyd.stream(20)))
model.unwrap()
// {firstName:'Green', lastName:'World', fullName:'Green World', age:20}

- Use in React

const model = wrapData(flyd.stream)({user: {name: 'earth'}})

class App extends React.Component {
    constructor(props){
        super(props)
        const {model} = this.props
        
        this.update = model.change.map(({value, type})=>{
            this.forceUpdate()
        })
        
        this.onChange = e => {
            const {name, value} = e.target
            model.set(name, value)
        }
    }
  
    componentWillUnmount(){
        this.update.end(true)
    }
    
    render(){
        const {model} = this.props
        const userName = model.unwrap('user.name')
        return <div>
            <h3>Hello {userName}</h3>
            <input name='user.name' value={userName} onChange={this.onChange} />
        </div>
    }
}

ReactDOM.render(<App model={model} />, app)

You can play with the demo here

API

- wrapData = require('wrap-data')

The lib expose a default wrapData function to use

- wrapFactory = wrapData(stream)

the wrapFactory is used to turn data into wrapped_data.

A wrapped_data is just a stream, with some helper methods added to it, like get, set etc.

return: function(data) -> wrapped_data

var flyd = require('flyd')
var wrapFactory = wrapData(flyd.stream)

- root = wrapFactory(data: any)

the root is a wrapped_data, with all nested data wrapped.

return: wrapped_data for data

root.change is also a stream, you can map it to receive any data changes inside.

Any data inside root is a wrapped_data, and may be contained by {} or [] stream, keep the same structure as before.

Any wrapped_data have root and path propperties, get, set, ... helper functions.

var root = wrapFactory({x: {y: {z: 1}}})
root().x().y().z()  // 1
root.change.map(({value, type})=>{ console.log(value, type) })
root().x().y().z(2)

- wrapped_data.get(path: string|string[])

get nested wrapped data from path, path is array of string or dot(".") seperated string.

return: wrapped_data at path

var z = root.get('x.y.z')
// or
var z = root.get(['x','y','z'])
z() //2
z(10)

- wrapped_data.set(path?: string|string[], value?: any, descriptor?: object)

set nested wrapped data value from path, same rule as get method. The descriptor only applied when path not exists.

return: wrapped_data for value, at path

path can contain a.[3] alike string denote 3 is an array element of a.

value can be any data types, if path is omitted, set value into wrapped_data itself.

If value is a stream, then it's an atom data, which will not be wrapped inside.

descriptor is optional, same as 3rd argument of Object.defineProperty, this can e.g. create non-enumerable stream which will be hidden when unwrap.

If data not exist in path, all intermediate object will be created.

var z = root.set('x.a', 10)
z()  // 10

// same as: (only if x.a exits)
root.get('x.a').set(10)
root.get('x.a')(10)

var z = root.set('x.c', [], {enumerable: false})  // c is non-enumerable
Object.keys( z.get('x')() )  // ['a']

root.unwrap()  // {x: {y: {z: 1}}, a: 10}  // `c` is hidden!

root.set(`arr.[0]`, 10)
root.get('arr.0')()  // 10

root.unwrap()  // {x: {y: {z: 1}}, a: 10, arr:[10]}  // `arr` is array!

- wrapped_data.getset(path?: string|string[], function(prevValue, empty?: boolean)->newValue, descriptor: object)

like set, but value is from a function, it let you set value based on previous value, the descriptor only applied when empty is true.

return: wrapped_data for newValue, at path

var z = root.getset('x.a', val=>val+1)
z()  // 11

- wrapped_data.ensure(path: string|string[], value?: any, descriptor?: object)

like set, but only set when the path not exists, otherwise perform a get operation.

return: wrapped_data at path

var z = root.ensure('x.a', 5)
// x.a exists, so perform a get, `5` ignored
z()  // 11

var z = root.ensure('x.b', 5)
// x.b not exists, so perform a `set`
z()  // 5

- wrapped_data.unset(path: string|string[])

delete wrapped_data or value in path

return: deleted data been unwrapped

var z = root.unset('x.b')
z // 5

- wrapped_data.unwrap(path?: string|string[], config?: {json: true})

unwrap data and nested data while keep data structure, any level of wrapper on any data will be stripped.

If set config arg with {json: true}, then any circular referenced data will be set undefined, suitable for JSON.stringify.

return: unwrapped data

var z = root.unwrap()

z // {x: {y: {z: 11}}, a: [10]},   x.c is hidden

- wrapped_array.push(value: any)

push new value into wrapped data when it's array, all the inside will be wrapped.

return: newly pushed wrapped_data

var z = root.set('d', [])
z.push({v: 10})
z.get('d.0.v')()  // 10

- wrapped_array.pop()

pop and unwrap last element in wrapped array.

return: unwrapped data in last array element

var z = root.ensure('d', [])
z.get('d').pop()  // {v: 10}