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

braid-blob

v0.0.82

Published

Library for collaborative blobs over http using braid.

Readme

Braid-Blob

A library for synchronizing binary blobs (files, images, etc.) consistently over Braid-HTTP. Guarantees all peers (HTTP clients and servers) resolve to the same version of blob.

Blobs can be read written locally, or over the network.
Blobs are stored on disk in a folder.

Quick Start

Install this library in your Javascript project:

npm install braid-blob

And now use it to serve HTTP requests with:

// Create a HTTP server
require('http').createServer((req, res) => {

    // At any point, use braid-blob to respond to any GET, PUT, or DELETE request
    require('braid-blob').serve(req, res)

}).listen(8888)

That's it! You are now serving synchronized binary blobs at http://localhost:8888/*.

Upload an image to it:

curl -X PUT -H "Content-Type: image/png" -T blob.png http://localhost:8888/blob.png

You can view it at http://localhost:8888/blob.png.
A browser client is described below.

Interactive Demo

Run the demo web server with:

node server-demo.js

Now open up http://localhost:8888 in your browser, to see the client. Open two windows. You can drag and drop images between them, and they will always stay synchronized.

Network API

Braid-blob speaks Braid-HTTP, an extension to HTTP for synchronization.

Special Braid-HTTP Headers

| Header | Description | |--------|-------------| | Version | Unique identifier for this version of the blob (e.g., "1768467700.000") | | Version-Type | How to interpret the structure of version strings (e.g., wallclockish); see Version-Type spec | | Parents | The previous version | | Merge-Type | How conflicts resolve consistently (e.g. aww for arbitrary-writer-wins) | | Subscribe | In GET, subscribes client to all future changes | | Accept-Subscribe | Server indicates it supports subscriptions | | Current-Version | The latest version that the server is aware of |

GET retrieves a blob

GET /blob.png HTTP/1.1

Response:

HTTP/1.1 200 OK
Version: "1768467700.000"
Version-Type: wallclockish
Content-Type: image/png
Merge-Type: aww
Accept-Subscribe: true
Content-Length: 12345

<binary data>

Returns 404 Not Found if the blob doesn't exist.

GET with Subscribe syncs client with realtime updates

Add Subscribe: true to receive updates whenever the blob changes:

GET /blob.png HTTP/1.1
Subscribe: true

Response (keeps connection open, streams updates):

HTTP/1.1 209 Multiresponse
Subscribe: true
Current-Version: "1768467700.000"
Version-Type: wallclockish

HTTP 200 OK
Version: "1768467700.000"
Version-Type: wallclockish
Content-Type: image/png
Merge-Type: aww
Content-Length: 12345

<binary data>

HTTP 200 OK
Version: "1768467700.100"
Version-Type: wallclockish
Content-Type: image/png
Merge-Type: aww
Content-Length: 23456

<new binary data>
...

If the blob doesn't exist yet, Current-Version will be blank and no initial update is sent. If the blob is deleted, a 404 update is streamed.

PUT stores a blob

PUT /blob.png HTTP/1.1
Version: "1768467700.200"
Version-Type: wallclockish
Content-Type: image/png
Merge-Type: aww
Content-Length: 34567

<binary data>

Response:

HTTP/1.1 200 OK
Current-Version: "1768467700.200"
Version-Type: wallclockish

If the sent version is older or eclipsed by the server's current version, the returned Current-Version will be the server's version (not the one you sent).

The braid_blob.serve() method (below) will accept every PUT sent to it, but you can implement access control for any request before passing it to serve(), and return e.g. 401 Unauthorized if you do no want to allow the PUT.

DELETE removes a blob

DELETE /blob.png HTTP/1.1

Response:

HTTP/1.1 200 OK

Returns 200 OK even if the blob didn't exist.

Understanding versions and conflict resolution

Braid-blob uses two complementary mechanisms for distributed consistency:

Version-Type: wallclockish defines the format and interpretation of version identifiers:

  • Versions are seconds since the epoch with decimal precision (e.g., "1768467700.000")
  • If the current time is behind the latest known version, a small random decimal is added to the current version, providing entropy when multiple peers write simultaneously
  • Versions are compared numerically—larger timestamps are newer

Merge-Type: aww (arbitrary-writer-wins) defines how conflicts are resolved:

  • When two peers make concurrent changes, the version with the higher timestamp wins
  • All peers deterministically converge to the same state without coordination
  • This provides "last-writer-wins" semantics under normal clock conditions

Nodejs API

Import and configure braid-blob with:

var braid_blob = require('braid-blob')

// Optional: set custom blob storage folder
braid_blob.db_folder = './braid-blobs'       // Default: ./braid-blobs

Examples

Get a blob

// Get a local blob:
var {body, version, content_type} = await braid_blob.get('foo')

// Get a remote blob:
var {body, version, content_type} = await braid_blob.get(new URL('https://foo.bar/baz'))

// Get a specific version of a remote blob:
var {body} = await braid_blob.get(
    new URL('https://foo.bar/baz'),
    {version: ['1768467700.000']}
)

// To subscribe to a remote blob, without storing updates locally:
await braid_blob.get(new URL('https://foo.bar/baz'), {
    subscribe: (update) => {
        console.log('Got update:', update.version, update.content_type)
        // update.body contains the new blob data
    }
})

