JSPM

@grayfalcon666/loom

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q59619F
  • License MIT

A Go/WASM CRDT engine for collaborative text editing — runs in the browser with no build step

Package Exports

  • @grayfalcon666/loom
  • @grayfalcon666/loom/loom.js
  • @grayfalcon666/loom/loom.wasm
  • @grayfalcon666/loom/slim

Readme

@grayfalcon666/loom

A Go-powered CRDT engine for collaborative text editing, compiled to WebAssembly and running entirely in the browser — no backend required for the CRDT itself.

Features

  • RGA (Replicated Growable Array) — character-level CRDT text editing with concurrent insert/delete support
  • LWW-Map — key-value CRDT map
  • Zero-config init — WASM is embedded as base64, npm install and go
  • Full TypeScript type declarations included
  • Go + syscall/js — all CRDT logic runs in the browser via WASM

Install

npm install @grayfalcon666/loom

Quick Start

import { init } from '@grayfalcon666/loom';

const loom = await init();           // WASM loaded from embedded base64 — no extra files

const aliceDoc = loom.newDocument('alice');
const bobDoc   = loom.newDocument('bob');

loom.addText(aliceDoc, 'body', 'shared-text');

// Alice inserts 'H' at position 0
const op = loom.insertChar(aliceDoc, 'shared-text', 0, 'H'.charCodeAt(0));
// Send op to your WebSocket server → peers receive it

// Bob applies the remote op
loom.unwrapReceive(bobDoc, op);

console.log(loom.peekText(bobDoc, 'shared-text')); // 'H'

WebSocket Sync

loom does not include a WebSocket server. Any WS relay works. See the server implementation for a reference Go backend.

const WS_URL = 'ws://localhost:8080/ws?doc=my-doc';
const ws = new WebSocket(WS_URL);

// Send local op to peers
ws.addEventListener('open', () => {
  const op = loom.insertChar(doc, 'text-1', 0, 'X'.charCodeAt(0));
  ws.send(op);
});

// Apply remote op from peers
ws.addEventListener('message', (e) => {
  loom.unwrapReceive(doc, e.data);
});

API Reference

init(wasmUrl?)

Initialize the WASM module.

  • No argument (default): uses the embedded base64-encoded WASM. Zero network requests, no CORS issues.
  • wasmUrl: load from a custom URL. Useful if you want to host loom.wasm yourself.
// Zero-config
const loom = await init();

// Custom URL (e.g. from /slim export or CDN)
const loom = await init('https://cdn.example.com/loom.wasm');

Document Management

Method Returns Description
newDocument(actorID) number (docID) Create a CRDT document
freeDocument(docID) void Free memory
save(docID) string (JSON) Serialize document
load(snapshotJSON) number (new docID) Load from snapshot
getActorID(docID) string Get actor ID

Text Editing

Method Returns Description
addText(docID, key, textObjID) string (Op JSON) Declare shared text object
insertChar(docID, textObjID, index, charCode) string (Op JSON) Insert character
deleteChar(docID, textObjID, index) string (Op JSON) Delete character
peekText(docID, textObjID) string Get current text
  • insertChar / deleteChar return an Op JSON string — send this to your WebSocket server
  • addText also returns an Op — call it on all peers before inserting
  • unwrapReceive(docID, opJSON) applies a remote Op to a document

LWW-Map

Method Returns Description
set(docID, key, value) string (Op JSON) Set key-value
get(docID, key) string (JSON) Get value

Package Structure

@grayfalcon666/loom/
├── index.js          # Main entry — init() with embedded base64 WASM
├── loom.js           # Raw JS wrapper (same as index.js but older API)
├── loom.d.ts         # TypeScript declarations
├── loom.wasm         # Raw .wasm binary (used if you pass url to init())
├── wasm_exec.js      # Go WASM bootstrap (required by Go runtime)
└── embedded_wasm.js  # AUTO-GENERATED: base64-encoded loom.wasm

Saving / Loading

// Save to localStorage
localStorage.setItem('doc', loom.save(docID));

// Load on page refresh
const id = loom.load(localStorage.getItem('doc'));

Subpath Exports

// Full package (base64 WASM embedded — recommended)
import { init } from '@grayfalcon666/loom';

// Slim version — requires you to host loom.wasm separately
// (loom.wasm is ~3MB; slim skips the embedded base64 for smaller JS bundle)
import { init } from '@grayfalcon666/loom/slim';
const loom = await init('/node_modules/@grayfalcon666/loom/loom.wasm');

TypeScript

Full TypeScript declarations are included. No @types/ package needed.

import { init, type LoomAPI } from '@grayfalcon666/loom';

const loom: LoomAPI = await init();
const docID: number = loom.newDocument('alice');

Limitations

  • syscall/js is the standard Go WASM path. Note that Go has deprecated syscall/js and the recommended future path is TinyGo + wasm-tools.
  • This package is currently at v1.0.0 — API is stabilizing but breaking changes are possible in minor releases until v2.
  • The WASM is large (~3MB) because it includes the full Go runtime. Consider using the /slim export if bundle size matters.

License

MIT