@roomservice/browser
Advanced tools
Comparing version 2.1.5 to 3.0.0-0
@@ -8,2 +8,3 @@ 'use strict'; | ||
var invariant = _interopDefault(require('tiny-invariant')); | ||
var core = require('@roomservice/core'); | ||
@@ -152,3 +153,3 @@ // export const WS_URL = 'ws://localhost:3452'; | ||
var fetchSession = function fetchSession(strategy, room, document) { | ||
var fetchSession = function fetchSession(strategy, ctx, room, document) { | ||
try { | ||
@@ -218,3 +219,6 @@ var _temp3 = function _temp3(_result) { | ||
if (typeof strategy === 'function') { | ||
return Promise.resolve(strategy(room)).then(function (result) { | ||
return Promise.resolve(strategy({ | ||
room: room, | ||
ctx: ctx | ||
})).then(function (result) { | ||
if (!result.user) { | ||
@@ -291,249 +295,18 @@ throw new Error("The auth function must return a 'user' key."); | ||
function unescapeID(checkpoint, id) { | ||
if (id === 'root') return 'root'; | ||
var _id$split = id.split(':'), | ||
index = _id$split[0], | ||
a = _id$split[1]; | ||
return index + ':' + checkpoint.actors[parseInt(a)]; | ||
} | ||
/** | ||
* A Reverse Tree is one where the children point to the | ||
* parents, instead of the otherway around. | ||
* | ||
* We use a reverse tree because the "insert" operation | ||
* can be done in paralell. | ||
*/ | ||
var ReverseTree = /*#__PURE__*/function () { | ||
function ReverseTree(actor) { | ||
// The number of operations used by this tree | ||
this.count = 0; | ||
this.actor = actor; | ||
this.nodes = {}; | ||
this.log = []; | ||
} | ||
var _proto = ReverseTree.prototype; | ||
_proto["import"] = function _import(checkpoint, listID) { | ||
!checkpoint ? invariant(false) : void 0; | ||
var list = checkpoint.lists[listID]; | ||
var afters = list.afters || []; | ||
var ids = list.ids || []; | ||
var values = list.values || []; // Rehydrate the cache | ||
for (var i = 0; i < afters.length; i++) { | ||
var node = { | ||
after: unescapeID(checkpoint, afters[i]), | ||
id: unescapeID(checkpoint, ids[i]), | ||
value: values[i] | ||
}; | ||
this.nodes[node.id] = node; | ||
this.log.push(node); | ||
} | ||
this.count = this.log.length; | ||
}; | ||
_proto.get = function get(itemID) { | ||
if (this.nodes[itemID]) { | ||
return this.nodes[itemID].value; | ||
} | ||
return undefined; | ||
}; | ||
_proto.insert = function insert(after, value, externalNewID) { | ||
!this.log ? invariant(false) : void 0; | ||
var id = externalNewID; | ||
if (!id) { | ||
id = this.count + ":" + this.actor; | ||
} | ||
this.count++; | ||
var node = { | ||
after: after, | ||
value: value, | ||
id: id | ||
}; | ||
this.nodes[id] = node; | ||
this.log.push(node); | ||
return id; | ||
}; | ||
_proto.put = function put(itemID, value) { | ||
if (!!this.nodes[itemID]) { | ||
this.nodes[itemID].value = value; | ||
} | ||
}; | ||
_proto.has = function has(itemID) { | ||
return !!this.nodes[itemID]; | ||
}; | ||
_proto["delete"] = function _delete(itemID) { | ||
if (!this.nodes[itemID]) return; | ||
this.nodes[itemID].value = { | ||
t: '' | ||
}; | ||
}; | ||
_proto.toTree = function toTree() { | ||
var childrenById = new Map(); | ||
var valueById = new Map(); | ||
for (var _iterator = _createForOfIteratorHelperLoose(this.log), _step; !(_step = _iterator()).done;) { | ||
var _childrenById$get; | ||
var node = _step.value; | ||
if (!childrenById.has(node.after)) { | ||
childrenById.set(node.after, []); | ||
} | ||
(_childrenById$get = childrenById.get(node.after)) === null || _childrenById$get === void 0 ? void 0 : _childrenById$get.push(node.id); | ||
valueById.set(node.id, node.value); | ||
} | ||
childrenById.forEach(function (children) { | ||
// sort by logical timestamp descending so that latest inserts appear first | ||
children.sort(function (a, b) { | ||
var _a$split = a.split(':'), | ||
leftCount = _a$split[0], | ||
leftActor = _a$split[1]; | ||
var _b$split = b.split(':'), | ||
rightCount = _b$split[0], | ||
rightActor = _b$split[1]; | ||
if (leftCount === rightCount) { | ||
return leftActor.localeCompare(rightActor); | ||
} | ||
return parseInt(rightCount) - parseInt(leftCount); | ||
}); | ||
}); | ||
return { | ||
childrenById: childrenById, | ||
valueById: valueById | ||
}; | ||
}; | ||
_proto.lastID = function lastID() { | ||
if (this.log.length === 0) { | ||
return 'root'; | ||
} | ||
var root = this.toTree(); // Search the right side of the tree | ||
function right(t, node) { | ||
var children = t.childrenById.get(node); | ||
if (!children || children.length === 0) { | ||
return node; | ||
} | ||
return right(t, children[children.length - 1]); | ||
} | ||
return right(root, 'root'); | ||
}; | ||
_proto.preOrderTraverse = function preOrderTraverse() { | ||
// -- Convert the log into a regular tree | ||
var tree = this.toTree(); | ||
var seenNodes = new Set(); // -- Do a depth-first traversal to get the result | ||
function preOrder(t, node) { | ||
if (seenNodes.has(node)) { | ||
console.warn('RoomService list cycle detected. Consider updating @roomservice/browser.'); | ||
return []; | ||
} | ||
seenNodes.add(node); | ||
var result = []; | ||
var value = t.valueById.get(node); | ||
if (value) { | ||
if (typeof value === 'string') { | ||
result.push({ | ||
value: value, | ||
id: node | ||
}); | ||
} else if ('t' in value && value.t === '') ; else { | ||
throw new Error('Unimplemented'); | ||
} | ||
} | ||
var children = t.childrenById.get(node); | ||
if (!children || children.length === 0) { | ||
return result; | ||
} | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(children), _step2; !(_step2 = _iterator2()).done;) { | ||
var child = _step2.value; | ||
result = result.concat(preOrder(t, child)); | ||
} | ||
return result; | ||
} | ||
return preOrder(tree, 'root'); | ||
}; | ||
_proto.toArray = function toArray() { | ||
return this.preOrderTraverse().map(function (idValue) { | ||
return idValue.value; | ||
}); | ||
}; | ||
_createClass(ReverseTree, [{ | ||
key: "length", | ||
get: function get() { | ||
return Object.keys(this.nodes).length; | ||
} | ||
}]); | ||
return ReverseTree; | ||
}(); | ||
function escape(value) { | ||
return JSON.stringify(value); | ||
} | ||
function unescape(value) { | ||
try { | ||
return JSON.parse(value); | ||
} catch (error) { | ||
return value; | ||
} | ||
} | ||
var InnerListClient = /*#__PURE__*/function () { | ||
function InnerListClient(props) { | ||
// Map indexes to item ids | ||
this.itemIDs = []; | ||
this.roomID = props.roomID; | ||
this.docID = props.docID; | ||
this.ws = props.ws; | ||
this.bus = props.bus; | ||
this.actor = props.actor; | ||
this.id = props.listID; | ||
this.ws = props.ws; | ||
this.rt = new ReverseTree(props.actor); | ||
!props.checkpoint.lists[props.listID] ? invariant(false, "Unknown listid '" + props.listID + "' in checkpoint.") : void 0; | ||
this.rt["import"](props.checkpoint, props.listID); | ||
var list = props.checkpoint.lists[props.listID]; | ||
var ids = list.ids || []; | ||
for (var i = 0; i < ids.length; i++) { | ||
var val = props.checkpoint.lists[props.listID].values[i]; | ||
var _ListInterpreter$newL = core.ListInterpreter.newList(props.docID, props.listID, props.actor), | ||
meta = _ListInterpreter$newL.meta, | ||
store = _ListInterpreter$newL.store; | ||
if (typeof val === 'object' && val['t'] === '') { | ||
continue; // skip tombstones | ||
} | ||
this.itemIDs.push(unescapeID(props.checkpoint, ids[i])); | ||
} | ||
this.meta = meta; | ||
this.store = store; | ||
!props.checkpoint.lists[props.listID] ? invariant(false, "Unknown listid '" + props.listID + "' in checkpoint.") : void 0; | ||
core.ListInterpreter.importFromRawCheckpoint(this.store, props.checkpoint, this.meta.listID); | ||
} | ||
@@ -548,2 +321,6 @@ | ||
}); | ||
this.bus.publish({ | ||
args: cmd, | ||
from: this.actor | ||
}); | ||
}; | ||
@@ -557,43 +334,4 @@ | ||
_proto.dangerouslyUpdateClientDirectly = function dangerouslyUpdateClientDirectly(cmd) { | ||
if (cmd.length < 3) { | ||
throw new Error('Unexpected command: ' + cmd); | ||
} | ||
var keyword = cmd[0]; | ||
var docID = cmd[1]; | ||
var id = cmd[2]; | ||
if (docID !== this.docID || id !== this.id) { | ||
throw new Error('Command unexpectedly routed to the wrong client'); | ||
} | ||
switch (keyword) { | ||
case 'lins': | ||
var insAfter = cmd[3]; | ||
var insItemID = cmd[4]; | ||
var insValue = cmd[5]; | ||
this.itemIDs.splice(this.itemIDs.findIndex(function (f) { | ||
return f === insAfter; | ||
}) + 1, 0, insItemID); | ||
this.rt.insert(insAfter, insValue, insItemID); | ||
break; | ||
case 'lput': | ||
var putItemID = cmd[3]; | ||
var putVal = cmd[4]; | ||
this.rt.put(putItemID, putVal); | ||
break; | ||
case 'ldel': | ||
var delItemID = cmd[3]; | ||
this.rt["delete"](delItemID); | ||
this.itemIDs.splice(this.itemIDs.findIndex(function (f) { | ||
return f === delItemID; | ||
}), 1); | ||
break; | ||
default: | ||
throw new Error('Unexpected command keyword: ' + keyword); | ||
} | ||
core.ListInterpreter.validateCommand(this.meta, cmd); | ||
core.ListInterpreter.applyCommand(this.store, cmd); | ||
return this.clone(); | ||
@@ -603,30 +341,9 @@ }; | ||
_proto.get = function get(index) { | ||
var itemID = this.itemIDs[index]; | ||
if (!itemID) return undefined; | ||
var val = this.rt.get(itemID); | ||
if (!val) return undefined; | ||
if (typeof val === 'object') { | ||
if (val.t === '') { | ||
return undefined; | ||
} | ||
throw new Error('Unimplemented references'); | ||
} | ||
return unescape(val); | ||
return core.ListInterpreter.get(this.store, index); | ||
}; | ||
_proto.set = function set(index, val) { | ||
var itemID = this.itemIDs[index]; | ||
var cmd = core.ListInterpreter.runSet(this.store, this.meta, index, val); // Remote | ||
if (!itemID) { | ||
throw new Error("Index '" + index + "' doesn't already exist. Try .push() or .insertAfter() instead."); | ||
} | ||
var escaped = escape(val); // Local | ||
this.rt.put(itemID, escaped); // Remote | ||
this.sendCmd(['lput', this.docID, this.id, itemID, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -636,18 +353,10 @@ }; | ||
_proto["delete"] = function _delete(index) { | ||
if (this.itemIDs.length === 0) { | ||
return this.clone(); | ||
} | ||
var cmd = core.ListInterpreter.runDelete(this.store, this.meta, index); | ||
var itemID = this.itemIDs[index]; | ||
if (!itemID) { | ||
console.warn('Unknown index: ', index, this.itemIDs); | ||
if (!cmd) { | ||
return this.clone(); | ||
} // Local | ||
} // Remote | ||
this.rt["delete"](itemID); | ||
this.itemIDs.splice(index, 1); // Remote | ||
this.sendCmd(['ldel', this.docID, this.id, itemID]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -661,41 +370,9 @@ }; | ||
_proto.insertAt = function insertAt(index, val) { | ||
if (index < 0) { | ||
throw 'negative indices unsupported'; | ||
} | ||
var cmd = core.ListInterpreter.runInsertAt(this.store, this.meta, index, val); // Remote | ||
var afterID; | ||
if (index == 0) { | ||
afterID = 'root'; | ||
} else { | ||
afterID = this.itemIDs[index - 1]; | ||
} | ||
if (!afterID) { | ||
throw new RangeError("List '" + this.id + "' has no index: '" + index + "'"); | ||
} | ||
var escaped = escape(val); // Local | ||
var itemID = this.rt.insert(afterID, escaped); | ||
this.itemIDs.splice(index, 0, itemID); // Remote | ||
this.sendCmd(['lins', this.docID, this.id, afterID, itemID, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
}; | ||
_proto.pushOne = function pushOne(val) { | ||
var lastID = this.rt.lastID(); | ||
var escaped = escape(val); // Local | ||
var itemID = this.rt.insert(lastID, escaped); | ||
this.itemIDs.push(itemID); // Remote | ||
this.sendCmd(['lins', this.docID, this.id, lastID, itemID, escaped]); | ||
return this.clone(); | ||
}; | ||
_proto.push = function push() { | ||
var self; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -705,20 +382,18 @@ args[_key] = arguments[_key]; | ||
for (var _i = 0, _args = args; _i < _args.length; _i++) { | ||
var arg = _args[_i]; | ||
self = this.pushOne(arg); | ||
var cmds = core.ListInterpreter.runPush.apply(core.ListInterpreter, [this.store, this.meta].concat(args)); | ||
for (var _iterator = _createForOfIteratorHelperLoose(cmds), _step; !(_step = _iterator()).done;) { | ||
var cmd = _step.value; | ||
this.sendCmd(cmd); | ||
} | ||
return self; | ||
return this; | ||
}; | ||
_proto.map = function map(fn) { | ||
return this.rt.preOrderTraverse().map(function (idValue, i) { | ||
return fn(unescape(idValue.value), i, idValue.id); | ||
}); | ||
return core.ListInterpreter.map(this.store, fn); | ||
}; | ||
_proto.toArray = function toArray() { | ||
return this.rt.toArray().map(function (m) { | ||
return unescape(m); | ||
}); | ||
return core.ListInterpreter.toArray(this.store); | ||
}; | ||
@@ -732,14 +407,13 @@ | ||
this.roomID = props.roomID; | ||
this.docID = props.docID; | ||
this.id = props.mapID; | ||
this.ws = props.ws; | ||
this.store = {}; // import | ||
this.bus = props.bus; | ||
this.actor = props.actor; | ||
for (var k in props.checkpoint) { | ||
var val = props.checkpoint[k]; | ||
var _MapInterpreter$newMa = core.MapInterpreter.newMap(props.docID, props.mapID), | ||
store = _MapInterpreter$newMa.store, | ||
meta = _MapInterpreter$newMa.meta; | ||
if (typeof val === 'string') { | ||
this.store[k] = unescape(val); | ||
} | ||
} | ||
this.store = store; | ||
this.meta = meta; | ||
core.MapInterpreter.importFromRawCheckpoint(this.store, props.checkpoint, this.meta.mapID); | ||
} | ||
@@ -754,2 +428,6 @@ | ||
}); | ||
this.bus.publish({ | ||
from: this.actor, | ||
args: cmd | ||
}); | ||
}; | ||
@@ -762,40 +440,4 @@ | ||
_proto.dangerouslyUpdateClientDirectly = function dangerouslyUpdateClientDirectly(cmd) { | ||
if (cmd.length < 3) { | ||
throw new Error('Unexpected command: ' + cmd); | ||
} | ||
var keyword = cmd[0]; | ||
var docID = cmd[1]; | ||
var id = cmd[2]; | ||
if (docID !== this.docID || id !== this.id) { | ||
throw new Error('Command unexpectedly routed to the wrong client'); | ||
} | ||
switch (keyword) { | ||
case 'mput': | ||
if (cmd.length !== 5) { | ||
console.error('Malformed command ', cmd); | ||
break; | ||
} | ||
var putKey = cmd[3]; | ||
var putVal = cmd[4]; | ||
this.store[putKey] = unescape(putVal); | ||
break; | ||
case 'mdel': | ||
if (cmd.length !== 4) { | ||
console.error('Malformed command ', cmd); | ||
break; | ||
} | ||
var delKey = cmd[3]; | ||
delete this.store[delKey]; | ||
break; | ||
default: | ||
throw new Error('Unexpected command keyword: ' + keyword); | ||
} | ||
core.MapInterpreter.validateCommand(this.meta, cmd); | ||
core.MapInterpreter.applyCommand(this.store, cmd); | ||
return this.clone(); | ||
@@ -809,7 +451,5 @@ }; | ||
_proto.set = function set(key, value) { | ||
var escaped = escape(value); // Local | ||
var cmd = core.MapInterpreter.runSet(this.store, this.meta, key, value); // Remote | ||
this.store[key] = value; // Remote | ||
this.sendCmd(['mput', this.docID, this.id, key, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -830,6 +470,5 @@ }; | ||
_proto["delete"] = function _delete(key) { | ||
// local | ||
delete this.store[key]; // remote | ||
var cmd = core.MapInterpreter.runDelete(this.store, this.meta, key); // remote | ||
this.sendCmd(['mdel', this.docID, this.id, key]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -839,2 +478,7 @@ }; | ||
_createClass(InnerMapClient, [{ | ||
key: "id", | ||
get: function get() { | ||
return this.meta.mapID; | ||
} | ||
}, { | ||
key: "keys", | ||
@@ -891,2 +535,4 @@ get: function get() { | ||
this.cache = {}; | ||
this.bus = props.bus; | ||
this.key = props.key; | ||
@@ -907,9 +553,9 @@ var sendPres = function sendPres(_, args) { | ||
_proto.getAll = function getAll(key) { | ||
_proto.getAll = function getAll() { | ||
try { | ||
var _this3 = this; | ||
return Promise.resolve(fetchPresence(PRESENCE_URL, _this3.token, _this3.roomID, key)).then(function (val) { | ||
_this3.cache[key] = val; | ||
return _this3.withoutExpiredAndSelf(key); | ||
return Promise.resolve(fetchPresence(PRESENCE_URL, _this3.token, _this3.roomID, _this3.key)).then(function (val) { | ||
_this3.cache[_this3.key] = val; | ||
return _this3.withoutExpiredAndSelf(_this3.key); | ||
}); | ||
@@ -971,13 +617,17 @@ } catch (e) { | ||
* | ||
* @param key | ||
* @param value Any arbitrary object, string, boolean, or number. | ||
* @param exp (Optional) Expiration time in seconds | ||
*/ | ||
_proto.set = function set(key, value, exp) { | ||
_proto.set = function set(value, exp) { | ||
var addition = exp ? exp : 60; // Convert to unix + add seconds | ||
var expAt = Math.round(new Date().getTime() / 1000) + addition; | ||
this.sendPres(key, { | ||
this.bus.publish({ | ||
key: this.key, | ||
value: value, | ||
expAt: expAt | ||
}); | ||
this.sendPres(this.key, { | ||
room: this.roomID, | ||
key: key, | ||
key: this.key, | ||
value: JSON.stringify(value), | ||
@@ -987,11 +637,11 @@ expAt: expAt | ||
if (!this.cache[key]) { | ||
this.cache[key] = {}; | ||
if (!this.cache[this.key]) { | ||
this.cache[this.key] = {}; | ||
} | ||
this.cache[key][this.actor] = { | ||
this.cache[this.key][this.actor] = { | ||
value: value, | ||
expAt: new Date(expAt * 1000) | ||
}; | ||
return this.withoutExpiredAndSelf(key); | ||
return this.withoutExpiredAndSelf(this.key); | ||
}; | ||
@@ -1036,35 +686,50 @@ | ||
function base64toArrayBuffer(vs) { | ||
var binary = window.atob(vs); | ||
var len = binary.length; | ||
var bytes = new Uint8Array(len); | ||
var errNoInfiniteLoop = function errNoInfiniteLoop() { | ||
return new Error('Infinite loop detected, see more: See more: https://err.sh/getroomservice/browser/infinite-loop'); | ||
}; | ||
for (var i = 0; i < len; i++) { | ||
bytes[i] = binary.charCodeAt(i); | ||
// Local pubsub, so that if you call .set in one place | ||
// it will trigger a .subscribe elsewhere, without | ||
// needing to go through the websockets | ||
var LocalBus = /*#__PURE__*/function () { | ||
function LocalBus() { | ||
this.isPublishing = false; | ||
this.subs = new Set(); | ||
} | ||
return bytes.buffer; | ||
} | ||
function isOlderVS(older, newer) { | ||
if (!older) return true; | ||
if (!newer) return false; // These are ALWAYS 10 bytes | ||
var _proto = LocalBus.prototype; | ||
var olderArr = new Uint8Array(base64toArrayBuffer(older).slice(0, 9)); | ||
var newerArr = new Uint8Array(base64toArrayBuffer(newer).slice(0, 9)); | ||
_proto.unsubscribe = function unsubscribe(fn) { | ||
this.subs["delete"](fn); | ||
}; | ||
for (var i = 0; i < olderArr.byteLength; i++) { | ||
if (newerArr[i] > olderArr[i]) return true; | ||
if (newerArr[i] < olderArr[i]) return false; | ||
} | ||
_proto.subscribe = function subscribe(fn) { | ||
this.subs.add(fn); | ||
return fn; | ||
}; | ||
return false; | ||
} | ||
_proto.publish = function publish(msg) { | ||
// This is an infinite loop | ||
if (this.isPublishing) { | ||
throw errNoInfiniteLoop(); | ||
} | ||
var createRoom = function createRoom(conn, docsURL, authStrategy, room, document) { | ||
this.isPublishing = true; | ||
this.subs.forEach(function (fn) { | ||
fn(msg); | ||
}); | ||
this.isPublishing = false; | ||
}; | ||
return LocalBus; | ||
}(); | ||
var createRoom = function createRoom(params) { | ||
try { | ||
return Promise.resolve(fetchSession(authStrategy, room, document)).then(function (sess) { | ||
return Promise.resolve(fetchDocument(docsURL, sess.token, sess.docID)).then(function (_ref2) { | ||
return Promise.resolve(fetchSession(params.authStrategy, params.authCtx, params.room, params.document)).then(function (sess) { | ||
return Promise.resolve(fetchDocument(params.docsURL, sess.token, sess.docID)).then(function (_ref2) { | ||
var body = _ref2.body; | ||
var roomClient = new RoomClient({ | ||
conn: conn, | ||
conn: params.conn, | ||
actor: sess.guestReference, | ||
@@ -1104,2 +769,3 @@ checkpoint: body, | ||
this.InnerPresenceClient = undefined; | ||
var vs = core.vsReader(window.atob); | ||
this.ws.bind('doc:fwd', function (body) { | ||
@@ -1116,3 +782,3 @@ if (body.room !== _this.roomID) return; | ||
if (isOlderVS(body.vs, _this.checkpoint.vs)) return; // Ignore validated commands | ||
if (vs.isOlderVS(body.vs, _this.checkpoint.vs)) return; // Ignore validated commands | ||
@@ -1140,3 +806,3 @@ if (body.from === _this.actor) return; | ||
var client = _this.presence(); | ||
var client = _this.presence('_____any'); | ||
@@ -1161,10 +827,3 @@ var newClient = client.dangerouslyUpdateClientDirectly('room:rm_guest', body); | ||
if (!this.mapClients[objID]) { | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint.maps[objID] || {}, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
mapID: objID, | ||
ws: this.ws | ||
}); | ||
this.mapClients[objID] = m; | ||
this.createMapLocally(objID); | ||
} | ||
@@ -1177,3 +836,3 @@ | ||
var cb = _step2.value; | ||
cb(updatedClient, body.from); | ||
cb(updatedClient.toObject(), body.from); | ||
} | ||
@@ -1184,11 +843,3 @@ }; | ||
if (!this.listClients[objID]) { | ||
var l = new InnerListClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: objID, | ||
ws: this.ws, | ||
actor: this.actor | ||
}); | ||
this.listClients[objID] = l; | ||
this.createListLocally(objID); | ||
} | ||
@@ -1201,3 +852,3 @@ | ||
var cb = _step3.value; | ||
cb(updatedClient, body.from); | ||
cb(updatedClient.toArray(), body.from); | ||
} | ||
@@ -1211,3 +862,3 @@ }; | ||
if (body.from === this.actor) return; | ||
var client = this.presence(); | ||
var client = this.presence(body.key); | ||
var key = body.key; | ||
@@ -1306,2 +957,27 @@ var now = new Date().getTime() / 1000; | ||
_proto.createListLocally = function createListLocally(name) { | ||
var _this7 = this; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
var client = _this7.listClients[name]; | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(_this7.listCallbacksByObjID[name] || []), _step6; !(_step6 = _iterator6()).done;) { | ||
var cb = _step6.value; | ||
cb(client.toArray(), body.from); | ||
} | ||
}); | ||
var l = new InnerListClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: name, | ||
ws: this.ws, | ||
actor: this.actor, | ||
bus: bus | ||
}); | ||
this.listClients[name] = l; | ||
return l; | ||
}; | ||
_proto.list = function list(name) { | ||
@@ -1326,12 +1002,28 @@ if (this.listClients[name]) { | ||
var l = new InnerListClient({ | ||
return this.createListLocally(name); | ||
}; | ||
_proto.createMapLocally = function createMapLocally(name) { | ||
var _this8 = this; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
var client = _this8.mapClients[name]; | ||
for (var _iterator7 = _createForOfIteratorHelperLoose(_this8.mapCallbacksByObjID[name] || []), _step7; !(_step7 = _iterator7()).done;) { | ||
var cb = _step7.value; | ||
cb(client.toObject(), body.from); | ||
} | ||
}); | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: name, | ||
mapID: name, | ||
ws: this.ws, | ||
bus: bus, | ||
actor: this.actor | ||
}); | ||
this.listClients[name] = l; | ||
return l; | ||
this.mapClients[name] = m; | ||
return m; | ||
}; | ||
@@ -1352,14 +1044,8 @@ | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint.maps[name] || {}, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
mapID: name, | ||
ws: this.ws | ||
}); | ||
this.mapClients[name] = m; | ||
return m; | ||
return this.createMapLocally(name); | ||
}; | ||
_proto.presence = function presence() { | ||
_proto.presence = function presence(key) { | ||
var _this9 = this; | ||
if (this.InnerPresenceClient) { | ||
@@ -1369,2 +1055,12 @@ return this.InnerPresenceClient; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
_this9.dispatchPresenceCmd({ | ||
key: body.key, | ||
value: body.value, | ||
expAt: body.expAt, | ||
from: _this9.actor, | ||
room: _this9.roomID | ||
}); | ||
}); | ||
var p = new InnerPresenceClient({ | ||
@@ -1374,3 +1070,5 @@ roomID: this.roomID, | ||
actor: this.actor, | ||
token: this.token | ||
token: this.token, | ||
bus: bus, | ||
key: key | ||
}); | ||
@@ -1391,3 +1089,3 @@ | ||
return this.subscribePresence(obj, onChangeFnOrString, onChangeFn); | ||
} // create new closure so fns can be subscribed/unsubscribed multiple times | ||
} // create new closure so fns can be subscribed/unsubscribed multiple times | ||
@@ -1439,4 +1137,4 @@ | ||
_proto.unsubscribe = function unsubscribe(listeners) { | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(listeners), _step6; !(_step6 = _iterator6()).done;) { | ||
var l = _step6.value; | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(listeners), _step8; !(_step8 = _iterator8()).done;) { | ||
var l = _step8.value; | ||
@@ -1479,2 +1177,3 @@ if (l.objID) { | ||
this.auth = params.auth; | ||
this.ctx = params.ctx || {}; | ||
} | ||
@@ -1493,3 +1192,10 @@ | ||
var ws = new WebSocket(WS_URL); | ||
return Promise.resolve(createRoom(ws, DOCS_URL, _this2.auth, name, 'default')).then(function (client) { | ||
return Promise.resolve(createRoom({ | ||
conn: ws, | ||
docsURL: DOCS_URL, | ||
authStrategy: _this2.auth, | ||
authCtx: _this2.ctx, | ||
room: name, | ||
document: 'default' | ||
})).then(function (client) { | ||
_this2.roomClients[name] = client; | ||
@@ -1496,0 +1202,0 @@ return client; |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=(e=require("tiny-invariant"))&&"object"==typeof e&&"default"in e?e.default:e;function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function n(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function o(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return i(e,void 0);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?i(e,void 0):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(r=e[Symbol.iterator]()).next.bind(r)}var s=function(){function e(e){var t=this;this.callbacks={},this.lastTime=0,this.msgsThisMilisecond=0,this.conn=e,this.conn.onmessage=function(e){var r=JSON.parse(e.data);t.dispatch(r.type,r.body)}}var t=e.prototype;return t.timestamp=function(){var e=Date.now();return e===this.lastTime?this.msgsThisMilisecond++:(this.lastTime=e,this.msgsThisMilisecond=0),e+":"+this.msgsThisMilisecond},t.send=function(e,t){var r=this,n=this.timestamp();this.conn.readyState!==this.conn.CONNECTING?this.conn.send(JSON.stringify({type:e,ts:n,ver:0,body:t})):setTimeout((function(){r.send(e,t)}),100+100*Math.random())},t.bind=function(e,t){return this.callbacks[e]=this.callbacks[e]||[],this.callbacks[e].push(t),t},t.unbind=function(e,t){this.callbacks[e]=this.callbacks[e].filter((function(e){return e!==t}))},t.dispatch=function(e,t){var r=this.callbacks[e];if(r)for(var n=0;n<r.length;n++)r[n](t)},e}();function c(e,t){if("root"===t)return"root";var r=t.split(":");return r[0]+":"+e.actors[parseInt(r[1])]}var a=function(){function e(e){this.count=0,this.actor=e,this.nodes={},this.log=[]}var r=e.prototype;return r.import=function(e,r){e||t(!1);for(var n=e.lists[r],i=n.afters||[],o=n.ids||[],s=n.values||[],a=0;a<i.length;a++){var h={after:c(e,i[a]),id:c(e,o[a]),value:s[a]};this.nodes[h.id]=h,this.log.push(h)}this.count=this.log.length},r.get=function(e){if(this.nodes[e])return this.nodes[e].value},r.insert=function(e,r,n){this.log||t(!1);var i=n;i||(i=this.count+":"+this.actor),this.count++;var o={after:e,value:r,id:i};return this.nodes[i]=o,this.log.push(o),i},r.put=function(e,t){this.nodes[e]&&(this.nodes[e].value=t)},r.has=function(e){return!!this.nodes[e]},r.delete=function(e){this.nodes[e]&&(this.nodes[e].value={t:""})},r.toTree=function(){for(var e,t=new Map,r=new Map,n=o(this.log);!(e=n()).done;){var i,s=e.value;t.has(s.after)||t.set(s.after,[]),null===(i=t.get(s.after))||void 0===i||i.push(s.id),r.set(s.id,s.value)}return t.forEach((function(e){e.sort((function(e,t){var r=e.split(":"),n=r[0],i=r[1],o=t.split(":"),s=o[0];return n===s?i.localeCompare(o[1]):parseInt(s)-parseInt(n)}))})),{childrenById:t,valueById:r}},r.lastID=function(){return 0===this.log.length?"root":function e(t,r){var n=t.childrenById.get(r);return n&&0!==n.length?e(t,n[n.length-1]):r}(this.toTree(),"root")},r.preOrderTraverse=function(){var e=this.toTree(),t=new Set;return function e(r,n){if(t.has(n))return console.warn("RoomService list cycle detected. Consider updating @roomservice/browser."),[];t.add(n);var i=[],s=r.valueById.get(n);if(s)if("string"==typeof s)i.push({value:s,id:n});else if(!("t"in s)||""!==s.t)throw new Error("Unimplemented");var c=r.childrenById.get(n);if(!c||0===c.length)return i;for(var a,h=o(c);!(a=h()).done;)i=i.concat(e(r,a.value));return i}(e,"root")},r.toArray=function(){return this.preOrderTraverse().map((function(e){return e.value}))},n(e,[{key:"length",get:function(){return Object.keys(this.nodes).length}}]),e}();function h(e){return JSON.stringify(e)}function u(e){try{return JSON.parse(e)}catch(t){return e}}var l=function(){function e(e){this.itemIDs=[],this.roomID=e.roomID,this.docID=e.docID,this.id=e.listID,this.ws=e.ws,this.rt=new a(e.actor),e.checkpoint.lists[e.listID]||t(!1),this.rt.import(e.checkpoint,e.listID);for(var r=e.checkpoint.lists[e.listID].ids||[],n=0;n<r.length;n++){var i=e.checkpoint.lists[e.listID].values[n];"object"==typeof i&&""===i.t||this.itemIDs.push(c(e.checkpoint,r[n]))}}var r=e.prototype;return r.sendCmd=function(e){this.ws.send("doc:cmd",{room:this.roomID,args:e})},r.clone=function(){return Object.assign(Object.create(Object.getPrototypeOf(this)),this)},r.dangerouslyUpdateClientDirectly=function(e){if(e.length<3)throw new Error("Unexpected command: "+e);var t=e[0];if(e[1]!==this.docID||e[2]!==this.id)throw new Error("Command unexpectedly routed to the wrong client");switch(t){case"lins":var r=e[3],n=e[4],i=e[5];this.itemIDs.splice(this.itemIDs.findIndex((function(e){return e===r}))+1,0,n),this.rt.insert(r,i,n);break;case"lput":this.rt.put(e[3],e[4]);break;case"ldel":var o=e[3];this.rt.delete(o),this.itemIDs.splice(this.itemIDs.findIndex((function(e){return e===o})),1);break;default:throw new Error("Unexpected command keyword: "+t)}return this.clone()},r.get=function(e){var t=this.itemIDs[e];if(t){var r=this.rt.get(t);if(r){if("object"==typeof r){if(""===r.t)return;throw new Error("Unimplemented references")}return u(r)}}},r.set=function(e,t){var r=this.itemIDs[e];if(!r)throw new Error("Index '"+e+"' doesn't already exist. Try .push() or .insertAfter() instead.");var n=h(t);return this.rt.put(r,n),this.sendCmd(["lput",this.docID,this.id,r,n]),this.clone()},r.delete=function(e){if(0===this.itemIDs.length)return this.clone();var t=this.itemIDs[e];return t?(this.rt.delete(t),this.itemIDs.splice(e,1),this.sendCmd(["ldel",this.docID,this.id,t]),this.clone()):(console.warn("Unknown index: ",e,this.itemIDs),this.clone())},r.insertAfter=function(e,t){return this.insertAt(e+1,t)},r.insertAt=function(e,t){if(e<0)throw"negative indices unsupported";var r;if(!(r=0==e?"root":this.itemIDs[e-1]))throw new RangeError("List '"+this.id+"' has no index: '"+e+"'");var n=h(t),i=this.rt.insert(r,n);return this.itemIDs.splice(e,0,i),this.sendCmd(["lins",this.docID,this.id,r,i,n]),this.clone()},r.pushOne=function(e){var t=this.rt.lastID(),r=h(e),n=this.rt.insert(t,r);return this.itemIDs.push(n),this.sendCmd(["lins",this.docID,this.id,t,n,r]),this.clone()},r.push=function(){for(var e,t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];for(var i=0,o=r;i<o.length;i++){var s=o[i];e=this.pushOne(s)}return e},r.map=function(e){return this.rt.preOrderTraverse().map((function(t,r){return e(u(t.value),r,t.id)}))},r.toArray=function(){return this.rt.toArray().map((function(e){return u(e)}))},e}(),d=function(){function e(e){for(var t in this.roomID=e.roomID,this.docID=e.docID,this.id=e.mapID,this.ws=e.ws,this.store={},e.checkpoint){var r=e.checkpoint[t];"string"==typeof r&&(this.store[t]=u(r))}}var t=e.prototype;return t.sendCmd=function(e){this.ws.send("doc:cmd",{room:this.roomID,args:e})},t.clone=function(){return Object.assign(Object.create(Object.getPrototypeOf(this)),this)},t.dangerouslyUpdateClientDirectly=function(e){if(e.length<3)throw new Error("Unexpected command: "+e);var t=e[0];if(e[1]!==this.docID||e[2]!==this.id)throw new Error("Command unexpectedly routed to the wrong client");switch(t){case"mput":if(5!==e.length){console.error("Malformed command ",e);break}this.store[e[3]]=u(e[4]);break;case"mdel":if(4!==e.length){console.error("Malformed command ",e);break}delete this.store[e[3]];break;default:throw new Error("Unexpected command keyword: "+t)}return this.clone()},t.get=function(e){return this.store[e]},t.set=function(e,t){var r=h(t);return this.store[e]=t,this.sendCmd(["mput",this.docID,this.id,e,r]),this.clone()},t.toObject=function(){for(var e,t={},r=o(this.keys);!(e=r()).done;){var n=e.value;t[n]=this.get(n)}return t},t.delete=function(e){return delete this.store[e],this.sendCmd(["mdel",this.docID,this.id,e]),this.clone()},n(e,[{key:"keys",get:function(){return Object.keys(this.store)}}]),e}(),f=function(){function e(e){var t=this;this.roomID=e.roomID,this.ws=e.ws,this.actor=e.actor,this.token=e.token,this.cache={},this.sendPres=function(e,t,r){void 0===r&&(r=!1);var n={},i=!0;return function(){var t=arguments,o=this,s=r&&i,c=function(){e.apply(o,t),n[t[0]]=null};s&&(i=!1,c()),n[arguments[0]]||(n[arguments[0]]=setTimeout(c,40))}}((function(e,r){t.ws.send("presence:cmd",r)}))}var t=e.prototype;return t.getAll=function(e){try{var t=this;return Promise.resolve(function(e,t,r,n){try{return Promise.resolve(fetch("https://super.roomservice.dev/presence/"+r+"/"+encodeURIComponent(n),{headers:{Authorization:"Bearer: "+t}})).then((function(e){return Promise.resolve(e.json()).then((function(e){for(var t in e)if("string"==typeof e[t].value){var r=void 0;try{r=JSON.parse(e[t].value)}catch(e){}r&&(e[t].value=r)}return e}))}))}catch(e){return Promise.reject(e)}}(0,t.token,t.roomID,e)).then((function(r){return t.cache[e]=r,t.withoutExpiredAndSelf(e)}))}catch(e){return Promise.reject(e)}},t.withoutExpiredAndSelf=function(e){var t={};for(var r in this.cache[e]){var n=this.cache[e][r];r===this.actor&&delete this.cache[e][r],new Date>n.expAt?delete this.cache[e][r]:t[r]=n.value}return t},t.withoutActorOrExpired=function(e){var t={};for(var r in this.cache)for(var n in this.cache[r]){var i=this.cache[r][n];i&&(n===e&&this.cache[r][n]||new Date>i.expAt?delete this.cache[r][n]:t[n]=i.value)}return t},t.set=function(e,t,r){var n=r||60,i=Math.round((new Date).getTime()/1e3)+n;return this.sendPres(e,{room:this.roomID,key:e,value:JSON.stringify(t),expAt:i}),this.cache[e]||(this.cache[e]={}),this.cache[e][this.actor]={value:t,expAt:new Date(1e3*i)},this.withoutExpiredAndSelf(e)},t.dangerouslyUpdateClientDirectly=function(e,t){if("room:rm_guest"===e)return this.withoutActorOrExpired(t.guest);if("presence:expire"===e)return this.withoutExpiredAndSelf(t.key);if(t.room!==this.roomID)return!1;if(t.from===this.actor)return!1;var r={expAt:new Date(1e3*t.expAt),value:JSON.parse(t.value)};return this.cache[t.key]||(this.cache[t.key]={}),this.cache[t.key][t.from]=r,this.withoutExpiredAndSelf(t.key)},n(e,[{key:"me",get:function(){return console.warn("presence.me() is deprecated and will be removed in a future version!"),this.actor}}]),e}();function m(e){for(var t=window.atob(e),r=t.length,n=new Uint8Array(r),i=0;i<r;i++)n[i]=t.charCodeAt(i);return n.buffer}var p=["mcreate","mput","mputref","mdel"],v=["lcreate","lins","linsref","lput","lputref","ldel"],y=function(){function e(e){var t=this;this.listClients={},this.mapClients={},this.expires={},this.mapCallbacksByObjID={},this.listCallbacksByObjID={},this.presenceCallbacksByKey={},this.ws=new s(e.conn),this.token=e.token,this.roomID=e.roomID,this.docID=e.checkpoint.id,this.actor=e.actor,this.checkpoint=e.checkpoint,this.InnerPresenceClient=void 0,this.ws.bind("doc:fwd",(function(e){if(e.room===t.roomID)if(!e.args||e.args.length<3)console.error("Unexpected command: ",e.args);else if(!function(e,t){if(!e)return!0;if(!t)return!1;for(var r=new Uint8Array(m(e).slice(0,9)),n=new Uint8Array(m(t).slice(0,9)),i=0;i<r.byteLength;i++){if(n[i]>r[i])return!0;if(n[i]<r[i])return!1}return!1}(e.vs,t.checkpoint.vs)&&e.from!==t.actor){var r=[e.args[0],e.args[1],e.args[2]],n=r[0],i=r[2];r[1]===t.docID&&(p.includes(n)?t.dispatchMapCmd(i,e):v.includes(n)?t.dispatchListCmd(i,e):console.warn("Unhandled Room Service doc:fwd command: "+n+". Consider updating the Room Service client."))}})),this.ws.bind("presence:fwd",(function(e){t.dispatchPresenceCmd(e)})),this.ws.bind("room:rm_guest",(function(e){if(e.room===t.roomID)for(var r=t.presence().dangerouslyUpdateClientDirectly("room:rm_guest",e),n=0,i=Object.entries(t.presenceCallbacksByKey);n<i.length;n++)for(var s,c=o(i[n][1]);!(s=c()).done;)(0,s.value)(r,e.guest)}))}var r=e.prototype;return r.dispatchMapCmd=function(e,t){if(!this.mapClients[e]){var r=new d({checkpoint:this.checkpoint.maps[e]||{},roomID:this.roomID,docID:this.docID,mapID:e,ws:this.ws});this.mapClients[e]=r}for(var n,i=this.mapClients[e].dangerouslyUpdateClientDirectly(t.args),s=o(this.mapCallbacksByObjID[e]||[]);!(n=s()).done;)(0,n.value)(i,t.from)},r.dispatchListCmd=function(e,t){if(!this.listClients[e]){var r=new l({checkpoint:this.checkpoint,roomID:this.roomID,docID:this.docID,listID:e,ws:this.ws,actor:this.actor});this.listClients[e]=r}for(var n,i=this.listClients[e].dangerouslyUpdateClientDirectly(t.args),s=o(this.listCallbacksByObjID[e]||[]);!(n=s()).done;)(0,n.value)(i,t.from)},r.dispatchPresenceCmd=function(e){var t=this;if(e.room===this.roomID&&e.from!==this.actor){var r=this.presence(),n=e.key,i=(new Date).getTime()/1e3,s=e.expAt-i;if(!(s<0)){if(s<43200){this.expires[n]&&clearTimeout(this.expires[n]);var c=setTimeout((function(){var i=r.dangerouslyUpdateClientDirectly("presence:expire",{key:e.key});if(i)for(var s,c=o(null!==(a=t.presenceCallbacksByKey[n])&&void 0!==a?a:[]);!(s=c()).done;){var a;(0,s.value)(i,e.from)}}),1e3*s);this.expires[n]=c}var a=r.dangerouslyUpdateClientDirectly("presence:fwd",e);if(a)for(var h,u=o(null!==(l=this.presenceCallbacksByKey[n])&&void 0!==l?l:[]);!(h=u()).done;){var l;(0,h.value)(a,e.from)}}}},r.once=function(e){try{var t,r=this;return Promise.race([new Promise((function(e,t){return setTimeout((function(){return t("timeout")}),2e3)})),new Promise((function(n){t=r.ws.bind(e,(function(e){n(e)}))}))]).then((function(){t&&r.ws.unbind(e,t)}))}catch(e){return Promise.reject(e)}},r.reconnect=function(){try{var e=this;e.errorListener||(e.errorListener=e.ws.bind("error",(function(e){console.error("Room Service encountered a server-side error. If you see this, please let us know; this could be a bug.",e)})));var t=e.once("guest:authenticated");return e.ws.send("guest:authenticate",e.token),Promise.resolve(t).then((function(){var t=e.once("room:joined");return e.ws.send("room:join",e.roomID),Promise.resolve(t).then((function(){}))}))}catch(e){return Promise.reject(e)}},r.list=function(e){if(this.listClients[e])return this.listClients[e];this.checkpoint.lists[e]||(this.ws.send("doc:cmd",{args:["lcreate",this.docID,e],room:this.roomID}),this.checkpoint.lists[e]={afters:[],ids:[],values:[]});var t=new l({checkpoint:this.checkpoint,roomID:this.roomID,docID:this.docID,listID:e,ws:this.ws,actor:this.actor});return this.listClients[e]=t,t},r.map=function(e){if(this.mapClients[e])return this.mapClients[e];this.checkpoint.maps[e]||this.ws.send("doc:cmd",{args:["mcreate",this.docID,e],room:this.roomID});var t=new d({checkpoint:this.checkpoint.maps[e]||{},roomID:this.roomID,docID:this.docID,mapID:e,ws:this.ws});return this.mapClients[e]=t,t},r.presence=function(){if(this.InnerPresenceClient)return this.InnerPresenceClient;var e=new f({roomID:this.roomID,ws:this.ws,actor:this.actor,token:this.token});try{this.InnerPresenceClient=e}catch(e){throw new Error("Don't Freeze State. See more: https://err.sh/getroomservice/browser/dont-freeze")}return this.InnerPresenceClient},r.subscribe=function(e,t,r){if("string"==typeof t)return this.subscribePresence(e,t,r);var n,i=function(e,r){t(e,r)};return e instanceof d&&(this.mapCallbacksByObjID[n=e.id]=this.mapCallbacksByObjID[n]||[],this.mapCallbacksByObjID[n].push(i)),e instanceof l&&(this.listCallbacksByObjID[n=e.id]=this.listCallbacksByObjID[n]||[],this.listCallbacksByObjID[n].push(i)),[{objID:n,fn:i}]},r.subscribePresence=function(e,r,n){e||t(!1);var i=function(e,t){n&&n(e,t)};return this.presenceCallbacksByKey[r]=this.presenceCallbacksByKey[r]||[],this.presenceCallbacksByKey[r].push(i),[{objID:r,fn:i}]},r.unsubscribe=function(e){for(var t,r=o(e);!(t=r()).done;){var n=t.value;n.objID&&(this.mapCallbacksByObjID[n.objID]=b(this.mapCallbacksByObjID[n.objID],n.fn),this.listCallbacksByObjID[n.objID]=b(this.listCallbacksByObjID[n.objID],n.fn),this.presenceCallbacksByKey[n.objID]=b(this.presenceCallbacksByKey[n.objID],n.fn)),n.event&&this.ws.unbind(n.event,n.fn)}},n(e,[{key:"me",get:function(){return this.actor}}]),e}();function b(e,t){return e?e.filter((function(e){return e!==t})):[]}var I=function(){function e(e){this.roomClients={},this.auth=e.auth}return e.prototype.room=function(e){try{var t=this;if(t.roomClients[e])return Promise.resolve(t.roomClients[e]);var r=new WebSocket("wss://super.roomservice.dev/ws");return Promise.resolve(function(e,t,r,n,i){try{return Promise.resolve(function(e,t,r){try{var n=function(r){return i?r:Promise.resolve(fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({resources:[{object:"document",reference:"default",permission:"read_write",room:t},{object:"room",reference:t,permission:"join"}]})})).then((function(e){if(401===e.status)throw new Error("The Auth Webhook returned unauthorized.");if(200!==e.status)throw new Error("The Auth Webhook returned a status code other than 200.");return Promise.resolve(e.json()).then((function(e){var t=e.resources,r=e.token,n=e.user;if(!t||!r||!n){if("Unauthorized"===e.body)throw new Error("The Auth Webhook unexpectedly return unauthorized. You may be using an invalid API key.");throw new Error("The Auth Webhook has an incorrectly formatted JSON response.")}return{token:r,guestReference:n,docID:t.find((function(e){return"document"===e.object})).id,roomID:t.find((function(e){return"room"===e.object})).id}}))}))},i=!1,o=function(){if("function"==typeof e)return Promise.resolve(e(t)).then((function(e){if(!e.user)throw new Error("The auth function must return a 'user' key.");var t=e.resources.find((function(e){return"document"===e.object})).id,r=e.resources.find((function(e){return"room"===e.object})).id;return i=!0,{token:e.token,guestReference:e.user,docID:t,roomID:r}}))}();return Promise.resolve(o&&o.then?o.then(n):n(o))}catch(e){return Promise.reject(e)}}(r,n)).then((function(t){return Promise.resolve(function(e,t,r){try{return Promise.resolve(fetch("https://super.roomservice.dev/docs/"+r,{headers:{Authorization:"Bearer: "+t}})).then((function(e){return Promise.resolve(e.json())}))}catch(e){return Promise.reject(e)}}(0,t.token,t.docID)).then((function(r){var n=new y({conn:e,actor:t.guestReference,checkpoint:r.body,token:t.token,roomID:t.roomID});return Promise.resolve(n.reconnect()).then((function(){return n}))}))}))}catch(e){return Promise.reject(e)}}(r,0,t.auth,e)).then((function(r){return t.roomClients[e]=r,r}))}catch(e){return Promise.reject(e)}},e}();exports.RoomClient=y,exports.RoomService=I,exports.default=I; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=(e=require("tiny-invariant"))&&"object"==typeof e&&"default"in e?e.default:e,r=require("@roomservice/core");function n(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function i(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),e}function s(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function o(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return s(e,void 0);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?s(e,void 0):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(r=e[Symbol.iterator]()).next.bind(r)}var c=function(){function e(e){var t=this;this.callbacks={},this.lastTime=0,this.msgsThisMilisecond=0,this.conn=e,this.conn.onmessage=function(e){var r=JSON.parse(e.data);t.dispatch(r.type,r.body)}}var t=e.prototype;return t.timestamp=function(){var e=Date.now();return e===this.lastTime?this.msgsThisMilisecond++:(this.lastTime=e,this.msgsThisMilisecond=0),e+":"+this.msgsThisMilisecond},t.send=function(e,t){var r=this,n=this.timestamp();this.conn.readyState!==this.conn.CONNECTING?this.conn.send(JSON.stringify({type:e,ts:n,ver:0,body:t})):setTimeout((function(){r.send(e,t)}),100+100*Math.random())},t.bind=function(e,t){return this.callbacks[e]=this.callbacks[e]||[],this.callbacks[e].push(t),t},t.unbind=function(e,t){this.callbacks[e]=this.callbacks[e].filter((function(e){return e!==t}))},t.dispatch=function(e,t){var r=this.callbacks[e];if(r)for(var n=0;n<r.length;n++)r[n](t)},e}(),a=function(){function e(e){this.roomID=e.roomID,this.ws=e.ws,this.bus=e.bus,this.actor=e.actor,this.id=e.listID;var n=r.ListInterpreter.newList(e.docID,e.listID,e.actor),i=n.store;this.meta=n.meta,this.store=i,e.checkpoint.lists[e.listID]||t(!1),r.ListInterpreter.importFromRawCheckpoint(this.store,e.checkpoint,this.meta.listID)}var n=e.prototype;return n.sendCmd=function(e){this.ws.send("doc:cmd",{room:this.roomID,args:e}),this.bus.publish({args:e,from:this.actor})},n.clone=function(){return Object.assign(Object.create(Object.getPrototypeOf(this)),this)},n.dangerouslyUpdateClientDirectly=function(e){return r.ListInterpreter.validateCommand(this.meta,e),r.ListInterpreter.applyCommand(this.store,e),this.clone()},n.get=function(e){return r.ListInterpreter.get(this.store,e)},n.set=function(e,t){var n=r.ListInterpreter.runSet(this.store,this.meta,e,t);return this.sendCmd(n),this.clone()},n.delete=function(e){var t=r.ListInterpreter.runDelete(this.store,this.meta,e);return t?(this.sendCmd(t),this.clone()):this.clone()},n.insertAfter=function(e,t){return this.insertAt(e+1,t)},n.insertAt=function(e,t){var n=r.ListInterpreter.runInsertAt(this.store,this.meta,e,t);return this.sendCmd(n),this.clone()},n.push=function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];for(var i,s=r.ListInterpreter.runPush.apply(r.ListInterpreter,[this.store,this.meta].concat(t)),c=o(s);!(i=c()).done;){var a=i.value;this.sendCmd(a)}return this},n.map=function(e){return r.ListInterpreter.map(this.store,e)},n.toArray=function(){return r.ListInterpreter.toArray(this.store)},e}(),u=function(){function e(e){this.roomID=e.roomID,this.ws=e.ws,this.bus=e.bus,this.actor=e.actor;var t=r.MapInterpreter.newMap(e.docID,e.mapID),n=t.meta;this.store=t.store,this.meta=n,r.MapInterpreter.importFromRawCheckpoint(this.store,e.checkpoint,this.meta.mapID)}var t=e.prototype;return t.sendCmd=function(e){this.ws.send("doc:cmd",{room:this.roomID,args:e}),this.bus.publish({from:this.actor,args:e})},t.clone=function(){return Object.assign(Object.create(Object.getPrototypeOf(this)),this)},t.dangerouslyUpdateClientDirectly=function(e){return r.MapInterpreter.validateCommand(this.meta,e),r.MapInterpreter.applyCommand(this.store,e),this.clone()},t.get=function(e){return this.store[e]},t.set=function(e,t){var n=r.MapInterpreter.runSet(this.store,this.meta,e,t);return this.sendCmd(n),this.clone()},t.toObject=function(){for(var e,t={},r=o(this.keys);!(e=r()).done;){var n=e.value;t[n]=this.get(n)}return t},t.delete=function(e){var t=r.MapInterpreter.runDelete(this.store,this.meta,e);return this.sendCmd(t),this.clone()},i(e,[{key:"id",get:function(){return this.meta.mapID}},{key:"keys",get:function(){return Object.keys(this.store)}}]),e}(),h=function(){function e(e){var t=this;this.roomID=e.roomID,this.ws=e.ws,this.actor=e.actor,this.token=e.token,this.cache={},this.bus=e.bus,this.key=e.key,this.sendPres=function(e,t,r){void 0===r&&(r=!1);var n={},i=!0;return function(){var t=arguments,s=this,o=r&&i,c=function(){e.apply(s,t),n[t[0]]=null};o&&(i=!1,c()),n[arguments[0]]||(n[arguments[0]]=setTimeout(c,40))}}((function(e,r){t.ws.send("presence:cmd",r)}))}var t=e.prototype;return t.getAll=function(){try{var e=this;return Promise.resolve(function(e,t,r,n){try{return Promise.resolve(fetch("https://super.roomservice.dev/presence/"+r+"/"+encodeURIComponent(n),{headers:{Authorization:"Bearer: "+t}})).then((function(e){return Promise.resolve(e.json()).then((function(e){for(var t in e)if("string"==typeof e[t].value){var r=void 0;try{r=JSON.parse(e[t].value)}catch(e){}r&&(e[t].value=r)}return e}))}))}catch(e){return Promise.reject(e)}}(0,e.token,e.roomID,e.key)).then((function(t){return e.cache[e.key]=t,e.withoutExpiredAndSelf(e.key)}))}catch(e){return Promise.reject(e)}},t.withoutExpiredAndSelf=function(e){var t={};for(var r in this.cache[e]){var n=this.cache[e][r];r===this.actor&&delete this.cache[e][r],new Date>n.expAt?delete this.cache[e][r]:t[r]=n.value}return t},t.withoutActorOrExpired=function(e){var t={};for(var r in this.cache)for(var n in this.cache[r]){var i=this.cache[r][n];i&&(n===e&&this.cache[r][n]||new Date>i.expAt?delete this.cache[r][n]:t[n]=i.value)}return t},t.set=function(e,t){var r=t||60,n=Math.round((new Date).getTime()/1e3)+r;return this.bus.publish({key:this.key,value:e,expAt:n}),this.sendPres(this.key,{room:this.roomID,key:this.key,value:JSON.stringify(e),expAt:n}),this.cache[this.key]||(this.cache[this.key]={}),this.cache[this.key][this.actor]={value:e,expAt:new Date(1e3*n)},this.withoutExpiredAndSelf(this.key)},t.dangerouslyUpdateClientDirectly=function(e,t){if("room:rm_guest"===e)return this.withoutActorOrExpired(t.guest);if("presence:expire"===e)return this.withoutExpiredAndSelf(t.key);if(t.room!==this.roomID)return!1;if(t.from===this.actor)return!1;var r={expAt:new Date(1e3*t.expAt),value:JSON.parse(t.value)};return this.cache[t.key]||(this.cache[t.key]={}),this.cache[t.key][t.from]=r,this.withoutExpiredAndSelf(t.key)},i(e,[{key:"me",get:function(){return console.warn("presence.me() is deprecated and will be removed in a future version!"),this.actor}}]),e}(),l=function(){function e(){this.isPublishing=!1,this.subs=new Set}var t=e.prototype;return t.unsubscribe=function(e){this.subs.delete(e)},t.subscribe=function(e){return this.subs.add(e),e},t.publish=function(e){if(this.isPublishing)throw new Error("Infinite loop detected, see more: See more: https://err.sh/getroomservice/browser/infinite-loop");this.isPublishing=!0,this.subs.forEach((function(t){t(e)})),this.isPublishing=!1},e}(),f=["mcreate","mput","mputref","mdel"],m=["lcreate","lins","linsref","lput","lputref","ldel"],d=function(){function e(e){var t=this;this.listClients={},this.mapClients={},this.expires={},this.mapCallbacksByObjID={},this.listCallbacksByObjID={},this.presenceCallbacksByKey={},this.ws=new c(e.conn),this.token=e.token,this.roomID=e.roomID,this.docID=e.checkpoint.id,this.actor=e.actor,this.checkpoint=e.checkpoint,this.InnerPresenceClient=void 0;var n=r.vsReader(window.atob);this.ws.bind("doc:fwd",(function(e){if(e.room===t.roomID)if(!e.args||e.args.length<3)console.error("Unexpected command: ",e.args);else if(!n.isOlderVS(e.vs,t.checkpoint.vs)&&e.from!==t.actor){var r=[e.args[0],e.args[1],e.args[2]],i=r[0],s=r[2];r[1]===t.docID&&(f.includes(i)?t.dispatchMapCmd(s,e):m.includes(i)?t.dispatchListCmd(s,e):console.warn("Unhandled Room Service doc:fwd command: "+i+". Consider updating the Room Service client."))}})),this.ws.bind("presence:fwd",(function(e){t.dispatchPresenceCmd(e)})),this.ws.bind("room:rm_guest",(function(e){if(e.room===t.roomID)for(var r=t.presence("_____any").dangerouslyUpdateClientDirectly("room:rm_guest",e),n=0,i=Object.entries(t.presenceCallbacksByKey);n<i.length;n++)for(var s,c=o(i[n][1]);!(s=c()).done;)(0,s.value)(r,e.guest)}))}var n=e.prototype;return n.dispatchMapCmd=function(e,t){this.mapClients[e]||this.createMapLocally(e);for(var r,n=this.mapClients[e].dangerouslyUpdateClientDirectly(t.args),i=o(this.mapCallbacksByObjID[e]||[]);!(r=i()).done;)(0,r.value)(n.toObject(),t.from)},n.dispatchListCmd=function(e,t){this.listClients[e]||this.createListLocally(e);for(var r,n=this.listClients[e].dangerouslyUpdateClientDirectly(t.args),i=o(this.listCallbacksByObjID[e]||[]);!(r=i()).done;)(0,r.value)(n.toArray(),t.from)},n.dispatchPresenceCmd=function(e){var t=this;if(e.room===this.roomID&&e.from!==this.actor){var r=this.presence(e.key),n=e.key,i=(new Date).getTime()/1e3,s=e.expAt-i;if(!(s<0)){if(s<43200){this.expires[n]&&clearTimeout(this.expires[n]);var c=setTimeout((function(){var i=r.dangerouslyUpdateClientDirectly("presence:expire",{key:e.key});if(i)for(var s,c=o(null!==(a=t.presenceCallbacksByKey[n])&&void 0!==a?a:[]);!(s=c()).done;){var a;(0,s.value)(i,e.from)}}),1e3*s);this.expires[n]=c}var a=r.dangerouslyUpdateClientDirectly("presence:fwd",e);if(a)for(var u,h=o(null!==(l=this.presenceCallbacksByKey[n])&&void 0!==l?l:[]);!(u=h()).done;){var l;(0,u.value)(a,e.from)}}}},n.once=function(e){try{var t,r=this;return Promise.race([new Promise((function(e,t){return setTimeout((function(){return t("timeout")}),2e3)})),new Promise((function(n){t=r.ws.bind(e,(function(e){n(e)}))}))]).then((function(){t&&r.ws.unbind(e,t)}))}catch(e){return Promise.reject(e)}},n.reconnect=function(){try{var e=this;e.errorListener||(e.errorListener=e.ws.bind("error",(function(e){console.error("Room Service encountered a server-side error. If you see this, please let us know; this could be a bug.",e)})));var t=e.once("guest:authenticated");return e.ws.send("guest:authenticate",e.token),Promise.resolve(t).then((function(){var t=e.once("room:joined");return e.ws.send("room:join",e.roomID),Promise.resolve(t).then((function(){}))}))}catch(e){return Promise.reject(e)}},n.createListLocally=function(e){var t=this,r=new l;r.subscribe((function(r){for(var n,i=t.listClients[e],s=o(t.listCallbacksByObjID[e]||[]);!(n=s()).done;)(0,n.value)(i.toArray(),r.from)}));var n=new a({checkpoint:this.checkpoint,roomID:this.roomID,docID:this.docID,listID:e,ws:this.ws,actor:this.actor,bus:r});return this.listClients[e]=n,n},n.list=function(e){return this.listClients[e]?this.listClients[e]:(this.checkpoint.lists[e]||(this.ws.send("doc:cmd",{args:["lcreate",this.docID,e],room:this.roomID}),this.checkpoint.lists[e]={afters:[],ids:[],values:[]}),this.createListLocally(e))},n.createMapLocally=function(e){var t=this,r=new l;r.subscribe((function(r){for(var n,i=t.mapClients[e],s=o(t.mapCallbacksByObjID[e]||[]);!(n=s()).done;)(0,n.value)(i.toObject(),r.from)}));var n=new u({checkpoint:this.checkpoint,roomID:this.roomID,docID:this.docID,mapID:e,ws:this.ws,bus:r,actor:this.actor});return this.mapClients[e]=n,n},n.map=function(e){return this.mapClients[e]?this.mapClients[e]:(this.checkpoint.maps[e]||this.ws.send("doc:cmd",{args:["mcreate",this.docID,e],room:this.roomID}),this.createMapLocally(e))},n.presence=function(e){var t=this;if(this.InnerPresenceClient)return this.InnerPresenceClient;var r=new l;r.subscribe((function(e){t.dispatchPresenceCmd({key:e.key,value:e.value,expAt:e.expAt,from:t.actor,room:t.roomID})}));var n=new h({roomID:this.roomID,ws:this.ws,actor:this.actor,token:this.token,bus:r,key:e});try{this.InnerPresenceClient=n}catch(e){throw new Error("Don't Freeze State. See more: https://err.sh/getroomservice/browser/dont-freeze")}return this.InnerPresenceClient},n.subscribe=function(e,t,r){if("string"==typeof t)return this.subscribePresence(e,t,r);var n,i=function(e,r){t(e,r)};return e instanceof u&&(this.mapCallbacksByObjID[n=e.id]=this.mapCallbacksByObjID[n]||[],this.mapCallbacksByObjID[n].push(i)),e instanceof a&&(this.listCallbacksByObjID[n=e.id]=this.listCallbacksByObjID[n]||[],this.listCallbacksByObjID[n].push(i)),[{objID:n,fn:i}]},n.subscribePresence=function(e,r,n){e||t(!1);var i=function(e,t){n&&n(e,t)};return this.presenceCallbacksByKey[r]=this.presenceCallbacksByKey[r]||[],this.presenceCallbacksByKey[r].push(i),[{objID:r,fn:i}]},n.unsubscribe=function(e){for(var t,r=o(e);!(t=r()).done;){var n=t.value;n.objID&&(this.mapCallbacksByObjID[n.objID]=p(this.mapCallbacksByObjID[n.objID],n.fn),this.listCallbacksByObjID[n.objID]=p(this.listCallbacksByObjID[n.objID],n.fn),this.presenceCallbacksByKey[n.objID]=p(this.presenceCallbacksByKey[n.objID],n.fn)),n.event&&this.ws.unbind(n.event,n.fn)}},i(e,[{key:"me",get:function(){return this.actor}}]),e}();function p(e,t){return e?e.filter((function(e){return e!==t})):[]}var v=function(){function e(e){this.roomClients={},this.auth=e.auth,this.ctx=e.ctx||{}}return e.prototype.room=function(e){try{var t=this;if(t.roomClients[e])return Promise.resolve(t.roomClients[e]);var r=new WebSocket("wss://super.roomservice.dev/ws");return Promise.resolve(function(e){try{return Promise.resolve(function(e,t,r,n){try{var i=function(t){return s?t:Promise.resolve(fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({resources:[{object:"document",reference:n,permission:"read_write",room:r},{object:"room",reference:r,permission:"join"}]})})).then((function(e){if(401===e.status)throw new Error("The Auth Webhook returned unauthorized.");if(200!==e.status)throw new Error("The Auth Webhook returned a status code other than 200.");return Promise.resolve(e.json()).then((function(e){var t=e.resources,r=e.token,n=e.user;if(!t||!r||!n){if("Unauthorized"===e.body)throw new Error("The Auth Webhook unexpectedly return unauthorized. You may be using an invalid API key.");throw new Error("The Auth Webhook has an incorrectly formatted JSON response.")}return{token:r,guestReference:n,docID:t.find((function(e){return"document"===e.object})).id,roomID:t.find((function(e){return"room"===e.object})).id}}))}))},s=!1,o=function(){if("function"==typeof e)return Promise.resolve(e({room:r,ctx:t})).then((function(e){if(!e.user)throw new Error("The auth function must return a 'user' key.");var t=e.resources.find((function(e){return"document"===e.object})).id,r=e.resources.find((function(e){return"room"===e.object})).id;return s=!0,{token:e.token,guestReference:e.user,docID:t,roomID:r}}))}();return Promise.resolve(o&&o.then?o.then(i):i(o))}catch(e){return Promise.reject(e)}}(e.authStrategy,e.authCtx,e.room,e.document)).then((function(t){return Promise.resolve(function(e,t,r){try{return Promise.resolve(fetch(e+"/"+r,{headers:{Authorization:"Bearer: "+t}})).then((function(e){return Promise.resolve(e.json())}))}catch(e){return Promise.reject(e)}}(e.docsURL,t.token,t.docID)).then((function(r){var n=new d({conn:e.conn,actor:t.guestReference,checkpoint:r.body,token:t.token,roomID:t.roomID});return Promise.resolve(n.reconnect()).then((function(){return n}))}))}))}catch(e){return Promise.reject(e)}}({conn:r,docsURL:"https://super.roomservice.dev/docs",authStrategy:t.auth,authCtx:t.ctx,room:e,document:"default"})).then((function(r){return t.roomClients[e]=r,r}))}catch(e){return Promise.reject(e)}},e}();exports.RoomClient=d,exports.RoomService=v,exports.default=v; | ||
//# sourceMappingURL=browser.cjs.production.min.js.map |
import invariant from 'tiny-invariant'; | ||
import { ListInterpreter, MapInterpreter, vsReader } from '@roomservice/core'; | ||
@@ -145,3 +146,3 @@ // export const WS_URL = 'ws://localhost:3452'; | ||
var fetchSession = function fetchSession(strategy, room, document) { | ||
var fetchSession = function fetchSession(strategy, ctx, room, document) { | ||
try { | ||
@@ -211,3 +212,6 @@ var _temp3 = function _temp3(_result) { | ||
if (typeof strategy === 'function') { | ||
return Promise.resolve(strategy(room)).then(function (result) { | ||
return Promise.resolve(strategy({ | ||
room: room, | ||
ctx: ctx | ||
})).then(function (result) { | ||
if (!result.user) { | ||
@@ -284,249 +288,18 @@ throw new Error("The auth function must return a 'user' key."); | ||
function unescapeID(checkpoint, id) { | ||
if (id === 'root') return 'root'; | ||
var _id$split = id.split(':'), | ||
index = _id$split[0], | ||
a = _id$split[1]; | ||
return index + ':' + checkpoint.actors[parseInt(a)]; | ||
} | ||
/** | ||
* A Reverse Tree is one where the children point to the | ||
* parents, instead of the otherway around. | ||
* | ||
* We use a reverse tree because the "insert" operation | ||
* can be done in paralell. | ||
*/ | ||
var ReverseTree = /*#__PURE__*/function () { | ||
function ReverseTree(actor) { | ||
// The number of operations used by this tree | ||
this.count = 0; | ||
this.actor = actor; | ||
this.nodes = {}; | ||
this.log = []; | ||
} | ||
var _proto = ReverseTree.prototype; | ||
_proto["import"] = function _import(checkpoint, listID) { | ||
!checkpoint ? process.env.NODE_ENV !== "production" ? invariant(false) : invariant(false) : void 0; | ||
var list = checkpoint.lists[listID]; | ||
var afters = list.afters || []; | ||
var ids = list.ids || []; | ||
var values = list.values || []; // Rehydrate the cache | ||
for (var i = 0; i < afters.length; i++) { | ||
var node = { | ||
after: unescapeID(checkpoint, afters[i]), | ||
id: unescapeID(checkpoint, ids[i]), | ||
value: values[i] | ||
}; | ||
this.nodes[node.id] = node; | ||
this.log.push(node); | ||
} | ||
this.count = this.log.length; | ||
}; | ||
_proto.get = function get(itemID) { | ||
if (this.nodes[itemID]) { | ||
return this.nodes[itemID].value; | ||
} | ||
return undefined; | ||
}; | ||
_proto.insert = function insert(after, value, externalNewID) { | ||
!this.log ? process.env.NODE_ENV !== "production" ? invariant(false) : invariant(false) : void 0; | ||
var id = externalNewID; | ||
if (!id) { | ||
id = this.count + ":" + this.actor; | ||
} | ||
this.count++; | ||
var node = { | ||
after: after, | ||
value: value, | ||
id: id | ||
}; | ||
this.nodes[id] = node; | ||
this.log.push(node); | ||
return id; | ||
}; | ||
_proto.put = function put(itemID, value) { | ||
if (!!this.nodes[itemID]) { | ||
this.nodes[itemID].value = value; | ||
} | ||
}; | ||
_proto.has = function has(itemID) { | ||
return !!this.nodes[itemID]; | ||
}; | ||
_proto["delete"] = function _delete(itemID) { | ||
if (!this.nodes[itemID]) return; | ||
this.nodes[itemID].value = { | ||
t: '' | ||
}; | ||
}; | ||
_proto.toTree = function toTree() { | ||
var childrenById = new Map(); | ||
var valueById = new Map(); | ||
for (var _iterator = _createForOfIteratorHelperLoose(this.log), _step; !(_step = _iterator()).done;) { | ||
var _childrenById$get; | ||
var node = _step.value; | ||
if (!childrenById.has(node.after)) { | ||
childrenById.set(node.after, []); | ||
} | ||
(_childrenById$get = childrenById.get(node.after)) === null || _childrenById$get === void 0 ? void 0 : _childrenById$get.push(node.id); | ||
valueById.set(node.id, node.value); | ||
} | ||
childrenById.forEach(function (children) { | ||
// sort by logical timestamp descending so that latest inserts appear first | ||
children.sort(function (a, b) { | ||
var _a$split = a.split(':'), | ||
leftCount = _a$split[0], | ||
leftActor = _a$split[1]; | ||
var _b$split = b.split(':'), | ||
rightCount = _b$split[0], | ||
rightActor = _b$split[1]; | ||
if (leftCount === rightCount) { | ||
return leftActor.localeCompare(rightActor); | ||
} | ||
return parseInt(rightCount) - parseInt(leftCount); | ||
}); | ||
}); | ||
return { | ||
childrenById: childrenById, | ||
valueById: valueById | ||
}; | ||
}; | ||
_proto.lastID = function lastID() { | ||
if (this.log.length === 0) { | ||
return 'root'; | ||
} | ||
var root = this.toTree(); // Search the right side of the tree | ||
function right(t, node) { | ||
var children = t.childrenById.get(node); | ||
if (!children || children.length === 0) { | ||
return node; | ||
} | ||
return right(t, children[children.length - 1]); | ||
} | ||
return right(root, 'root'); | ||
}; | ||
_proto.preOrderTraverse = function preOrderTraverse() { | ||
// -- Convert the log into a regular tree | ||
var tree = this.toTree(); | ||
var seenNodes = new Set(); // -- Do a depth-first traversal to get the result | ||
function preOrder(t, node) { | ||
if (seenNodes.has(node)) { | ||
console.warn('RoomService list cycle detected. Consider updating @roomservice/browser.'); | ||
return []; | ||
} | ||
seenNodes.add(node); | ||
var result = []; | ||
var value = t.valueById.get(node); | ||
if (value) { | ||
if (typeof value === 'string') { | ||
result.push({ | ||
value: value, | ||
id: node | ||
}); | ||
} else if ('t' in value && value.t === '') ; else { | ||
throw new Error('Unimplemented'); | ||
} | ||
} | ||
var children = t.childrenById.get(node); | ||
if (!children || children.length === 0) { | ||
return result; | ||
} | ||
for (var _iterator2 = _createForOfIteratorHelperLoose(children), _step2; !(_step2 = _iterator2()).done;) { | ||
var child = _step2.value; | ||
result = result.concat(preOrder(t, child)); | ||
} | ||
return result; | ||
} | ||
return preOrder(tree, 'root'); | ||
}; | ||
_proto.toArray = function toArray() { | ||
return this.preOrderTraverse().map(function (idValue) { | ||
return idValue.value; | ||
}); | ||
}; | ||
_createClass(ReverseTree, [{ | ||
key: "length", | ||
get: function get() { | ||
return Object.keys(this.nodes).length; | ||
} | ||
}]); | ||
return ReverseTree; | ||
}(); | ||
function escape(value) { | ||
return JSON.stringify(value); | ||
} | ||
function unescape(value) { | ||
try { | ||
return JSON.parse(value); | ||
} catch (error) { | ||
return value; | ||
} | ||
} | ||
var InnerListClient = /*#__PURE__*/function () { | ||
function InnerListClient(props) { | ||
// Map indexes to item ids | ||
this.itemIDs = []; | ||
this.roomID = props.roomID; | ||
this.docID = props.docID; | ||
this.ws = props.ws; | ||
this.bus = props.bus; | ||
this.actor = props.actor; | ||
this.id = props.listID; | ||
this.ws = props.ws; | ||
this.rt = new ReverseTree(props.actor); | ||
!props.checkpoint.lists[props.listID] ? process.env.NODE_ENV !== "production" ? invariant(false, "Unknown listid '" + props.listID + "' in checkpoint.") : invariant(false) : void 0; | ||
this.rt["import"](props.checkpoint, props.listID); | ||
var list = props.checkpoint.lists[props.listID]; | ||
var ids = list.ids || []; | ||
for (var i = 0; i < ids.length; i++) { | ||
var val = props.checkpoint.lists[props.listID].values[i]; | ||
var _ListInterpreter$newL = ListInterpreter.newList(props.docID, props.listID, props.actor), | ||
meta = _ListInterpreter$newL.meta, | ||
store = _ListInterpreter$newL.store; | ||
if (typeof val === 'object' && val['t'] === '') { | ||
continue; // skip tombstones | ||
} | ||
this.itemIDs.push(unescapeID(props.checkpoint, ids[i])); | ||
} | ||
this.meta = meta; | ||
this.store = store; | ||
!props.checkpoint.lists[props.listID] ? process.env.NODE_ENV !== "production" ? invariant(false, "Unknown listid '" + props.listID + "' in checkpoint.") : invariant(false) : void 0; | ||
ListInterpreter.importFromRawCheckpoint(this.store, props.checkpoint, this.meta.listID); | ||
} | ||
@@ -541,2 +314,6 @@ | ||
}); | ||
this.bus.publish({ | ||
args: cmd, | ||
from: this.actor | ||
}); | ||
}; | ||
@@ -550,43 +327,4 @@ | ||
_proto.dangerouslyUpdateClientDirectly = function dangerouslyUpdateClientDirectly(cmd) { | ||
if (cmd.length < 3) { | ||
throw new Error('Unexpected command: ' + cmd); | ||
} | ||
var keyword = cmd[0]; | ||
var docID = cmd[1]; | ||
var id = cmd[2]; | ||
if (docID !== this.docID || id !== this.id) { | ||
throw new Error('Command unexpectedly routed to the wrong client'); | ||
} | ||
switch (keyword) { | ||
case 'lins': | ||
var insAfter = cmd[3]; | ||
var insItemID = cmd[4]; | ||
var insValue = cmd[5]; | ||
this.itemIDs.splice(this.itemIDs.findIndex(function (f) { | ||
return f === insAfter; | ||
}) + 1, 0, insItemID); | ||
this.rt.insert(insAfter, insValue, insItemID); | ||
break; | ||
case 'lput': | ||
var putItemID = cmd[3]; | ||
var putVal = cmd[4]; | ||
this.rt.put(putItemID, putVal); | ||
break; | ||
case 'ldel': | ||
var delItemID = cmd[3]; | ||
this.rt["delete"](delItemID); | ||
this.itemIDs.splice(this.itemIDs.findIndex(function (f) { | ||
return f === delItemID; | ||
}), 1); | ||
break; | ||
default: | ||
throw new Error('Unexpected command keyword: ' + keyword); | ||
} | ||
ListInterpreter.validateCommand(this.meta, cmd); | ||
ListInterpreter.applyCommand(this.store, cmd); | ||
return this.clone(); | ||
@@ -596,30 +334,9 @@ }; | ||
_proto.get = function get(index) { | ||
var itemID = this.itemIDs[index]; | ||
if (!itemID) return undefined; | ||
var val = this.rt.get(itemID); | ||
if (!val) return undefined; | ||
if (typeof val === 'object') { | ||
if (val.t === '') { | ||
return undefined; | ||
} | ||
throw new Error('Unimplemented references'); | ||
} | ||
return unescape(val); | ||
return ListInterpreter.get(this.store, index); | ||
}; | ||
_proto.set = function set(index, val) { | ||
var itemID = this.itemIDs[index]; | ||
var cmd = ListInterpreter.runSet(this.store, this.meta, index, val); // Remote | ||
if (!itemID) { | ||
throw new Error("Index '" + index + "' doesn't already exist. Try .push() or .insertAfter() instead."); | ||
} | ||
var escaped = escape(val); // Local | ||
this.rt.put(itemID, escaped); // Remote | ||
this.sendCmd(['lput', this.docID, this.id, itemID, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -629,18 +346,10 @@ }; | ||
_proto["delete"] = function _delete(index) { | ||
if (this.itemIDs.length === 0) { | ||
return this.clone(); | ||
} | ||
var cmd = ListInterpreter.runDelete(this.store, this.meta, index); | ||
var itemID = this.itemIDs[index]; | ||
if (!itemID) { | ||
console.warn('Unknown index: ', index, this.itemIDs); | ||
if (!cmd) { | ||
return this.clone(); | ||
} // Local | ||
} // Remote | ||
this.rt["delete"](itemID); | ||
this.itemIDs.splice(index, 1); // Remote | ||
this.sendCmd(['ldel', this.docID, this.id, itemID]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -654,41 +363,9 @@ }; | ||
_proto.insertAt = function insertAt(index, val) { | ||
if (index < 0) { | ||
throw 'negative indices unsupported'; | ||
} | ||
var cmd = ListInterpreter.runInsertAt(this.store, this.meta, index, val); // Remote | ||
var afterID; | ||
if (index == 0) { | ||
afterID = 'root'; | ||
} else { | ||
afterID = this.itemIDs[index - 1]; | ||
} | ||
if (!afterID) { | ||
throw new RangeError("List '" + this.id + "' has no index: '" + index + "'"); | ||
} | ||
var escaped = escape(val); // Local | ||
var itemID = this.rt.insert(afterID, escaped); | ||
this.itemIDs.splice(index, 0, itemID); // Remote | ||
this.sendCmd(['lins', this.docID, this.id, afterID, itemID, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
}; | ||
_proto.pushOne = function pushOne(val) { | ||
var lastID = this.rt.lastID(); | ||
var escaped = escape(val); // Local | ||
var itemID = this.rt.insert(lastID, escaped); | ||
this.itemIDs.push(itemID); // Remote | ||
this.sendCmd(['lins', this.docID, this.id, lastID, itemID, escaped]); | ||
return this.clone(); | ||
}; | ||
_proto.push = function push() { | ||
var self; | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
@@ -698,20 +375,18 @@ args[_key] = arguments[_key]; | ||
for (var _i = 0, _args = args; _i < _args.length; _i++) { | ||
var arg = _args[_i]; | ||
self = this.pushOne(arg); | ||
var cmds = ListInterpreter.runPush.apply(ListInterpreter, [this.store, this.meta].concat(args)); | ||
for (var _iterator = _createForOfIteratorHelperLoose(cmds), _step; !(_step = _iterator()).done;) { | ||
var cmd = _step.value; | ||
this.sendCmd(cmd); | ||
} | ||
return self; | ||
return this; | ||
}; | ||
_proto.map = function map(fn) { | ||
return this.rt.preOrderTraverse().map(function (idValue, i) { | ||
return fn(unescape(idValue.value), i, idValue.id); | ||
}); | ||
return ListInterpreter.map(this.store, fn); | ||
}; | ||
_proto.toArray = function toArray() { | ||
return this.rt.toArray().map(function (m) { | ||
return unescape(m); | ||
}); | ||
return ListInterpreter.toArray(this.store); | ||
}; | ||
@@ -725,14 +400,13 @@ | ||
this.roomID = props.roomID; | ||
this.docID = props.docID; | ||
this.id = props.mapID; | ||
this.ws = props.ws; | ||
this.store = {}; // import | ||
this.bus = props.bus; | ||
this.actor = props.actor; | ||
for (var k in props.checkpoint) { | ||
var val = props.checkpoint[k]; | ||
var _MapInterpreter$newMa = MapInterpreter.newMap(props.docID, props.mapID), | ||
store = _MapInterpreter$newMa.store, | ||
meta = _MapInterpreter$newMa.meta; | ||
if (typeof val === 'string') { | ||
this.store[k] = unescape(val); | ||
} | ||
} | ||
this.store = store; | ||
this.meta = meta; | ||
MapInterpreter.importFromRawCheckpoint(this.store, props.checkpoint, this.meta.mapID); | ||
} | ||
@@ -747,2 +421,6 @@ | ||
}); | ||
this.bus.publish({ | ||
from: this.actor, | ||
args: cmd | ||
}); | ||
}; | ||
@@ -755,40 +433,4 @@ | ||
_proto.dangerouslyUpdateClientDirectly = function dangerouslyUpdateClientDirectly(cmd) { | ||
if (cmd.length < 3) { | ||
throw new Error('Unexpected command: ' + cmd); | ||
} | ||
var keyword = cmd[0]; | ||
var docID = cmd[1]; | ||
var id = cmd[2]; | ||
if (docID !== this.docID || id !== this.id) { | ||
throw new Error('Command unexpectedly routed to the wrong client'); | ||
} | ||
switch (keyword) { | ||
case 'mput': | ||
if (cmd.length !== 5) { | ||
console.error('Malformed command ', cmd); | ||
break; | ||
} | ||
var putKey = cmd[3]; | ||
var putVal = cmd[4]; | ||
this.store[putKey] = unescape(putVal); | ||
break; | ||
case 'mdel': | ||
if (cmd.length !== 4) { | ||
console.error('Malformed command ', cmd); | ||
break; | ||
} | ||
var delKey = cmd[3]; | ||
delete this.store[delKey]; | ||
break; | ||
default: | ||
throw new Error('Unexpected command keyword: ' + keyword); | ||
} | ||
MapInterpreter.validateCommand(this.meta, cmd); | ||
MapInterpreter.applyCommand(this.store, cmd); | ||
return this.clone(); | ||
@@ -802,7 +444,5 @@ }; | ||
_proto.set = function set(key, value) { | ||
var escaped = escape(value); // Local | ||
var cmd = MapInterpreter.runSet(this.store, this.meta, key, value); // Remote | ||
this.store[key] = value; // Remote | ||
this.sendCmd(['mput', this.docID, this.id, key, escaped]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -823,6 +463,5 @@ }; | ||
_proto["delete"] = function _delete(key) { | ||
// local | ||
delete this.store[key]; // remote | ||
var cmd = MapInterpreter.runDelete(this.store, this.meta, key); // remote | ||
this.sendCmd(['mdel', this.docID, this.id, key]); | ||
this.sendCmd(cmd); | ||
return this.clone(); | ||
@@ -832,2 +471,7 @@ }; | ||
_createClass(InnerMapClient, [{ | ||
key: "id", | ||
get: function get() { | ||
return this.meta.mapID; | ||
} | ||
}, { | ||
key: "keys", | ||
@@ -884,2 +528,4 @@ get: function get() { | ||
this.cache = {}; | ||
this.bus = props.bus; | ||
this.key = props.key; | ||
@@ -900,9 +546,9 @@ var sendPres = function sendPres(_, args) { | ||
_proto.getAll = function getAll(key) { | ||
_proto.getAll = function getAll() { | ||
try { | ||
var _this3 = this; | ||
return Promise.resolve(fetchPresence(PRESENCE_URL, _this3.token, _this3.roomID, key)).then(function (val) { | ||
_this3.cache[key] = val; | ||
return _this3.withoutExpiredAndSelf(key); | ||
return Promise.resolve(fetchPresence(PRESENCE_URL, _this3.token, _this3.roomID, _this3.key)).then(function (val) { | ||
_this3.cache[_this3.key] = val; | ||
return _this3.withoutExpiredAndSelf(_this3.key); | ||
}); | ||
@@ -964,13 +610,17 @@ } catch (e) { | ||
* | ||
* @param key | ||
* @param value Any arbitrary object, string, boolean, or number. | ||
* @param exp (Optional) Expiration time in seconds | ||
*/ | ||
_proto.set = function set(key, value, exp) { | ||
_proto.set = function set(value, exp) { | ||
var addition = exp ? exp : 60; // Convert to unix + add seconds | ||
var expAt = Math.round(new Date().getTime() / 1000) + addition; | ||
this.sendPres(key, { | ||
this.bus.publish({ | ||
key: this.key, | ||
value: value, | ||
expAt: expAt | ||
}); | ||
this.sendPres(this.key, { | ||
room: this.roomID, | ||
key: key, | ||
key: this.key, | ||
value: JSON.stringify(value), | ||
@@ -980,11 +630,11 @@ expAt: expAt | ||
if (!this.cache[key]) { | ||
this.cache[key] = {}; | ||
if (!this.cache[this.key]) { | ||
this.cache[this.key] = {}; | ||
} | ||
this.cache[key][this.actor] = { | ||
this.cache[this.key][this.actor] = { | ||
value: value, | ||
expAt: new Date(expAt * 1000) | ||
}; | ||
return this.withoutExpiredAndSelf(key); | ||
return this.withoutExpiredAndSelf(this.key); | ||
}; | ||
@@ -1029,35 +679,50 @@ | ||
function base64toArrayBuffer(vs) { | ||
var binary = window.atob(vs); | ||
var len = binary.length; | ||
var bytes = new Uint8Array(len); | ||
var errNoInfiniteLoop = function errNoInfiniteLoop() { | ||
return new Error('Infinite loop detected, see more: See more: https://err.sh/getroomservice/browser/infinite-loop'); | ||
}; | ||
for (var i = 0; i < len; i++) { | ||
bytes[i] = binary.charCodeAt(i); | ||
// Local pubsub, so that if you call .set in one place | ||
// it will trigger a .subscribe elsewhere, without | ||
// needing to go through the websockets | ||
var LocalBus = /*#__PURE__*/function () { | ||
function LocalBus() { | ||
this.isPublishing = false; | ||
this.subs = new Set(); | ||
} | ||
return bytes.buffer; | ||
} | ||
function isOlderVS(older, newer) { | ||
if (!older) return true; | ||
if (!newer) return false; // These are ALWAYS 10 bytes | ||
var _proto = LocalBus.prototype; | ||
var olderArr = new Uint8Array(base64toArrayBuffer(older).slice(0, 9)); | ||
var newerArr = new Uint8Array(base64toArrayBuffer(newer).slice(0, 9)); | ||
_proto.unsubscribe = function unsubscribe(fn) { | ||
this.subs["delete"](fn); | ||
}; | ||
for (var i = 0; i < olderArr.byteLength; i++) { | ||
if (newerArr[i] > olderArr[i]) return true; | ||
if (newerArr[i] < olderArr[i]) return false; | ||
} | ||
_proto.subscribe = function subscribe(fn) { | ||
this.subs.add(fn); | ||
return fn; | ||
}; | ||
return false; | ||
} | ||
_proto.publish = function publish(msg) { | ||
// This is an infinite loop | ||
if (this.isPublishing) { | ||
throw errNoInfiniteLoop(); | ||
} | ||
var createRoom = function createRoom(conn, docsURL, authStrategy, room, document) { | ||
this.isPublishing = true; | ||
this.subs.forEach(function (fn) { | ||
fn(msg); | ||
}); | ||
this.isPublishing = false; | ||
}; | ||
return LocalBus; | ||
}(); | ||
var createRoom = function createRoom(params) { | ||
try { | ||
return Promise.resolve(fetchSession(authStrategy, room, document)).then(function (sess) { | ||
return Promise.resolve(fetchDocument(docsURL, sess.token, sess.docID)).then(function (_ref2) { | ||
return Promise.resolve(fetchSession(params.authStrategy, params.authCtx, params.room, params.document)).then(function (sess) { | ||
return Promise.resolve(fetchDocument(params.docsURL, sess.token, sess.docID)).then(function (_ref2) { | ||
var body = _ref2.body; | ||
var roomClient = new RoomClient({ | ||
conn: conn, | ||
conn: params.conn, | ||
actor: sess.guestReference, | ||
@@ -1097,2 +762,3 @@ checkpoint: body, | ||
this.InnerPresenceClient = undefined; | ||
var vs = vsReader(window.atob); | ||
this.ws.bind('doc:fwd', function (body) { | ||
@@ -1109,3 +775,3 @@ if (body.room !== _this.roomID) return; | ||
if (isOlderVS(body.vs, _this.checkpoint.vs)) return; // Ignore validated commands | ||
if (vs.isOlderVS(body.vs, _this.checkpoint.vs)) return; // Ignore validated commands | ||
@@ -1133,3 +799,3 @@ if (body.from === _this.actor) return; | ||
var client = _this.presence(); | ||
var client = _this.presence('_____any'); | ||
@@ -1154,10 +820,3 @@ var newClient = client.dangerouslyUpdateClientDirectly('room:rm_guest', body); | ||
if (!this.mapClients[objID]) { | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint.maps[objID] || {}, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
mapID: objID, | ||
ws: this.ws | ||
}); | ||
this.mapClients[objID] = m; | ||
this.createMapLocally(objID); | ||
} | ||
@@ -1170,3 +829,3 @@ | ||
var cb = _step2.value; | ||
cb(updatedClient, body.from); | ||
cb(updatedClient.toObject(), body.from); | ||
} | ||
@@ -1177,11 +836,3 @@ }; | ||
if (!this.listClients[objID]) { | ||
var l = new InnerListClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: objID, | ||
ws: this.ws, | ||
actor: this.actor | ||
}); | ||
this.listClients[objID] = l; | ||
this.createListLocally(objID); | ||
} | ||
@@ -1194,3 +845,3 @@ | ||
var cb = _step3.value; | ||
cb(updatedClient, body.from); | ||
cb(updatedClient.toArray(), body.from); | ||
} | ||
@@ -1204,3 +855,3 @@ }; | ||
if (body.from === this.actor) return; | ||
var client = this.presence(); | ||
var client = this.presence(body.key); | ||
var key = body.key; | ||
@@ -1299,2 +950,27 @@ var now = new Date().getTime() / 1000; | ||
_proto.createListLocally = function createListLocally(name) { | ||
var _this7 = this; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
var client = _this7.listClients[name]; | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(_this7.listCallbacksByObjID[name] || []), _step6; !(_step6 = _iterator6()).done;) { | ||
var cb = _step6.value; | ||
cb(client.toArray(), body.from); | ||
} | ||
}); | ||
var l = new InnerListClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: name, | ||
ws: this.ws, | ||
actor: this.actor, | ||
bus: bus | ||
}); | ||
this.listClients[name] = l; | ||
return l; | ||
}; | ||
_proto.list = function list(name) { | ||
@@ -1319,12 +995,28 @@ if (this.listClients[name]) { | ||
var l = new InnerListClient({ | ||
return this.createListLocally(name); | ||
}; | ||
_proto.createMapLocally = function createMapLocally(name) { | ||
var _this8 = this; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
var client = _this8.mapClients[name]; | ||
for (var _iterator7 = _createForOfIteratorHelperLoose(_this8.mapCallbacksByObjID[name] || []), _step7; !(_step7 = _iterator7()).done;) { | ||
var cb = _step7.value; | ||
cb(client.toObject(), body.from); | ||
} | ||
}); | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
listID: name, | ||
mapID: name, | ||
ws: this.ws, | ||
bus: bus, | ||
actor: this.actor | ||
}); | ||
this.listClients[name] = l; | ||
return l; | ||
this.mapClients[name] = m; | ||
return m; | ||
}; | ||
@@ -1345,14 +1037,8 @@ | ||
var m = new InnerMapClient({ | ||
checkpoint: this.checkpoint.maps[name] || {}, | ||
roomID: this.roomID, | ||
docID: this.docID, | ||
mapID: name, | ||
ws: this.ws | ||
}); | ||
this.mapClients[name] = m; | ||
return m; | ||
return this.createMapLocally(name); | ||
}; | ||
_proto.presence = function presence() { | ||
_proto.presence = function presence(key) { | ||
var _this9 = this; | ||
if (this.InnerPresenceClient) { | ||
@@ -1362,2 +1048,12 @@ return this.InnerPresenceClient; | ||
var bus = new LocalBus(); | ||
bus.subscribe(function (body) { | ||
_this9.dispatchPresenceCmd({ | ||
key: body.key, | ||
value: body.value, | ||
expAt: body.expAt, | ||
from: _this9.actor, | ||
room: _this9.roomID | ||
}); | ||
}); | ||
var p = new InnerPresenceClient({ | ||
@@ -1367,3 +1063,5 @@ roomID: this.roomID, | ||
actor: this.actor, | ||
token: this.token | ||
token: this.token, | ||
bus: bus, | ||
key: key | ||
}); | ||
@@ -1384,3 +1082,3 @@ | ||
return this.subscribePresence(obj, onChangeFnOrString, onChangeFn); | ||
} // create new closure so fns can be subscribed/unsubscribed multiple times | ||
} // create new closure so fns can be subscribed/unsubscribed multiple times | ||
@@ -1432,4 +1130,4 @@ | ||
_proto.unsubscribe = function unsubscribe(listeners) { | ||
for (var _iterator6 = _createForOfIteratorHelperLoose(listeners), _step6; !(_step6 = _iterator6()).done;) { | ||
var l = _step6.value; | ||
for (var _iterator8 = _createForOfIteratorHelperLoose(listeners), _step8; !(_step8 = _iterator8()).done;) { | ||
var l = _step8.value; | ||
@@ -1472,2 +1170,3 @@ if (l.objID) { | ||
this.auth = params.auth; | ||
this.ctx = params.ctx || {}; | ||
} | ||
@@ -1486,3 +1185,10 @@ | ||
var ws = new WebSocket(WS_URL); | ||
return Promise.resolve(createRoom(ws, DOCS_URL, _this2.auth, name, 'default')).then(function (client) { | ||
return Promise.resolve(createRoom({ | ||
conn: ws, | ||
docsURL: DOCS_URL, | ||
authStrategy: _this2.auth, | ||
authCtx: _this2.ctx, | ||
room: name, | ||
document: 'default' | ||
})).then(function (client) { | ||
_this2.roomClients[name] = client; | ||
@@ -1489,0 +1195,0 @@ return client; |
import SuperlumeWebSocket from './ws'; | ||
import { ObjectClient, DocumentCheckpoint } from './types'; | ||
export declare class InnerListClient<T extends any> implements ObjectClient { | ||
import { LocalBus } from './localbus'; | ||
export declare type ListObject = Array<any>; | ||
export declare class InnerListClient<T extends ListObject> implements ObjectClient { | ||
private roomID; | ||
private docID; | ||
private ws; | ||
private rt; | ||
private itemIDs; | ||
private bus; | ||
private actor; | ||
private store; | ||
private meta; | ||
id: string; | ||
@@ -17,2 +20,6 @@ constructor(props: { | ||
actor: string; | ||
bus: LocalBus<{ | ||
args: string[]; | ||
from: string; | ||
}>; | ||
}); | ||
@@ -22,11 +29,10 @@ private sendCmd; | ||
dangerouslyUpdateClientDirectly(cmd: string[]): InnerListClient<T>; | ||
get(index: number): T | undefined; | ||
set(index: number, val: T): InnerListClient<T>; | ||
delete(index: number): InnerListClient<T>; | ||
insertAfter(index: number, val: T): InnerListClient<T>; | ||
insertAt(index: number, val: T): InnerListClient<T>; | ||
private pushOne; | ||
push(...args: T[]): InnerListClient<T>; | ||
map<T extends any>(fn: (val: T, index: number, key: string) => T[]): T[]; | ||
get<K extends keyof T>(index: K): T | undefined; | ||
set<K extends keyof T>(index: K, val: T[K]): InnerListClient<T>; | ||
delete<K extends keyof T>(index: K): InnerListClient<T>; | ||
insertAfter<K extends keyof T>(index: K, val: T[K]): InnerListClient<T>; | ||
insertAt<K extends keyof T>(index: K, val: T[K]): InnerListClient<T>; | ||
push<K extends keyof T>(...args: Array<T[K]>): InnerListClient<T>; | ||
map<K extends keyof T>(fn: (val: T[K], index: number, key: string) => Array<T[K]>): Array<T[K]>; | ||
toArray(): T[]; | ||
} |
@@ -1,16 +0,28 @@ | ||
import { ObjectClient, MapCheckpoint } from './types'; | ||
import { ObjectClient } from './types'; | ||
import SuperlumeWebSocket from './ws'; | ||
export declare class InnerMapClient<T extends any> implements ObjectClient { | ||
import { LocalBus } from './localbus'; | ||
import { DocumentCheckpoint } from '@roomservice/core'; | ||
export declare type MapObject = { | ||
[key: string]: any; | ||
}; | ||
export declare class InnerMapClient<T extends MapObject> implements ObjectClient { | ||
private roomID; | ||
private docID; | ||
private ws; | ||
private meta; | ||
private store; | ||
id: string; | ||
private bus; | ||
private actor; | ||
constructor(props: { | ||
checkpoint: MapCheckpoint; | ||
checkpoint: DocumentCheckpoint; | ||
roomID: string; | ||
docID: string; | ||
mapID: string; | ||
actor: string; | ||
ws: SuperlumeWebSocket; | ||
bus: LocalBus<{ | ||
from: string; | ||
args: string[]; | ||
}>; | ||
}); | ||
get id(): string; | ||
private sendCmd; | ||
@@ -20,8 +32,8 @@ private clone; | ||
get keys(): string[]; | ||
get(key: string): T; | ||
set(key: string, value: T): InnerMapClient<T>; | ||
get<K extends keyof T>(key: K): T; | ||
set<K extends keyof T>(key: K, value: T[K]): InnerMapClient<T>; | ||
toObject(): { | ||
[key: string]: T; | ||
}; | ||
delete(key: string): InnerMapClient<T>; | ||
delete<K extends keyof T>(key: K): InnerMapClient<T>; | ||
} |
import SuperlumeWebSocket from './ws'; | ||
import { Prop } from './types'; | ||
import { WebSocketPresenceFwdMessage, WebSocketLeaveMessage } from './wsMessages'; | ||
import { LocalBus } from 'localbus'; | ||
export declare class InnerPresenceClient { | ||
@@ -9,9 +10,17 @@ private roomID; | ||
private token; | ||
private key; | ||
private cache; | ||
private sendPres; | ||
private bus; | ||
constructor(props: { | ||
roomID: string; | ||
key: string; | ||
ws: SuperlumeWebSocket; | ||
actor: string; | ||
token: string; | ||
bus: LocalBus<{ | ||
key: string; | ||
value: any; | ||
expAt: number; | ||
}>; | ||
}); | ||
@@ -22,3 +31,3 @@ /** | ||
*/ | ||
getAll<T extends any>(key: string): Promise<{ | ||
getAll<T extends any>(): Promise<{ | ||
[key: string]: T; | ||
@@ -31,7 +40,6 @@ }>; | ||
* | ||
* @param key | ||
* @param value Any arbitrary object, string, boolean, or number. | ||
* @param exp (Optional) Expiration time in seconds | ||
*/ | ||
set<T extends any>(key: string, value: T, exp?: number): { | ||
set<T extends any>(value: T, exp?: number): { | ||
[key: string]: T; | ||
@@ -38,0 +46,0 @@ }; |
@@ -18,2 +18,2 @@ import { Message, DocumentCheckpoint, PresenceCheckpoint, AuthStrategy } from './types'; | ||
} | ||
export declare function fetchSession(strategy: AuthStrategy, room: string, document: string): Promise<LocalSession>; | ||
export declare function fetchSession<T extends object>(strategy: AuthStrategy<T>, ctx: T, room: string, document: string): Promise<LocalSession>; |
import { WebSocketLikeConnection, DocumentCheckpoint, AuthStrategy, Prop } from './types'; | ||
import { InnerListClient } from './ListClient'; | ||
import { InnerMapClient } from './MapClient'; | ||
import { InnerListClient, ListObject } from './ListClient'; | ||
import { InnerMapClient, MapObject } from './MapClient'; | ||
import { InnerPresenceClient } from './PresenceClient'; | ||
@@ -12,5 +12,6 @@ import { WebSocketServerMessage } from './wsMessages'; | ||
declare type ListenerBundle = Array<Listener>; | ||
declare type InternalFunctions = 'dangerouslyUpdateClientDirectly'; | ||
declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; | ||
export declare type MapClient<T> = Omit<InnerMapClient<T>, 'dangerouslyUpdateClientDirectly' | 'id'>; | ||
export declare type ListClient<T> = Omit<InnerListClient<T>, 'dangerouslyUpdateClientDirectly' | 'id'>; | ||
export declare type MapClient<T extends MapObject> = Omit<InnerMapClient<T>, InternalFunctions | 'id'>; | ||
export declare type ListClient<T extends ListObject> = Omit<InnerListClient<T>, 'dangerouslyUpdateClientDirectly' | 'id'>; | ||
export declare type PresenceClient = Omit<InnerPresenceClient, 'dangerouslyUpdateClientDirectly'>; | ||
@@ -45,12 +46,14 @@ export declare class RoomClient { | ||
get me(): string; | ||
list<T extends any>(name: string): ListClient<T>; | ||
map<T extends any>(name: string): MapClient<T>; | ||
presence(): PresenceClient; | ||
private createListLocally; | ||
list<T extends ListObject>(name: string): ListClient<T>; | ||
private createMapLocally; | ||
map<T extends MapObject>(name: string): MapClient<T>; | ||
presence(key: string): PresenceClient; | ||
private mapCallbacksByObjID; | ||
private listCallbacksByObjID; | ||
private presenceCallbacksByKey; | ||
subscribe<T>(list: ListClient<T>, onChangeFn: (list: ListClient<T>) => any): ListenerBundle; | ||
subscribe<T>(list: ListClient<T>, onChangeFn: (list: ListClient<T>, from: string) => any): ListenerBundle; | ||
subscribe<T>(map: MapClient<T>, onChangeFn: (map: MapClient<T>) => {}): ListenerBundle; | ||
subscribe<T>(map: MapClient<T>, onChangeFn: (map: MapClient<T>, from: string) => any): ListenerBundle; | ||
subscribe<T extends ListObject>(list: ListClient<T>, onChangeFn: (list: T) => any): ListenerBundle; | ||
subscribe<T extends ListObject>(list: ListClient<T>, onChangeFn: (list: T, from: string) => any): ListenerBundle; | ||
subscribe<T extends MapObject>(map: MapClient<T>, onChangeFn: (map: T) => {}): ListenerBundle; | ||
subscribe<T extends MapObject>(map: MapClient<T>, onChangeFn: (map: T, from: string) => any): ListenerBundle; | ||
subscribe<T extends any>(presence: PresenceClient, key: string, onChangeFn: (obj: { | ||
@@ -62,3 +65,10 @@ [key: string]: T; | ||
} | ||
export declare function createRoom(conn: WebSocketLikeConnection, docsURL: string, authStrategy: AuthStrategy, room: string, document: string): Promise<RoomClient>; | ||
export declare function createRoom<T extends object>(params: { | ||
conn: WebSocketLikeConnection; | ||
docsURL: string; | ||
authStrategy: AuthStrategy<T>; | ||
authCtx: T; | ||
room: string; | ||
document: string; | ||
}): Promise<RoomClient>; | ||
export {}; |
import { RoomClient } from './RoomClient'; | ||
import { AuthStrategy } from 'types'; | ||
export interface RoomServiceParameters { | ||
auth: AuthStrategy; | ||
import { AuthFunction } from 'types'; | ||
interface SimpleAuthParams { | ||
auth: string; | ||
} | ||
export declare class RoomService { | ||
interface ComplexAuthParams<T extends object> { | ||
auth: AuthFunction<T>; | ||
ctx: T; | ||
} | ||
export declare type RoomServiceParameters<T extends object> = SimpleAuthParams | ComplexAuthParams<T>; | ||
export declare class RoomService<T extends object> { | ||
private auth; | ||
private ctx; | ||
private roomClients; | ||
constructor(params: RoomServiceParameters); | ||
constructor(params: RoomServiceParameters<T>); | ||
room(name: string): Promise<RoomClient>; | ||
} | ||
export {}; |
@@ -106,4 +106,7 @@ import ReverseTree from './ReverseTree'; | ||
} | ||
export declare type AuthFunction = (room: string) => Promise<AuthResponse>; | ||
export declare type AuthStrategy = string | AuthFunction; | ||
export declare type AuthFunction<T extends object> = (params: { | ||
room: string; | ||
ctx: T; | ||
}) => Promise<AuthResponse>; | ||
export declare type AuthStrategy<T extends object> = string | AuthFunction<T>; | ||
export {}; |
{ | ||
"version": "2.1.5", | ||
"version": "3.0.0-0", | ||
"license": "MIT", | ||
@@ -43,4 +43,5 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@roomservice/core": "0.2.0", | ||
"tiny-invariant": "^1.1.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4
278709
2
31
2462
3
+ Added@roomservice/core@0.2.0
+ Added@roomservice/core@0.2.0(transitive)