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 🙏

© 2026 – Pkg Stats / Ryan Hefner

conjoiner

v0.0.3

Published

Bindings and controllers using ES6 proxies

Readme

Conjoiner

Conjoiner is intended to provide a simple native framework for MVC front end applications. It is born out of a frustration with Angular and an excitement about new ES6 developments like Proxies Custom Elements/Web Components along with new HTML constructs such as the template tag and the slot tag.

The notifier part is first out of the blocks - I hope to make the various parts individually useful so they are seperate exports.

The transpiler world still struggles a little with ES6 imports and the best feature of CommonJS/require, granular modules grouped together into a whole piece of code, struggles with ES6 import depth restrictions in ways I haven't bottomed out yet so the modules are grouped using require

Since class syntax is advertised as being for the most part syntactic sugar rather than a novel way of creating class-based code the old prototype syntax is used. This may change if the trees deepen as it does seem to make calling superclass methods simpler.

Usage

Notifier

The notifier provides the two-way bindings. It will return a proxy with bindings wherever a property field is an object, so deep properties can be bound to using dot notation in the usual way. Arrays are currently returned as is - it seems unwise to add too much overhead to iterables if the array elements themselves turn out to be themselves objects. There is an assumption that array elements will not be individually bound to but rather singled out for having a notifier of their own set. What there is of this is not guaranteed to be stable but likely to be. The most likely thing to change is the structure of the modules as views/web components are added in.

From your project root run:

npm install conjoiner --save

Then in your javascript:

		const Notifier = require('conjoiner').Notifier
		let model = {
			someProperty: "foo",
			someFunction: par => { console.log('Parameter => ', par)}
		}
		const notifier = new Notifier(model)
		model = notifier.model
		const el = document.createElement('span')

Add some bindings:

		const propertyBinding = {target: el, event: 'propertyChange', property: 'someProperty'}
		notifier.addBinding(propertyBinding)
		el.addEventListener('propertyChange', function(ev){
			if(ev.detail.property === 'someProperty') {
				console.log('Got event ', ev.detail.property, ' changed to ', ev.detail.value)
			}
		})

Then make a change to the notifier.model.someProperty field:

		notifier.model.someProperty = 'bar'
		//expected result: 'Got event someProperty changed to bar'

A more useful pattern might be to use the base controller, which also takes a view as an argument, and extend this to make controllers with bindings (which is the target goal of this part of the project):

   	const BaseController = require('conjoiner').BaseController
   	function ReadingContext(model, view){
   	  BaseController.call(this, model, view)
   	  this.model.addBinding({target: view, event: 'propertyChange', property: 'someProperty'})
   	  this.view.addEventListener('change', ev => this.model.someProperty = this.view.value)
   	}
   	ReadingContext.prototype = Object.create(BaseController.prototype);
   	ReadingContext.prototype.constructor = ReadingContext;

Available events:

  • propertyChange : *detail.property: property modified *detail.value: new value
  • functionCall: *detail.property: name of function called *detail.completed: true for synchronous or completed asynchronous, false for pending asynchronous function *detail.arguments: arguments *detail.result: undefined for pending (where completed is falese) o/w return value of function/resolution of promise

Tests

The tests can be run with npm test in the usual way. At the moment the script in test also transpiles and relies on browserify being available locally this may change. The headless browser landscape seems to be in some flux at the moment so test will open a browser and run the tests in there.