loro-crdt
Version:
Loro CRDTs is a high-performance CRDT framework that makes your app state synchronized, collaborative and maintainable effortlessly.
1,508 lines (1,436 loc) • 255 kB
JavaScript
let imports = {};
imports['__wbindgen_placeholder__'] = module.exports;
let wasm;
const { TextEncoder, TextDecoder } = require(`util`);
const heap = new Array(128).fill(undefined);
heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; }
let WASM_VECTOR_LEN = 0;
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}
let cachedTextEncoder = new TextEncoder('utf-8');
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
});
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len, 1) >>> 0;
const mem = getUint8ArrayMemory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
function handleError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
wasm.__wbindgen_exn_store(addHeapObject(e));
}
}
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
cachedTextDecoder.decode();
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
function dropObject(idx) {
if (idx < 132) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
function isLikeNone(x) {
return x === undefined || x === null;
}
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(state => {
wasm.__wbindgen_export_4.get(state.dtor)(state.a, state.b)
});
function makeMutClosure(arg0, arg1, dtor, f) {
const state = { a: arg0, b: arg1, cnt: 1, dtor };
const real = (...args) => {
// First up with a closure we increment the internal reference
// count. This ensures that the Rust closure environment won't
// be deallocated while we're invoking it.
state.cnt++;
const a = state.a;
state.a = 0;
try {
return f(a, state.b, ...args);
} finally {
if (--state.cnt === 0) {
wasm.__wbindgen_export_4.get(state.dtor)(a, state.b);
CLOSURE_DTORS.unregister(state);
} else {
state.a = a;
}
}
};
real.original = state;
CLOSURE_DTORS.register(real, state, state);
return real;
}
function debugString(val) {
// primitive types
const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) {
return `${val}`;
}
if (type == 'string') {
return `"${val}"`;
}
if (type == 'symbol') {
const description = val.description;
if (description == null) {
return 'Symbol';
} else {
return `Symbol(${description})`;
}
}
if (type == 'function') {
const name = val.name;
if (typeof name == 'string' && name.length > 0) {
return `Function(${name})`;
} else {
return 'Function';
}
}
// objects
if (Array.isArray(val)) {
const length = val.length;
let debug = '[';
if (length > 0) {
debug += debugString(val[0]);
}
for(let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]);
}
debug += ']';
return debug;
}
// Test for built-in
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
let className;
if (builtInMatches && builtInMatches.length > 1) {
className = builtInMatches[1];
} else {
// Failed to match the standard '[object ClassName]'
return toString.call(val);
}
if (className == 'Object') {
// we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`.
try {
return 'Object(' + JSON.stringify(val) + ')';
} catch (_) {
return 'Object';
}
}
// errors
if (val instanceof Error) {
return `${val.name}: ${val.message}\n${val.stack}`;
}
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
function _assertClass(instance, klass) {
if (!(instance instanceof klass)) {
throw new Error(`expected instance of ${klass.name}`);
}
}
let stack_pointer = 128;
function addBorrowedObject(obj) {
if (stack_pointer == 1) throw new Error('out of js stack');
heap[--stack_pointer] = obj;
return stack_pointer;
}
function passArray8ToWasm0(arg, malloc) {
const ptr = malloc(arg.length * 1, 1) >>> 0;
getUint8ArrayMemory0().set(arg, ptr / 1);
WASM_VECTOR_LEN = arg.length;
return ptr;
}
function getArrayU8FromWasm0(ptr, len) {
ptr = ptr >>> 0;
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
}
function getArrayJsValueFromWasm0(ptr, len) {
ptr = ptr >>> 0;
const mem = getDataViewMemory0();
const result = [];
for (let i = ptr; i < ptr + 4 * len; i += 4) {
result.push(takeObject(mem.getUint32(i, true)));
}
return result;
}
/**
* Redacts sensitive content in JSON updates within the specified version range.
*
* This function allows you to share document history while removing potentially sensitive content.
* It preserves the document structure and collaboration capabilities while replacing content with
* placeholders according to these redaction rules:
*
* - Preserves delete and move operations
* - Replaces text insertion content with the Unicode replacement character
* - Substitutes list and map insert values with null
* - Maintains structure of child containers
* - Replaces text mark values with null
* - Preserves map keys and text annotation keys
*
* @param {Object|string} jsonUpdates - The JSON updates to redact (object or JSON string)
* @param {Object} versionRange - Version range defining what content to redact,
* format: { peerId: [startCounter, endCounter], ... }
* @returns {Object} The redacted JSON updates
* @param {string | JsonSchema} json_updates
* @param {any} version_range
* @returns {JsonSchema}
*/
module.exports.redactJsonUpdates = function(json_updates, version_range) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.redactJsonUpdates(retptr, addHeapObject(json_updates), addHeapObject(version_range));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
/**
* @param {Uint8Array} bytes
* @returns {{ peer: PeerID, counter: number }[]}
*/
module.exports.decodeFrontiers = function(bytes) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.decodeFrontiers(retptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
function passArrayJsValueToWasm0(array, malloc) {
const ptr = malloc(array.length * 4, 4) >>> 0;
const mem = getDataViewMemory0();
for (let i = 0; i < array.length; i++) {
mem.setUint32(ptr + 4 * i, addHeapObject(array[i]), true);
}
WASM_VECTOR_LEN = array.length;
return ptr;
}
/**
* @param {({ peer: PeerID, counter: number })[]} frontiers
* @returns {Uint8Array}
*/
module.exports.encodeFrontiers = function(frontiers) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArrayJsValueToWasm0(frontiers, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.encodeFrontiers(retptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
if (r3) {
throw takeObject(r2);
}
var v2 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v2;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
/**
* Decode the metadata of the import blob.
*
* This method is useful to get the following metadata of the import blob:
*
* - startVersionVector
* - endVersionVector
* - startTimestamp
* - endTimestamp
* - mode
* - changeNum
* @param {Uint8Array} blob
* @param {boolean} check_checksum
* @returns {ImportBlobMetadata}
*/
module.exports.decodeImportBlobMeta = function(blob, check_checksum) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(blob, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.decodeImportBlobMeta(retptr, ptr0, len0, check_checksum);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
};
/**
* Get the version of Loro
* @returns {string}
*/
module.exports.LORO_VERSION = function() {
let deferred1_0;
let deferred1_1;
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.LORO_VERSION(retptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
deferred1_0 = r0;
deferred1_1 = r1;
return getStringFromWasm0(r0, r1);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
}
};
module.exports.run = function() {
wasm.run();
};
module.exports.callPendingEvents = function() {
wasm.callPendingEvents();
};
/**
* Enable debug info of Loro
*/
module.exports.setDebug = function() {
wasm.setDebug();
};
function __wbg_adapter_60(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h23bd7b34cf0bced7(arg0, arg1, addHeapObject(arg2));
}
function __wbg_adapter_63(arg0, arg1) {
wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hd82b624f21a7fa96(arg0, arg1);
}
const AwarenessWasmFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_awarenesswasm_free(ptr >>> 0, 1));
/**
* `Awareness` is a structure that tracks the ephemeral state of peers.
*
* It can be used to synchronize cursor positions, selections, and the names of the peers.
*
* The state of a specific peer is expected to be removed after a specified timeout. Use
* `remove_outdated` to eliminate outdated states.
*/
class AwarenessWasm {
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
AwarenessWasmFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_awarenesswasm_free(ptr, 0);
}
/**
* Get the state of all peers.
* @returns {{[peer in PeerID]: unknown}}
*/
getAllStates() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_getAllStates(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get the timestamp of the state of a given peer.
* @param {number | bigint | `${number}`} peer
* @returns {number | undefined}
*/
getTimestamp(peer) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-32);
wasm.awarenesswasm_getTimestamp(retptr, this.__wbg_ptr, addHeapObject(peer));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r2 = getDataViewMemory0().getFloat64(retptr + 8 * 1, true);
var r4 = getDataViewMemory0().getInt32(retptr + 4 * 4, true);
var r5 = getDataViewMemory0().getInt32(retptr + 4 * 5, true);
if (r5) {
throw takeObject(r4);
}
return r0 === 0 ? undefined : r2;
} finally {
wasm.__wbindgen_add_to_stack_pointer(32);
}
}
/**
* Sets the state of the local peer.
* @param {any} value
*/
setLocalState(value) {
wasm.awarenesswasm_setLocalState(this.__wbg_ptr, addHeapObject(value));
}
/**
* Remove the states of outdated peers.
* @returns {PeerID[]}
*/
removeOutdated() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_removeOutdated(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayJsValueFromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 4, 4);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Creates a new `Awareness` instance.
*
* The `timeout` parameter specifies the duration in milliseconds.
* A state of a peer is considered outdated, if the last update of the state of the peer
* is older than the `timeout`.
* @param {number | bigint | `${number}`} peer
* @param {number} timeout
*/
constructor(peer, timeout) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_new(retptr, addHeapObject(peer), timeout);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
this.__wbg_ptr = r0 >>> 0;
AwarenessWasmFinalization.register(this, this.__wbg_ptr, this);
return this;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get the PeerID of the local peer.
* @returns {PeerID}
*/
peer() {
const ret = wasm.awarenesswasm_peer(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Applies the encoded state of peers.
*
* Each peer's deletion countdown will be reset upon update, requiring them to pass through the `timeout`
* interval again before being eligible for deletion.
* @param {Uint8Array} encoded_peers_info
* @returns {{ updated: PeerID[], added: PeerID[] }}
*/
apply(encoded_peers_info) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(encoded_peers_info, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.awarenesswasm_apply(retptr, this.__wbg_ptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get all the peers
* @returns {PeerID[]}
*/
peers() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_peers(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayJsValueFromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 4, 4);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Encodes the state of the given peers.
* @param {Array<any>} peers
* @returns {Uint8Array}
*/
encode(peers) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_encode(retptr, this.__wbg_ptr, addHeapObject(peers));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
if (r3) {
throw takeObject(r2);
}
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get the number of peers.
* @returns {number}
*/
length() {
const ret = wasm.awarenesswasm_length(this.__wbg_ptr);
return ret;
}
/**
* If the state is empty.
* @returns {boolean}
*/
isEmpty() {
const ret = wasm.awarenesswasm_isEmpty(this.__wbg_ptr);
return ret !== 0;
}
/**
* Get the state of a given peer.
* @param {number | bigint | `${number}`} peer
* @returns {any}
*/
getState(peer) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_getState(retptr, this.__wbg_ptr, addHeapObject(peer));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Encodes the state of all peers.
* @returns {Uint8Array}
*/
encodeAll() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.awarenesswasm_encodeAll(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
}
module.exports.AwarenessWasm = AwarenessWasm;
const ChangeModifierFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_changemodifier_free(ptr >>> 0, 1));
class ChangeModifier {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(ChangeModifier.prototype);
obj.__wbg_ptr = ptr;
ChangeModifierFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
ChangeModifierFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_changemodifier_free(ptr, 0);
}
/**
* @param {string} message
* @returns {ChangeModifier}
*/
setMessage(message) {
const ptr0 = passStringToWasm0(message, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.changemodifier_setMessage(this.__wbg_ptr, ptr0, len0);
return ChangeModifier.__wrap(ret);
}
/**
* @param {number} timestamp
* @returns {ChangeModifier}
*/
setTimestamp(timestamp) {
const ret = wasm.changemodifier_setTimestamp(this.__wbg_ptr, timestamp);
return ChangeModifier.__wrap(ret);
}
}
module.exports.ChangeModifier = ChangeModifier;
const CursorFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_cursor_free(ptr >>> 0, 1));
/**
* Cursor is a stable position representation in the doc.
* When expressing the position of a cursor, using "index" can be unstable
* because the cursor's position may change due to other deletions and insertions,
* requiring updates with each edit. To stably represent a position or range within
* a list structure, we can utilize the ID of each item/character on List CRDT or
* Text CRDT for expression.
*
* Loro optimizes State metadata by not storing the IDs of deleted elements. This
* approach complicates tracking cursors since they rely on these IDs. The solution
* recalculates position by replaying relevant history to update cursors
* accurately. To minimize the performance impact of history replay, the system
* updates cursor info to reference only the IDs of currently present elements,
* thereby reducing the need for replay.
*
* @example
* ```ts
*
* const doc = new LoroDoc();
* const text = doc.getText("text");
* text.insert(0, "123");
* const pos0 = text.getCursor(0, 0);
* {
* const ans = doc.getCursorPos(pos0!);
* expect(ans.offset).toBe(0);
* }
* text.insert(0, "1");
* {
* const ans = doc.getCursorPos(pos0!);
* expect(ans.offset).toBe(1);
* }
* ```
*/
class Cursor {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(Cursor.prototype);
obj.__wbg_ptr = ptr;
CursorFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
CursorFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_cursor_free(ptr, 0);
}
/**
* Get the id of the given container.
* @returns {ContainerID}
*/
containerId() {
const ret = wasm.cursor_containerId(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Get the ID that represents the position.
*
* It can be undefined if it's not bind into a specific ID.
* @returns {{ peer: PeerID, counter: number } | undefined}
*/
pos() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.cursor_pos(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* "Cursor"
* @returns {any}
*/
kind() {
const ret = wasm.cursor_kind(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Get which side of the character/list item the cursor is on.
* @returns {Side}
*/
side() {
const ret = wasm.cursor_side(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Decode the cursor from a Uint8Array.
* @param {Uint8Array} data
* @returns {Cursor}
*/
static decode(data) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.cursor_decode(retptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return Cursor.__wrap(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Encode the cursor into a Uint8Array.
* @returns {Uint8Array}
*/
encode() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.cursor_encode(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
}
module.exports.Cursor = Cursor;
const EphemeralStoreWasmFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_ephemeralstorewasm_free(ptr >>> 0, 1));
class EphemeralStoreWasm {
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
EphemeralStoreWasmFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_ephemeralstorewasm_free(ptr, 0);
}
/**
* @returns {any}
*/
getAllStates() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.ephemeralstorewasm_getAllStates(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
removeOutdated() {
wasm.ephemeralstorewasm_removeOutdated(this.__wbg_ptr);
}
/**
* @param {Function} f
* @returns {any}
*/
subscribeLocalUpdates(f) {
const ret = wasm.ephemeralstorewasm_subscribeLocalUpdates(this.__wbg_ptr, addHeapObject(f));
return takeObject(ret);
}
/**
* @param {string} key
* @returns {any}
*/
get(key) {
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.ephemeralstorewasm_get(this.__wbg_ptr, ptr0, len0);
return takeObject(ret);
}
/**
* Creates a new `EphemeralStore` instance.
*
* The `timeout` parameter specifies the duration in milliseconds.
* A state of a peer is considered outdated, if the last update of the state of the peer
* is older than the `timeout`.
* @param {number} timeout
*/
constructor(timeout) {
const ret = wasm.ephemeralstorewasm_new(timeout);
this.__wbg_ptr = ret >>> 0;
EphemeralStoreWasmFinalization.register(this, this.__wbg_ptr, this);
return this;
}
/**
* @param {string} key
* @param {any} value
*/
set(key, value) {
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.ephemeralstorewasm_set(this.__wbg_ptr, ptr0, len0, addHeapObject(value));
}
/**
* @returns {string[]}
*/
keys() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.ephemeralstorewasm_keys(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayJsValueFromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 4, 4);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* @param {Uint8Array} data
*/
apply(data) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
const len0 = WASM_VECTOR_LEN;
wasm.ephemeralstorewasm_apply(retptr, this.__wbg_ptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
if (r1) {
throw takeObject(r0);
}
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* @param {string} key
*/
delete(key) {
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.ephemeralstorewasm_delete(this.__wbg_ptr, ptr0, len0);
}
/**
* @param {string} key
* @returns {Uint8Array}
*/
encode(key) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
wasm.ephemeralstorewasm_encode(retptr, this.__wbg_ptr, ptr0, len0);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v2 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v2;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* If the state is empty.
* @returns {boolean}
*/
isEmpty() {
const ret = wasm.ephemeralstorewasm_isEmpty(this.__wbg_ptr);
return ret !== 0;
}
/**
* @returns {Uint8Array}
*/
encodeAll() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.ephemeralstorewasm_encodeAll(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var v1 = getArrayU8FromWasm0(r0, r1).slice();
wasm.__wbindgen_free(r0, r1 * 1, 1);
return v1;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* @param {Function} f
* @returns {any}
*/
subscribe(f) {
const ret = wasm.ephemeralstorewasm_subscribe(this.__wbg_ptr, addHeapObject(f));
return takeObject(ret);
}
}
module.exports.EphemeralStoreWasm = EphemeralStoreWasm;
const LoroCounterFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_lorocounter_free(ptr >>> 0, 1));
/**
* The handler of a counter container.
*/
class LoroCounter {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(LoroCounter.prototype);
obj.__wbg_ptr = ptr;
LoroCounterFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
LoroCounterFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_lorocounter_free(ptr, 0);
}
/**
* Whether the container is attached to a docuemnt.
*
* If it's detached, the operations on the container will not be persisted.
* @returns {boolean}
*/
isAttached() {
const ret = wasm.lorocounter_isAttached(this.__wbg_ptr);
return ret !== 0;
}
/**
* Get the attached container associated with this.
*
* Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
* @returns {LoroTree | undefined}
*/
getAttached() {
const ret = wasm.lorocounter_getAttached(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Get the value of the counter.
* @returns {number}
*/
getShallowValue() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_getShallowValue(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getFloat64(retptr + 8 * 0, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
if (r3) {
throw takeObject(r2);
}
return r0;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* The container id of this handler.
* @returns {ContainerID}
*/
get id() {
const ret = wasm.lorocounter_id(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Create a new LoroCounter.
*/
constructor() {
const ret = wasm.lorocounter_new();
this.__wbg_ptr = ret >>> 0;
LoroCounterFinalization.register(this, this.__wbg_ptr, this);
return this;
}
/**
* "Counter"
* @returns {'Counter'}
*/
kind() {
const ret = wasm.lorocounter_kind(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Get the parent container of the counter container.
*
* - The parent container of the root counter is `undefined`.
* - The object returned is a new js object each time because it need to cross
* the WASM boundary.
* @returns {Container | undefined}
*/
parent() {
const ret = wasm.lorocounter_parent(this.__wbg_ptr);
return takeObject(ret);
}
/**
* @returns {number}
*/
toJSON() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_getShallowValue(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getFloat64(retptr + 8 * 0, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
if (r3) {
throw takeObject(r2);
}
return r0;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Decrement the counter by the given value.
* @param {number} value
*/
decrement(value) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_decrement(retptr, this.__wbg_ptr, value);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
if (r1) {
throw takeObject(r0);
}
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get the value of the counter.
* @returns {number}
*/
get value() {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_getShallowValue(retptr, this.__wbg_ptr);
var r0 = getDataViewMemory0().getFloat64(retptr + 8 * 0, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
if (r3) {
throw takeObject(r2);
}
return r0;
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Increment the counter by the given value.
* @param {number} value
*/
increment(value) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_increment(retptr, this.__wbg_ptr, value);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
if (r1) {
throw takeObject(r0);
}
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Subscribe to the changes of the counter.
* @param {Function} f
* @returns {any}
*/
subscribe(f) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorocounter_subscribe(retptr, this.__wbg_ptr, addHeapObject(f));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
}
module.exports.LoroCounter = LoroCounter;
const LoroDocFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_lorodoc_free(ptr >>> 0, 1));
/**
* The CRDTs document. Loro supports different CRDTs include [**List**](LoroList),
* [**RichText**](LoroText), [**Map**](LoroMap) and [**Movable Tree**](LoroTree),
* you could build all kind of applications by these.
*
* **Important:** Loro is a pure library and does not handle network protocols.
* It is the responsibility of the user to manage the storage, loading, and synchronization
* of the bytes exported by Loro in a manner suitable for their specific environment.
*
* @example
* ```ts
* import { LoroDoc } from "loro-crdt"
*
* const loro = new LoroDoc();
* const text = loro.getText("text");
* const list = loro.getList("list");
* const map = loro.getMap("Map");
* const tree = loro.getTree("tree");
* ```
*/
class LoroDoc {
static __wrap(ptr) {
ptr = ptr >>> 0;
const obj = Object.create(LoroDoc.prototype);
obj.__wbg_ptr = ptr;
LoroDocFinalization.register(obj, obj.__wbg_ptr, obj);
return obj;
}
__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
LoroDocFinalization.unregister(this);
return ptr;
}
free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_lorodoc_free(ptr, 0);
}
/**
* Apply a batch of diff to the document
*
* A diff batch represents a set of changes between two versions of the document.
* You can calculate a diff batch using `doc.diff()`.
*
* Changes are associated with container IDs. During diff application, if new containers were created in the source
* document, they will be assigned fresh IDs in the target document. Loro automatically handles remapping these
* container IDs from their original IDs to the new IDs as the diff is applied.
*
* @example
* ```ts
* const doc1 = new LoroDoc();
* const doc2 = new LoroDoc();
*
* // Make some changes to doc1
* const text = doc1.getText("text");
* text.insert(0, "Hello");
*
* // Calculate diff between empty and current state
* const diff = doc1.diff([], doc1.frontiers());
*
* // Apply changes to doc2
* doc2.applyDiff(diff);
* console.log(doc2.getText("text").toString()); // "Hello"
* ```
* @param {[ContainerID, Diff|JsonDiff][]} diff
*/
applyDiff(diff) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorodoc_applyDiff(retptr, this.__wbg_ptr, addHeapObject(diff));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
if (r1) {
throw takeObject(r0);
}
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Check if the doc contains the full history.
* @returns {boolean}
*/
isShallow() {
const ret = wasm.lorodoc_isShallow(this.__wbg_ptr);
return ret !== 0;
}
/**
* Get the number of changes in the oplog.
* @returns {number}
*/
changeCount() {
const ret = wasm.lorodoc_changeCount(this.__wbg_ptr);
return ret >>> 0;
}
/**
* Get the value or container at the given path
*
* The path can be specified in different ways depending on the container type:
*
* For Tree:
* 1. Using node IDs: `tree/{node_id}/property`
* 2. Using indices: `tree/0/1/property`
*
* For List and MovableList:
* - Using indices: `list/0` or `list/1/property`
*
* For Map:
* - Using keys: `map/key` or `map/nested/property`
*
* For tree structures, index-based paths follow depth-first traversal order.
* The indices start from 0 and represent the position of a node among its siblings.
*
* @example
* ```ts
* import { LoroDoc } from "loro-crdt";
*
* const doc = new LoroDoc();
* const map = doc.getMap("map");
* map.set("key", 1);
* console.log(doc.getByPath("map/key")); // 1
* console.log(doc.getByPath("map")); // LoroMap
* ```
* @param {string} path
* @returns {Value | Container | undefined}
*/
getByPath(path) {
const ptr0 = passStringToWasm0(path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len0 = WASM_VECTOR_LEN;
const ret = wasm.lorodoc_getByPath(this.__wbg_ptr, ptr0, len0);
return takeObject(ret);
}
/**
* Get a LoroCounter by container id
*
* If the container does not exist, an error will be thrown.
* @param {ContainerID | string} cid
* @returns {LoroCounter}
*/
getCounter(cid) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorodoc_getCounter(retptr, this.__wbg_ptr, addBorrowedObject(cid));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return LoroCounter.__wrap(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
heap[stack_pointer++] = undefined;
}
}
/**
* `detached` indicates that the `DocState` is not synchronized with the latest version of `OpLog`.
*
* > The document becomes detached during a `checkout` operation.
* > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
* > In a detached state, the document is not editable by default, and any `import` operations will be
* > recorded in the `OpLog` without being applied to the `DocState`.
*
* @example
* ```ts
* import { LoroDoc } from "loro-crdt";
*
* const doc = new LoroDoc();
* const text = doc.getText("text");
* const frontiers = doc.frontiers();
* text.insert(0, "Hello World!");
* console.log(doc.isDetached()); // false
* doc.checkout(frontiers);
* console.log(doc.isDetached()); // true
* doc.attach();
* console.log(doc.isDetached()); // false
* ```
* @returns {boolean}
*/
isDetached() {
const ret = wasm.lorodoc_isDetached(this.__wbg_ptr);
return ret !== 0;
}
/**
* Get peer id in decimal string.
* @returns {PeerID}
*/
get peerIdStr() {
const ret = wasm.lorodoc_peerIdStr(this.__wbg_ptr);
return takeObject(ret);
}
/**
* Set the peer ID of the current writer.
*
* It must be a number, a BigInt, or a decimal string that can be parsed to a unsigned 64-bit integer.
*
* Note: use it with caution. You need to make sure there is not chance that two peers
* have the same peer ID. Otherwise, we cannot ensure the consistency of the document.
* @param {number | bigint | `${number}`} peer_id
*/
setPeerId(peer_id) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.lorodoc_setPeerId(retptr, this.__wbg_ptr, addHeapObject(peer_id));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
if (r1) {
throw takeObject(r0);
}
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Get the absolute position of the given Cursor
*
* @example
* ```ts
* const doc = new LoroDoc();
* const text = doc.getText("text");
* text.insert(0, "123");
* const pos0 = text.getCursor(0, 0);
* {
* const ans = doc.getCursorPos(pos0!);
* expect(ans.offset).toBe(0);
* }
* text.insert(0, "1");
* {
* const ans = doc.getCursorPos(pos0!);
* expect(ans.offset).toBe(1);
* }
* ```
* @param {Cursor} cursor
* @returns {{ update?: Cursor, offset: number, side: Side } | undefined}
*/
getCursorPos(cursor) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
_assertClass(cursor, Cursor);
wasm.lorodoc_getCursorPos(retptr, this.__wbg_ptr, cursor.__wbg_ptr);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
if (r2) {
throw takeObject(r1);
}
return takeObject(r0);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}
/**
* Check if the doc contains the target container.
*
* A root container always exists, while a normal container exists
* if it has ever been created on the doc.
*
* @example
* ```ts
* import { LoroDoc, LoroMap, LoroText, LoroList } from "loro-crdt";
*
* const doc = new LoroDoc();
* doc.setPeerId("1");
* const text = doc.getMap("map").setContainer("text", new LoroText());
* const list = doc.getMap("map").setContainer("li