@roomservice/browser
Advanced tools
Comparing version 0.0.12 to 0.0.13
@@ -11,11 +11,21 @@ import Automerge from "automerge"; | ||
private _socketURL; | ||
private _onUpdateCallback?; | ||
private _onConnectCallback?; | ||
private _onDisconnectCallback?; | ||
private _onUpdateSocketCallback?; | ||
private _onConnectSocketCallback?; | ||
private _onDisconnectSocketCallback?; | ||
private _saveOffline; | ||
constructor(authorizationUrl: string, reference: string, state?: T); | ||
/** | ||
* Manually attempt to restore the state from offline storage. | ||
*/ | ||
restore(): Promise<T>; | ||
/** | ||
* Attempts to go online. | ||
*/ | ||
connect(): Promise<{ | ||
state: T; | ||
state: T | Automerge.FreezeObject<T>; | ||
reference: string; | ||
}>; | ||
/** | ||
* Manually goes offline | ||
*/ | ||
disconnect(): void; | ||
@@ -22,0 +32,0 @@ onUpdate(callback: (state: Readonly<T>) => any): void; |
@@ -58,4 +58,12 @@ "use strict"; | ||
} | ||
/** | ||
* Manually attempt to restore the state from offline storage. | ||
*/ | ||
async restore() { | ||
return this.syncOfflineCache(); | ||
} | ||
/** | ||
* Attempts to go online. | ||
*/ | ||
async connect() { | ||
await this.syncOfflineCache(); | ||
const { room, session } = await authorize_1.default(this._authorizationUrl, this._reference); | ||
@@ -72,3 +80,3 @@ this._roomId = room.id; | ||
}); | ||
this._automergeConn.open(); | ||
this._automergeConn.open(); // must be called immediately after socket def | ||
/** | ||
@@ -82,24 +90,21 @@ * Errors | ||
/** | ||
* It's possible someone has created their callbacks BEFORE | ||
* we've actually connected. In this case, we'll just | ||
* attach them now. | ||
* We don't require these to be defined before hand since they're | ||
* optional | ||
*/ | ||
if (this._onUpdateCallback) { | ||
socket_1.default.on(this._socket, "sync_room_state", this._onUpdateCallback); | ||
if (this._onUpdateSocketCallback) { | ||
socket_1.default.on(this._socket, "sync_room_state", this._onUpdateSocketCallback); | ||
} | ||
if (this._onConnectCallback) { | ||
socket_1.default.on(this._socket, "connect", this._onConnectCallback); | ||
if (this._onConnectSocketCallback) { | ||
socket_1.default.on(this._socket, "connect", this._onConnectSocketCallback); | ||
} | ||
if (this._onDisconnectCallback) { | ||
socket_1.default.on(this._socket, "disconnect", this._onDisconnectCallback); | ||
if (this._onDisconnectSocketCallback) { | ||
socket_1.default.on(this._socket, "disconnect", this._onDisconnectSocketCallback); | ||
} | ||
/** | ||
* It's also possible someone's been working offline before we've | ||
* actually connected to the client. So we should push up their | ||
* changes. | ||
*/ | ||
this.syncOfflineCache(); | ||
// Merge RoomService's online cache with what we have locally | ||
let state; | ||
try { | ||
state = automerge_1.default.load(room.state); | ||
const local = await this.syncOfflineCache(); | ||
state = automerge_1.merge(local, state); | ||
this._docs.setDoc("default", state); | ||
} | ||
@@ -115,2 +120,5 @@ catch (err) { | ||
} | ||
/** | ||
* Manually goes offline | ||
*/ | ||
disconnect() { | ||
@@ -120,5 +128,6 @@ if (this._socket) { | ||
} | ||
this._socket = undefined; | ||
} | ||
onUpdate(callback) { | ||
invariant_1.default(!this._onUpdateCallback, "It looks like you've called onUpdate multiple times. Since this can cause quite severe performance issues if used incorrectly, we're not currently supporting this behavior. If you've got a use-case we haven't thought of, file a github issue and we may change this."); | ||
invariant_1.default(!this._onUpdateSocketCallback, "It looks like you've called onUpdate multiple times. Since this can cause quite severe performance issues if used incorrectly, we're not currently supporting this behavior. If you've got a use-case we haven't thought of, file a github issue and we may change this."); | ||
const socketCallback = (data) => { | ||
@@ -138,2 +147,9 @@ const { meta, payload } = JSON.parse(data); | ||
const newDoc = this._automergeConn.receiveMsg(payload.msg); | ||
// Automerge, in it's infinite wisdom, will just return undefined | ||
// if a message is corrupted in some way that it doesn't like. | ||
// In these cases, we shouldn't actually save it offline otherwise | ||
// we'd create a hard-to-fix corruption. | ||
if (!newDoc) { | ||
throw new Error(`Response from RoomService API seems corrupted, aborting. Response: ${data}`); | ||
} | ||
this._saveOffline("default", newDoc); | ||
@@ -144,3 +160,3 @@ callback(newDoc); | ||
if (!this._socket) { | ||
this._onUpdateCallback = socketCallback; | ||
this._onUpdateSocketCallback = socketCallback; | ||
return; | ||
@@ -153,3 +169,3 @@ } | ||
if (!this._socket) { | ||
this._onConnectCallback = callback; | ||
this._onConnectSocketCallback = callback; | ||
return; | ||
@@ -162,3 +178,3 @@ } | ||
if (!this._socket) { | ||
this._onDisconnectCallback = callback; | ||
this._onDisconnectSocketCallback = callback; | ||
return; | ||
@@ -173,6 +189,17 @@ } | ||
const data = await offline_1.default.get(this._reference, "default"); | ||
if (data) { | ||
const roomDoc = automerge_1.load(data); | ||
this._docs.setDoc("default", roomDoc); | ||
if (!data) { | ||
return this._docs.getDoc("default"); | ||
} | ||
const offlineDoc = automerge_1.load(data); | ||
const inMemDoc = this._docs.getDoc("default"); | ||
// Merge the offline doc with the current in-memory doc | ||
let newDoc; | ||
if (inMemDoc) { | ||
newDoc = automerge_1.merge(inMemDoc, offlineDoc); | ||
} | ||
else { | ||
newDoc = offlineDoc; | ||
} | ||
this._docs.setDoc("default", newDoc); | ||
return newDoc; | ||
} | ||
@@ -179,0 +206,0 @@ publishState(callback) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
const automerge_1 = require("automerge"); | ||
const nock_1 = tslib_1.__importDefault(require("nock")); | ||
const client_1 = tslib_1.__importDefault(require("./client")); | ||
const offline_1 = tslib_1.__importDefault(require("./offline")); | ||
const socket_1 = tslib_1.__importDefault(require("./socket")); | ||
const URL = "https://coolsite.com"; | ||
jest.mock("idb-keyval"); | ||
function mockAuthEndpoint() { | ||
function mockAuthEndpoint(stateStr) { | ||
return nock_1.default(URL) | ||
@@ -16,3 +18,3 @@ .post("/api/roomservice") | ||
reference: "my-room", | ||
state: "{}" | ||
state: stateStr || "{}" | ||
}, | ||
@@ -72,3 +74,59 @@ session: { | ||
}); | ||
test("room.restore() attempts to restore from offline", async () => { | ||
const client = new client_1.default(URL + "/api/roomservice"); | ||
const room = client.room("my-room"); | ||
jest.spyOn(offline_1.default, "get").mockImplementation(async (ref, doc) => { | ||
return automerge_1.save(automerge_1.from({ name: "offlinedoc" })); | ||
}); | ||
const doc = await room.restore(); | ||
expect(doc).toEqual({ name: "offlinedoc" }); | ||
}); | ||
test("room.connect() will merge online data with offline data", async () => { | ||
const client = new client_1.default(URL + "/api/roomservice"); | ||
const room = client.room("my-room"); | ||
// setup offline | ||
jest.spyOn(offline_1.default, "get").mockImplementation(async (ref, doc) => { | ||
return automerge_1.save(automerge_1.from({ offline: "offline" })); | ||
}); | ||
// setup online | ||
mockAuthEndpoint(automerge_1.save(automerge_1.from({ online: "online" }))); | ||
const { state } = await room.connect(); | ||
expect(state).toEqual({ | ||
offline: "offline", | ||
online: "online" | ||
}); | ||
}); | ||
test("room.onUpdate callback tries to save the document to offline", async (done) => { | ||
mockAuthEndpoint(); | ||
const client = new client_1.default(URL + "/api/roomservice"); | ||
const room = client.room("my-room"); | ||
const cb = jest.fn(); | ||
room.onUpdate(cb); | ||
// @ts-ignore private | ||
const onUpdateSocket = room._onUpdateSocketCallback; | ||
expect(onUpdateSocket).toBeTruthy(); | ||
await room.connect(); | ||
// @ts-ignore private; we'd normally get this from the auth endpoint | ||
room._roomId = "my-room-id"; | ||
const setOffline = jest.spyOn(offline_1.default, "set"); | ||
onUpdateSocket(JSON.stringify({ | ||
meta: { | ||
roomId: "my-room-id" | ||
}, | ||
payload: { | ||
msg: { | ||
clock: new Map(), | ||
docId: "default" | ||
} | ||
} | ||
})); | ||
// Sanity check that our onUpdate callback was called | ||
expect(cb.mock.calls.length).toBe(1); | ||
// We wait here because saving offline is debounced. | ||
setTimeout(() => { | ||
expect(setOffline.mock.calls.length).toBeGreaterThan(1); | ||
done(); | ||
}, 160); // Debounce time | ||
}); | ||
}); | ||
//# sourceMappingURL=client.test.js.map |
{ | ||
"name": "@roomservice/browser", | ||
"version": "0.0.12", | ||
"version": "0.0.13", | ||
"main": "dist/index", | ||
@@ -5,0 +5,0 @@ "types": "dist/index", |
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
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
38098
561