@eweser/db
Version:
eweser-db core database
1,473 lines (1,472 loc) • 110 kB
JavaScript
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("yjs")) : typeof define === "function" && define.amd ? define(["exports", "yjs"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["eweser-db"] = {}, global.Y));
})(this, function(exports2, Y) {
"use strict";var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
if (e) {
for (const k in e) {
if (k !== "default") {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const Y__namespace = /* @__PURE__ */ _interopNamespaceDefault(Y);
var events = { exports: {} };
var hasRequiredEvents;
function requireEvents() {
if (hasRequiredEvents) return events.exports;
hasRequiredEvents = 1;
var R = typeof Reflect === "object" ? Reflect : null;
var ReflectApply = R && typeof R.apply === "function" ? R.apply : function ReflectApply2(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
};
var ReflectOwnKeys;
if (R && typeof R.ownKeys === "function") {
ReflectOwnKeys = R.ownKeys;
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys2(target) {
return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys2(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN2(value) {
return value !== value;
};
function EventEmitter() {
EventEmitter.init.call(this);
}
events.exports = EventEmitter;
events.exports.once = once;
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = void 0;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = void 0;
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== "function") {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, "defaultMaxListeners", {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== "number" || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + ".");
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || void 0;
};
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== "number" || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === void 0)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = type === "error";
var events2 = this._events;
if (events2 !== void 0)
doError = doError && events2.error === void 0;
else if (!doError)
return false;
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
throw er;
}
var err = new Error("Unhandled error." + (er ? " (" + er.message + ")" : ""));
err.context = er;
throw err;
}
var handler = events2[type];
if (handler === void 0)
return false;
if (typeof handler === "function") {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events2;
var existing;
checkListener(listener);
events2 = target._events;
if (events2 === void 0) {
events2 = target._events = /* @__PURE__ */ Object.create(null);
target._eventsCount = 0;
} else {
if (events2.newListener !== void 0) {
target.emit(
"newListener",
type,
listener.listener ? listener.listener : listener
);
events2 = target._events;
}
existing = events2[type];
}
if (existing === void 0) {
existing = events2[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === "function") {
existing = events2[type] = prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
var w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
w.name = "MaxListenersExceededWarning";
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener = function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: void 0, target, type, listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once2(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.removeListener = function removeListener(type, listener) {
var list, events2, position, i, originalListener;
checkListener(listener);
events2 = this._events;
if (events2 === void 0)
return this;
list = events2[type];
if (list === void 0)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = /* @__PURE__ */ Object.create(null);
else {
delete events2[type];
if (events2.removeListener)
this.emit("removeListener", type, list.listener || listener);
}
} else if (typeof list !== "function") {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events2[type] = list[0];
if (events2.removeListener !== void 0)
this.emit("removeListener", type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
var listeners, events2, i;
events2 = this._events;
if (events2 === void 0)
return this;
if (events2.removeListener === void 0) {
if (arguments.length === 0) {
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
} else if (events2[type] !== void 0) {
if (--this._eventsCount === 0)
this._events = /* @__PURE__ */ Object.create(null);
else
delete events2[type];
}
return this;
}
if (arguments.length === 0) {
var keys2 = Object.keys(events2);
var key;
for (i = 0; i < keys2.length; ++i) {
key = keys2[i];
if (key === "removeListener") continue;
this.removeAllListeners(key);
}
this.removeAllListeners("removeListener");
this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events2[type];
if (typeof listeners === "function") {
this.removeListener(type, listeners);
} else if (listeners !== void 0) {
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events2 = target._events;
if (events2 === void 0)
return [];
var evlistener = events2[type];
if (evlistener === void 0)
return [];
if (typeof evlistener === "function")
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === "function") {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events2 = this._events;
if (events2 !== void 0) {
var evlistener = events2[type];
if (typeof evlistener === "function") {
return 1;
} else if (evlistener !== void 0) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function once(emitter, name) {
return new Promise(function(resolve, reject) {
function errorListener(err) {
emitter.removeListener(name, resolver);
reject(err);
}
function resolver() {
if (typeof emitter.removeListener === "function") {
emitter.removeListener("error", errorListener);
}
resolve([].slice.call(arguments));
}
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== "error") {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
}
});
}
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
if (typeof emitter.on === "function") {
eventTargetAgnosticAddListener(emitter, "error", handler, flags);
}
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === "function") {
if (flags.once) {
emitter.once(name, listener);
} else {
emitter.on(name, listener);
}
} else if (typeof emitter.addEventListener === "function") {
emitter.addEventListener(name, function wrapListener(arg) {
if (flags.once) {
emitter.removeEventListener(name, wrapListener);
}
listener(arg);
});
} else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
}
}
return events.exports;
}
var eventsExports = requireEvents();
class TypedEventEmitter extends eventsExports.EventEmitter {
on(event, listener) {
return super.on(event, listener);
}
emit(event, ...args) {
return super.emit(event, ...args);
}
}
const setupLogger = (db) => {
db.on("log", (level, ...message) => {
switch (level) {
case 0:
return console.info(...message);
case 1:
return console.log(...message);
case 2:
return console.warn(...message);
case 3:
return console.error(...message);
}
});
};
const newDocument = (_id, _ref, doc) => {
const now = (/* @__PURE__ */ new Date()).getTime();
const base = {
_created: now,
_id,
_ref,
_updated: now,
_deleted: false,
_ttl: void 0
};
return { ...base, ...doc };
};
const buildRef = (params) => {
Object.entries(params).forEach(([key, param]) => {
if (!param) throw new Error(`${key} is required`);
if (typeof param !== "string") throw new Error(`${key} must be a string`);
if (param.includes("|")) throw new Error(`${key} cannot include |`);
});
const { collectionKey, roomId, documentId, authServer } = params;
return `${authServer}|${collectionKey}|${roomId}|${documentId}`;
};
const wait$1 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const randomString = (length2) => Math.random().toString(36).substring(2, length2 + 2);
function getRoomDocuments(room) {
if (!room.ydoc) throw new Error("room.ydoc not found");
const registryMap = room.ydoc.getMap("documents");
return registryMap;
}
const getRoom = (_db) => ({
collectionKey,
roomId
}) => {
const room = _db.collections[collectionKey][roomId];
if (!room) return null;
return room;
};
const getDocuments = (_db) => (room) => {
var _a;
if (!room) throw new Error("no room");
const documents = (_a = room.ydoc) == null ? void 0 : _a.getMap("documents");
if (!documents) throw new Error("no documents");
return {
documents,
get: (id) => {
return documents.get(id);
},
set: (doc) => {
doc._updated = Date.now();
return documents.set(doc._id, doc);
},
new: (doc, id) => {
if (id && documents.get(id)) {
throw new Error("document already exists");
}
let documentId = id || randomString(24);
if (documents.get(documentId)) {
documentId = randomString(24);
if (documents.get(documentId)) {
throw new Error("document already exists");
}
}
const ref = buildRef({
authServer: _db.authServer,
collectionKey: room.collectionKey,
roomId: room.id,
documentId
});
const newDoc = newDocument(documentId, ref, doc);
documents.set(documentId, newDoc);
return newDoc;
},
delete: (id, timeToLiveMs) => {
const doc = documents.get(id);
if (!doc) throw new Error("document does not exist");
const oneMonth = 1e3 * 60 * 60 * 24 * 30;
doc._deleted = true;
doc._ttl = timeToLiveMs ?? (/* @__PURE__ */ new Date()).getTime() + oneMonth;
return documents.set(id, doc);
},
getAll: () => {
return documents.toJSON();
},
getAllToArray: () => {
return Object.values(documents.toJSON());
},
getUndeleted: () => {
const undeleted = {};
documents.forEach((doc) => {
if (doc && !doc._deleted) {
undeleted[doc._id] = doc;
}
});
return undeleted;
},
getUndeletedToArray: () => {
const undeleted = [];
documents.forEach((doc) => {
if (doc && !doc._deleted) {
undeleted.push(doc);
}
});
return undeleted;
},
toArray: (docs) => {
return Object.values(docs);
},
onChange: (callback) => {
documents.observe(callback);
},
sortByRecent: (docs) => {
const sortedArray = Object.entries(docs).sort(
(a, b) => b[1]._updated - a[1]._updated
);
return Object.fromEntries(sortedArray);
}
};
};
const floor$1 = Math.floor;
const min$1 = (a, b) => a < b ? a : b;
const max$1 = (a, b) => a > b ? a : b;
const getUnixTime = Date.now;
const create$4 = (f) => (
/** @type {Promise<T>} */
new Promise(f)
);
Promise.all.bind(Promise);
const create$3 = (s) => new Error(s);
const rtop = (request) => create$4((resolve, reject) => {
request.onerror = (event) => reject(new Error(event.target.error));
request.onsuccess = (event) => resolve(event.target.result);
});
const openDB = (name, initDB) => create$4((resolve, reject) => {
const request = indexedDB.open(name);
request.onupgradeneeded = (event) => initDB(event.target.result);
request.onerror = (event) => reject(create$3(event.target.error));
request.onsuccess = (event) => {
const db = event.target.result;
db.onversionchange = () => {
db.close();
};
resolve(db);
};
});
const deleteDB = (name) => rtop(indexedDB.deleteDatabase(name));
const createStores = (db, definitions) => definitions.forEach(
(d) => (
// @ts-ignore
db.createObjectStore.apply(db, d)
)
);
const transact = (db, stores, access = "readwrite") => {
const transaction = db.transaction(stores, access);
return stores.map((store) => getStore(transaction, store));
};
const count = (store, range) => rtop(store.count(range));
const get = (store, key) => rtop(store.get(key));
const del = (store, key) => rtop(store.delete(key));
const put = (store, item, key) => rtop(store.put(item, key));
const addAutoKey = (store, item) => rtop(store.add(item));
const getAll = (store, range, limit) => rtop(store.getAll(range, limit));
const queryFirst = (store, query, direction) => {
let first = null;
return iterateKeys(store, query, (key) => {
first = key;
return false;
}, direction).then(() => first);
};
const getLastKey = (store, range = null) => queryFirst(store, range, "prev");
const iterateOnRequest = (request, f) => create$4((resolve, reject) => {
request.onerror = reject;
request.onsuccess = async (event) => {
const cursor = event.target.result;
if (cursor === null || await f(cursor) === false) {
return resolve();
}
cursor.continue();
};
});
const iterateKeys = (store, keyrange, f, direction = "next") => iterateOnRequest(store.openKeyCursor(keyrange, direction), (cursor) => f(cursor.key));
const getStore = (t, store) => t.objectStore(store);
const createIDBKeyRangeUpperBound = (upper, upperOpen) => IDBKeyRange.upperBound(upper, upperOpen);
const createIDBKeyRangeLowerBound = (lower, lowerOpen) => IDBKeyRange.lowerBound(lower, lowerOpen);
const create$2 = () => /* @__PURE__ */ new Map();
const setIfUndefined = (map, key, createT) => {
let set = map.get(key);
if (set === void 0) {
map.set(key, set = createT());
}
return set;
};
const create$1 = () => /* @__PURE__ */ new Set();
const from = Array.from;
class Observable {
constructor() {
this._observers = create$2();
}
/**
* @param {N} name
* @param {function} f
*/
on(name, f) {
setIfUndefined(this._observers, name, create$1).add(f);
}
/**
* @param {N} name
* @param {function} f
*/
once(name, f) {
const _f = (...args) => {
this.off(name, _f);
f(...args);
};
this.on(name, _f);
}
/**
* @param {N} name
* @param {function} f
*/
off(name, f) {
const observers = this._observers.get(name);
if (observers !== void 0) {
observers.delete(f);
if (observers.size === 0) {
this._observers.delete(name);
}
}
}
/**
* Emit a named event. All registered event listeners that listen to the
* specified name will receive the event.
*
* @todo This should catch exceptions
*
* @param {N} name The event name.
* @param {Array<any>} args The arguments that are applied to the event listener.
*/
emit(name, args) {
return from((this._observers.get(name) || create$2()).values()).forEach((f) => f(...args));
}
destroy() {
this._observers = create$2();
}
}
const customStoreName = "custom";
const updatesStoreName = "updates";
const PREFERRED_TRIM_SIZE = 500;
const fetchUpdates = (idbPersistence, beforeApplyUpdatesCallback = () => {
}, afterApplyUpdatesCallback = () => {
}) => {
const [updatesStore] = transact(
/** @type {IDBDatabase} */
idbPersistence.db,
[updatesStoreName]
);
return getAll(updatesStore, createIDBKeyRangeLowerBound(idbPersistence._dbref, false)).then((updates) => {
if (!idbPersistence._destroyed) {
beforeApplyUpdatesCallback(updatesStore);
Y__namespace.transact(idbPersistence.doc, () => {
updates.forEach((val) => Y__namespace.applyUpdate(idbPersistence.doc, val));
}, idbPersistence, false);
afterApplyUpdatesCallback(updatesStore);
}
}).then(() => getLastKey(updatesStore).then((lastKey) => {
idbPersistence._dbref = lastKey + 1;
})).then(() => count(updatesStore).then((cnt) => {
idbPersistence._dbsize = cnt;
})).then(() => updatesStore);
};
const storeState = (idbPersistence, forceStore = true) => fetchUpdates(idbPersistence).then((updatesStore) => {
if (forceStore || idbPersistence._dbsize >= PREFERRED_TRIM_SIZE) {
addAutoKey(updatesStore, Y__namespace.encodeStateAsUpdate(idbPersistence.doc)).then(() => del(updatesStore, createIDBKeyRangeUpperBound(idbPersistence._dbref, true))).then(() => count(updatesStore).then((cnt) => {
idbPersistence._dbsize = cnt;
}));
}
});
class IndexeddbPersistence extends Observable {
/**
* @param {string} name
* @param {Y.Doc} doc
*/
constructor(name, doc) {
super();
this.doc = doc;
this.name = name;
this._dbref = 0;
this._dbsize = 0;
this._destroyed = false;
this.db = null;
this.synced = false;
this._db = openDB(
name,
(db) => createStores(db, [
["updates", { autoIncrement: true }],
["custom"]
])
);
this.whenSynced = create$4((resolve) => this.on("synced", () => resolve(this)));
this._db.then((db) => {
this.db = db;
const beforeApplyUpdatesCallback = (updatesStore) => addAutoKey(updatesStore, Y__namespace.encodeStateAsUpdate(doc));
const afterApplyUpdatesCallback = () => {
if (this._destroyed) return this;
this.synced = true;
this.emit("synced", [this]);
};
fetchUpdates(this, beforeApplyUpdatesCallback, afterApplyUpdatesCallback);
});
this._storeTimeout = 1e3;
this._storeTimeoutId = null;
this._storeUpdate = (update, origin) => {
if (this.db && origin !== this) {
const [updatesStore] = transact(
/** @type {IDBDatabase} */
this.db,
[updatesStoreName]
);
addAutoKey(updatesStore, update);
if (++this._dbsize >= PREFERRED_TRIM_SIZE) {
if (this._storeTimeoutId !== null) {
clearTimeout(this._storeTimeoutId);
}
this._storeTimeoutId = setTimeout(() => {
storeState(this, false);
this._storeTimeoutId = null;
}, this._storeTimeout);
}
}
};
doc.on("update", this._storeUpdate);
this.destroy = this.destroy.bind(this);
doc.on("destroy", this.destroy);
}
destroy() {
if (this._storeTimeoutId) {
clearTimeout(this._storeTimeoutId);
}
this.doc.off("update", this._storeUpdate);
this.doc.off("destroy", this.destroy);
this._destroyed = true;
return this._db.then((db) => {
db.close();
});
}
/**
* Destroys this instance and removes all data from indexeddb.
*
* @return {Promise<void>}
*/
clearData() {
return this.destroy().then(() => {
deleteDB(this.name);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @return {Promise<String | number | ArrayBuffer | Date | any>}
*/
get(key) {
return this._db.then((db) => {
const [custom] = transact(db, [customStoreName], "readonly");
return get(custom, key);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @param {String | number | ArrayBuffer | Date} value
* @return {Promise<String | number | ArrayBuffer | Date>}
*/
set(key, value) {
return this._db.then((db) => {
const [custom] = transact(db, [customStoreName]);
return put(custom, value, key);
});
}
/**
* @param {String | number | ArrayBuffer | Date} key
* @return {Promise<undefined>}
*/
del(key) {
return this._db.then((db) => {
const [custom] = transact(db, [customStoreName]);
return del(custom, key);
});
}
}
const Doc = Y.Doc;
const initializeDocAndLocalProvider = async (roomId, existingDoc, provider) => {
const yDoc = existingDoc || new Doc();
if (!yDoc) throw new Error("could not create doc");
const localProvider = provider ? provider(roomId, yDoc) : new IndexeddbPersistence(roomId, yDoc);
if (localProvider.synced) return { yDoc, localProvider };
const synced = await localProvider.whenSynced;
if (synced.synced) return { yDoc, localProvider };
else throw new Error("could not sync doc");
};
function stringToBase64(input) {
if (typeof window !== "undefined" && window.btoa) {
return window.btoa(input);
} else if (typeof Buffer !== "undefined") {
return Buffer.from(input).toString("base64");
} else {
throw new Error("Unable to encode to Base64");
}
}
function encodeClientToken(token) {
const jsonString = JSON.stringify(token);
let base64 = stringToBase64(jsonString);
base64 = base64.replace("+", "-").replace("/", "_").replace(/=+$/, "");
return base64;
}
const BIT8$1 = 128;
const BITS7$1 = 127;
const MAX_SAFE_INTEGER$1 = Number.MAX_SAFE_INTEGER;
const _encodeUtf8Polyfill$1 = (str) => {
const encodedString = unescape(encodeURIComponent(str));
const len = encodedString.length;
const buf = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buf[i] = /** @type {number} */
encodedString.codePointAt(i);
}
return buf;
};
const utf8TextEncoder$1 = (
/** @type {TextEncoder} */
typeof TextEncoder !== "undefined" ? new TextEncoder() : null
);
const _encodeUtf8Native$1 = (str) => utf8TextEncoder$1.encode(str);
const encodeUtf8$1 = utf8TextEncoder$1 ? _encodeUtf8Native$1 : _encodeUtf8Polyfill$1;
let utf8TextDecoder$1 = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
if (utf8TextDecoder$1 && utf8TextDecoder$1.decode(new Uint8Array()).length === 1) {
utf8TextDecoder$1 = null;
}
let Encoder$1 = class Encoder {
constructor() {
this.cpos = 0;
this.cbuf = new Uint8Array(100);
this.bufs = [];
}
};
const createEncoder$1 = () => new Encoder$1();
const length$2 = (encoder) => {
let len = encoder.cpos;
for (let i = 0; i < encoder.bufs.length; i++) {
len += encoder.bufs[i].length;
}
return len;
};
const toUint8Array$1 = (encoder) => {
const uint8arr = new Uint8Array(length$2(encoder));
let curPos = 0;
for (let i = 0; i < encoder.bufs.length; i++) {
const d = encoder.bufs[i];
uint8arr.set(d, curPos);
curPos += d.length;
}
uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
return uint8arr;
};
const write$1 = (encoder, num) => {
const bufferLen = encoder.cbuf.length;
if (encoder.cpos === bufferLen) {
encoder.bufs.push(encoder.cbuf);
encoder.cbuf = new Uint8Array(bufferLen * 2);
encoder.cpos = 0;
}
encoder.cbuf[encoder.cpos++] = num;
};
const writeVarUint$1 = (encoder, num) => {
while (num > BITS7$1) {
write$1(encoder, BIT8$1 | BITS7$1 & num);
num = floor$1(num / 128);
}
write$1(encoder, BITS7$1 & num);
};
const _strBuffer$1 = new Uint8Array(3e4);
const _maxStrBSize$1 = _strBuffer$1.length / 3;
const _writeVarStringNative$1 = (encoder, str) => {
if (str.length < _maxStrBSize$1) {
const written = utf8TextEncoder$1.encodeInto(str, _strBuffer$1).written || 0;
writeVarUint$1(encoder, written);
for (let i = 0; i < written; i++) {
write$1(encoder, _strBuffer$1[i]);
}
} else {
writeVarUint8Array$1(encoder, encodeUtf8$1(str));
}
};
const _writeVarStringPolyfill$1 = (encoder, str) => {
const encodedString = unescape(encodeURIComponent(str));
const len = encodedString.length;
writeVarUint$1(encoder, len);
for (let i = 0; i < len; i++) {
write$1(
encoder,
/** @type {number} */
encodedString.codePointAt(i)
);
}
};
const writeVarString = utf8TextEncoder$1 && /** @type {any} */
utf8TextEncoder$1.encodeInto ? _writeVarStringNative$1 : _writeVarStringPolyfill$1;
const writeUint8Array$1 = (encoder, uint8Array) => {
const bufferLen = encoder.cbuf.length;
const cpos = encoder.cpos;
const leftCopyLen = min$1(bufferLen - cpos, uint8Array.length);
const rightCopyLen = uint8Array.length - leftCopyLen;
encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
encoder.cpos += leftCopyLen;
if (rightCopyLen > 0) {
encoder.bufs.push(encoder.cbuf);
encoder.cbuf = new Uint8Array(max$1(bufferLen * 2, rightCopyLen));
encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
encoder.cpos = rightCopyLen;
}
};
const writeVarUint8Array$1 = (encoder, uint8Array) => {
writeVarUint$1(encoder, uint8Array.byteLength);
writeUint8Array$1(encoder, uint8Array);
};
const errorUnexpectedEndOfArray$1 = create$3("Unexpected end of array");
const errorIntegerOutOfRange$1 = create$3("Integer out of Range");
let Decoder$1 = class Decoder {
/**
* @param {Uint8Array} uint8Array Binary data to decode
*/
constructor(uint8Array) {
this.arr = uint8Array;
this.pos = 0;
}
};
const createDecoder$1 = (uint8Array) => new Decoder$1(uint8Array);
const readUint8Array$1 = (decoder, len) => {
const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
decoder.pos += len;
return view;
};
const readVarUint8Array$1 = (decoder) => readUint8Array$1(decoder, readVarUint$1(decoder));
const readUint8 = (decoder) => decoder.arr[decoder.pos++];
const readVarUint$1 = (decoder) => {
let num = 0;
let mult = 1;
const len = decoder.arr.length;
while (decoder.pos < len) {
const r = decoder.arr[decoder.pos++];
num = num + (r & BITS7$1) * mult;
mult *= 128;
if (r < BIT8$1) {
return num;
}
if (num > MAX_SAFE_INTEGER$1) {
throw errorIntegerOutOfRange$1;
}
}
throw errorUnexpectedEndOfArray$1;
};
const _readVarStringPolyfill = (decoder) => {
let remainingLen = readVarUint$1(decoder);
if (remainingLen === 0) {
return "";
} else {
let encodedString = String.fromCodePoint(readUint8(decoder));
if (--remainingLen < 100) {
while (remainingLen--) {
encodedString += String.fromCodePoint(readUint8(decoder));
}
} else {
while (remainingLen > 0) {
const nextLen = remainingLen < 1e4 ? remainingLen : 1e4;
const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen);
decoder.pos += nextLen;
encodedString += String.fromCodePoint.apply(
null,
/** @type {any} */
bytes
);
remainingLen -= nextLen;
}
}
return decodeURIComponent(escape(encodedString));
}
};
const _readVarStringNative = (decoder) => (
/** @type any */
utf8TextDecoder$1.decode(readVarUint8Array$1(decoder))
);
const readVarString = utf8TextDecoder$1 ? _readVarStringNative : _readVarStringPolyfill;
const keys = Object.keys;
const length$1 = (obj) => keys(obj).length;
const hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
const equalityStrict = (a, b) => a === b;
const equalityDeep = (a, b) => {
if (a == null || b == null) {
return equalityStrict(a, b);
}
if (a.constructor !== b.constructor) {
return false;
}
if (a === b) {
return true;
}
switch (a.constructor) {
case ArrayBuffer:
a = new Uint8Array(a);
b = new Uint8Array(b);
// eslint-disable-next-line no-fallthrough
case Uint8Array: {
if (a.byteLength !== b.byteLength) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
break;
}
case Set: {
if (a.size !== b.size) {
return false;
}
for (const value of a) {
if (!b.has(value)) {
return false;
}
}
break;
}
case Map: {
if (a.size !== b.size) {
return false;
}
for (const key of a.keys()) {
if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
return false;
}
}
break;
}
case Object:
if (length$1(a) !== length$1(b)) {
return false;
}
for (const key in a) {
if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
return false;
}
}
break;
case Array:
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!equalityDeep(a[i], b[i])) {
return false;
}
}
break;
default:
return false;
}
return true;
};
const outdatedTimeout = 3e4;
class Awareness extends Observable {
/**
* @param {Y.Doc} doc
*/
constructor(doc) {
super();
this.doc = doc;
this.clientID = doc.clientID;
this.states = /* @__PURE__ */ new Map();
this.meta = /* @__PURE__ */ new Map();
this._checkInterval = /** @type {any} */
setInterval(() => {
const now = getUnixTime();
if (this.getLocalState() !== null && outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */
this.meta.get(this.clientID).lastUpdated) {
this.setLocalState(this.getLocalState());
}
const remove = [];
this.meta.forEach((meta, clientid) => {
if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
remove.push(clientid);
}
});
if (remove.length > 0) {
removeAwarenessStates(this, remove, "timeout");
}
}, floor$1(outdatedTimeout / 10));
doc.on("destroy", () => {
this.destroy();
});
this.setLocalState({});
}
destroy() {
this.emit("destroy", [this]);
this.setLocalState(null);
super.destroy();
clearInterval(this._checkInterval);
}
/**
* @return {Object<string,any>|null}
*/
getLocalState() {
return this.states.get(this.clientID) || null;
}
/**
* @param {Object<string,any>|null} state
*/
setLocalState(state) {
const clientID = this.clientID;
const currLocalMeta = this.meta.get(clientID);
const clock = currLocalMeta === void 0 ? 0 : currLocalMeta.clock + 1;
const prevState = this.states.get(clientID);
if (state === null) {
this.states.delete(clientID);
} else {
this.states.set(clientID, state);
}
this.meta.set(clientID, {
clock,
lastUpdated: getUnixTime()
});
const added = [];
const updated = [];
const filteredUpdated = [];
const removed = [];
if (state === null) {
removed.push(clientID);
} else if (prevState == null) {
if (state != null) {
added.push(clientID);
}
} else {
updated.push(clientID);
if (!equalityDeep(prevState, state)) {
filteredUpdated.push(clientID);
}
}
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
this.emit("change", [{ added, updated: filteredUpdated, removed }, "local"]);
}
this.emit("update", [{ added, updated, removed }, "local"]);
}
/**
* @param {string} field
* @param {any} value
*/
setLocalStateField(field, value) {
const state = this.getLocalState();
if (state !== null) {
this.setLocalState({
...state,
[field]: value
});
}
}
/**
* @return {Map<number,Object<string,any>>}
*/
getStates() {
return this.states;
}
}
const removeAwarenessStates = (awareness, clients, origin) => {
const removed = [];
for (let i = 0; i < clients.length; i++) {
const clientID = clients[i];
if (awareness.states.has(clientID)) {
awareness.states.delete(clientID);
if (clientID === awareness.clientID) {
const curMeta = (
/** @type {MetaClientState} */
awareness.meta.get(clientID)
);
awareness.meta.set(clientID, {
clock: curMeta.clock + 1,
lastUpdated: getUnixTime()
});
}
removed.push(clientID);
}
}
if (removed.length > 0) {
awareness.emit("change", [{ added: [], updated: [], removed }, origin]);
awareness.emit("update", [{ added: [], updated: [], removed }, origin]);
}
};
const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
const len = clients.length;
const encoder = createEncoder$1();
writeVarUint$1(encoder, len);
for (let i = 0; i < len; i++) {
const clientID = clients[i];
const state = states.get(clientID) || null;
const clock = (
/** @type {MetaClientState} */
awareness.meta.get(clientID).clock
);
writeVarUint$1(encoder, clientID);
writeVarUint$1(encoder, clock);
writeVarString(encoder, JSON.stringify(state));
}
return toUint8Array$1(encoder);
};
const applyAwarenessUpdate = (awareness, update, origin) => {
const decoder = createDecoder$1(update);
const timestamp = getUnixTime();
const added = [];
const updated = [];
const filteredUpdated = [];
const removed = [];
const len = readVarUint$1(decoder);
for (let i = 0; i < len; i++) {
const clientID = readVarUint$1(decoder);
let clock = readVarUint$1(decoder);
const state = JSON.parse(readVarString(decoder));
const clientMeta = awareness.meta.get(clientID);
const prevState = awareness.states.get(clientID);
const currClock = clientMeta === void 0 ? 0 : clientMeta.clock;
if (currClock < clock || currClock === clock && state === null && awareness.states.has(clientID)) {
if (state === null) {
if (clientID === awareness.clientID && awareness.getLocalState() != null) {
clock++;
} else {
awareness.states.delete(clientID);
}
} else {
awareness.states.set(clientID, state);
}
awareness.meta.set(clientID, {
clock,
lastUpdated: timestamp
});
if (clientMeta === void 0 && state !== null) {
added.push(clientID);
} else if (clientMeta !== void 0 && state === null) {
removed.push(clientID);
} else if (state !== null) {
if (!equalityDeep(state, prevState)) {
filteredUpdated.push(clientID);
}
updated.push(clientID);
}
}
}
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
awareness.emit("change", [{
added,
updated: filteredUpdated,
removed
}, origin]);
}
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
awareness.emit("update", [{
added,
updated,
removed
}, origin]);
}
};
const messageYjsSyncStep1 = 0;
const messageYjsSyncStep2 = 1;
const messageYjsUpdate = 2;
const writeSyncStep1 = (encoder, doc) => {
writeVarUint$1(encoder, messageYjsSyncStep1);
const sv = Y__namespace.encodeStateVector(doc);
writeVarUint8Array$1(encoder, sv);
};
const writeSyncStep2 = (encoder, doc, encodedStateVector) => {
writeVarUint$1(encoder, messageYjsSyncStep2);
writeVarUint8Array$1(encoder, Y__namespace.encodeStateAsUpdate(doc, encodedStateVector));
};
const readSyncStep1 = (decoder, encoder, doc) => writeSyncStep2(encoder, doc, readVarUint8Array$1(decoder));
const readSyncStep2 = (decoder, doc, transactionOrigin) => {
try {
Y__namespace.applyUpdate(doc, readVarUint8Array$1(decoder), transactionOrigin);
} catch (error) {
console.error("Caught error while handling a Yjs update", error);
}
};
const writeUpdate = (encoder, update) => {
writeVarUint$1(encoder, messageYjsUpdate);
writeVarUint8Array$1(encoder, update);
};
const readUpdate = readSyncStep2;
const readSyncMessage = (decoder, encoder, doc, transactionOrigin) => {
const messageType = readVarUint$1(decoder);
switch (messageType) {
case messageYjsSyncStep1:
readSyncStep1(decoder, encoder, doc);
break;
case messageYjsSyncStep2:
readSyncStep2(decoder, doc, transactionOrigin);
break;
case messageYjsUpdate:
readUpdate(decoder, doc, transactionOrigin);
break;
default:
throw new Error("Unknown message type");
}
return messageType;
};
var BIT8 = 128;
var BITS7 = 127;
var floor = Math.floor;
var min = (a, b) => a < b ? a : b;
var max = (a, b) => a > b ? a : b;
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
var _encodeUtf8Polyfill = (str) => {
const encodedString = unescape(encodeURIComponent(str));
const len = encodedString.length;
const buf = new Uint8Array(len);
for (let i = 0; i < len; i++) {
buf[i] = /** @type {number} */
encodedString.codePointAt(i);
}
return buf;
};
var utf8TextEncoder = (
/** @type {TextEncoder} */
typeof TextEncoder !== "undefined" ? new TextEncoder() : null
);
var _encodeUtf8Native = (str) => utf8TextEncoder.encode(str);
var encodeUtf8 = utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill;
var utf8TextDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) {
utf8TextDecoder = null;
}
var create = (s) => new Error(s);
var Encoder = class {
constructor() {
this.cpos = 0;
this.cbuf = new Uint8Array(100);
this.bufs = [];
}
};
var createEncoder = () => new Encoder();
var length = (encoder) => {
let len = encoder.cpos;
for (let i = 0; i < encoder.bufs.length; i++) {
len += encoder.bufs[i].length;
}
return len;
};
var toUint8Array = (encoder) => {
const uint8arr = new Uint8Array(length(encoder));
let curPos = 0;
for (let i = 0; i < encoder.bufs.length; i++) {
const d = encoder.bufs[i];
uint8arr.set(d, curPos);
curPos += d.length;
}
uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
return uint8arr;
};
var write = (encoder, num) => {
const bufferLen = encoder.cbuf.length;
if (encoder.cpos === bufferLen) {
encoder.bufs.push(encoder.cbuf);
encoder.cbuf = new Uint8Array(bufferLen * 2);
encoder.cpos = 0;
}
encoder.cbuf[encoder.cpos++] = num;
};
var writeVarUint = (encoder, num) => {
while (num > BITS7) {
write(encoder, BIT8 | BITS7 & num);
num = floor(num / 128);
}
write(encoder, BITS7 & num);
};
var _strBuffer = new Uint8Array(3e4);
var _maxStrBSize = _strBuffer.length / 3;
var _writeVarStringNative = (encoder, str) => {
if (str.length < _maxStrBSize) {
const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
writeVarUint(encoder, written);
for (let i = 0; i < written; i++) {
write(encoder, _strBuffer[i]);
}
} else {
writeVarUint8Array(encoder, encodeUtf8(str));
}
};
var _writeVarStringPolyfill = (encoder, str) => {
const encodedString = unescape(encodeURIComponent(str));
const len = encodedString.length;
writeVarUint(encoder, len);
for (let i = 0; i < len; i++) {
write(
encoder,
/** @type {number} */
encodedString.codePointAt(i)
);
}
};
utf8TextEncoder && /** @type {any} */
utf8TextEncoder.encodeInto ? _writeVarStringNative : _writeVarStringPolyfill;
var writeUint8Array = (encoder, uint8Array) => {
const bufferLen = encoder.cbuf.length;
const cpos = encoder.cpos;
const leftCopyLen = min(bufferLen - cpos, uint8Array.length);
const rightCopyLen = uint8Array.length - leftCopyLen;
encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
encoder.cpos += leftCopyLen;
if (rightCopyLen > 0) {
encoder.bufs.push(encoder.cbuf);
encoder.cbuf = new Uint8Array(max(bufferLen * 2, rightCopyLen));
encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
encoder.cpos = rightCopyLen;
}
};
var writeVarUint8Array = (encoder, uint8Array) => {
writeVarUint(encoder, uint8Array.byteLength);
writeUint8Array(encoder, uint8Array);
};
var errorUnexpectedEndOfArray = create("Unexpected end of array");
var errorIntegerOutOfRange = create("Integer out of Range");
var Decoder = class {
/**
* @param {Uint8Array} uint8Array Binary data to decode
*/
constructor(uint8Array) {
this.arr = uint8Array;
this.pos = 0;
}
};
var createDecoder = (uint8Array) => new Decoder(uint8Array);
var readUint8Array = (decoder, len) => {
const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
decoder.pos += len;
return view;
};
var readVarUint8Array = (decoder) => readUint8Array(decoder, readVarUint(decoder));
var readVarUint = (decoder) => {
let num = 0;
let mult = 1;
const len = decoder.arr.length;
while (decoder.pos < len) {
const r = decoder.arr[decoder.pos++];
num = num + (r & BITS7) * mult;
mult *= 128;
if (r < BIT8) {
return num;
}
if (num > MAX_SAFE_INTEGER) {
throw errorIntegerOutOfRange;
}
}
throw errorUnexpectedEndOfArray;
};
var Sleeper = class {
constructor(timeout) {
this.promise = new Promise((resolve,