// To mirror a remote blob to local storage (bidirectional sync):
var ac = new AbortController()
braid_blob.sync('local-key', new URL('https://foo.bar/baz'), {signal: ac.signal})
// Later, stop syncing:
ac.abort()

Note: .get() with subscribe receives updates but does not store them locally. .sync() performs two subscriptions (local↔remote) plus auto-forwards updates in both directions.

Put a blob

// Write to a local blob:
await braid_blob.put('foo', Buffer.from('hello'), {content_type: 'text/plain'})

// Write to a remote blob:
await braid_blob.put(
    new URL('https://foo.bar/baz'),
    Buffer.from('hello'),
    {content_type: 'text/plain'}
)

Delete a blob

// Delete a local blob:
await braid_blob.delete('foo')

// Delete a remote blob:
await braid_blob.delete(new URL('https://foo.bar/baz'))

API Reference

braid_blob.get(key, params)

Retrieves a blob from local storage or a remote URL.

Parameters:

  • key - The local blob (if string) or remote URL (if URL object) to read from
  • params - Optional configuration object
    • version - Retrieve a specific version instead of the latest (e.g., ['1768467700.000'])
    • parents - When subscribing, only receive updates newer than this version (e.g., ['1768467700.000'])
    • subscribe - Callback (update) => {} called with each update; update has {body, version, content_type}
    • head - If true, returns only metadata ({version, content_type}) without the body—useful for checking if a blob exists or getting its current version
    • content_type - Expected content type (sent as Accept header for remote URLs)
    • signal - AbortSignal to cancel the request or stop a subscription

Returns: {version, body, content_type} object, or null if the blob doesn't exist. When subscribing to a remote URL, returns the fetch response object; updates are delivered via the callback.

braid_blob.put(key, body, params)

Writes a blob to local storage or a remote URL. Any other peers synchronizing with this blob (via .serve(), .sync(), or .get(.., {subscribe: ..})) will be updated.

Parameters:

  • key - The local blob (if string) or remote URL (if URL object) to write to
  • body - The data to store (Buffer, ArrayBuffer, or Uint8Array)
  • params - Optional configuration object
    • version - Specify a version ID for this write (e.g., ['1768467700.000']); if omitted, one is generated automatically
    • content_type - MIME type of the blob (e.g., 'image/png', 'application/json')
    • signal - AbortSignal to cancel the request

braid_blob.delete(key, params)

Deletes a blob from local storage or a remote URL.

Parameters:

  • key - The local blob (if string) or remote URL (if URL object) to delete
  • params - Optional configuration object
    • signal - AbortSignal for cancellation

braid_blob.sync(key, url, params)

Synchronizes a remote URL bidirectionally with a local blob on disk. This performs two subscriptions (one to the remote, one to the local blob) and auto-forwards updates in both directions.

Parameters:

  • key - The local blob on disk (string)
  • url - Remote URL (URL object)
  • params - Optional configuration object
    • signal - AbortSignal for cancellation (use to stop sync)
    • content_type - Content type for requests

braid_blob.serve(req, res, params)

Serves blob requests over HTTP. Synchronizes the client issuing the given request with its blob on disk.

Parameters:

  • req - HTTP request object
  • res - HTTP response object
  • params - Optional configuration object
    • key - The blob on disk to sync with (default: the path from req.url)

Browser Client API

A simple browser client (client.js) is included for subscribing to blob updates. Here's how to use it:

<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
<script src="https://unpkg.com/braid-blob/client.js"></script>
<img id="image"/>
<script>
    braid_blob_client('http://localhost:8888/blob.png', {
        // Called whenever the blob is updated
        on_update: (blob, content_type, version) =>
            image.src = URL.createObjectURL(
                new Blob([blob], { type: content_type })),
        on_delete: () => image.src = '',
        on_error: (e) => console.error('Error:', e)
    })
</script>

Subscribe to remote blob

braid_blob_client(url, params)

Subscribes to a blob endpoint, and calls params.on_update() with each update.

Parameters:

  • url - The blob endpoint URL
  • params - Configuration object
    • on_update(blob, content_type, version) - Callback for updates
    • on_delete - Callback when blob is deleted
    • on_error(e) - Callback for errors
    • signal - AbortSignal for cancellation

Live Image Polyfill

A polyfill for real-time collaborative images. Add live to any <img> element to keep it synced across all clients, and add droppable to let users drag-and-drop or paste new images directly onto it.

<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
<script src="https://unpkg.com/braid-blob/client.js"></script>
<script src="https://unpkg.com/braid-blob/img-live.js"></script>

<!-- Read-only: stays in sync with the server -->
<img live src="/blob.png">

<!-- Read-write: users can drag-and-drop or paste to update -->
<img live droppable src="/blob.png">

That's it! When any client updates /blob.png, all <img live src="/blob.png"> elements across all clients will update in real-time. With droppable, users can drag and drop an image file onto the element, or click it and paste from the clipboard — the new image is uploaded to the server and synced to everyone.

Under the hood, the polyfill:

  • Observes the DOM for <img live> elements (added, removed, or attribute changes)
  • Creates a braid_blob_client subscription for each unique image URL, shared across elements
  • Appends a cache-busting query parameter (e.g. ?img-live=k7x2f9m) to ensure the browser doesn't serve stale versions
  • Cleans up subscriptions when images are removed from the DOM

Improving this Package

You can run the nodejs tests with:

npm install
node test/test.js

Or run the browser tests with:

node test/test.js -b

Then open http://localhost:8889/test.html