Skip to content

Commit

Permalink
Cache (#3)
Browse files Browse the repository at this point in the history
* - Replaced provider.destroyHandler with provider.destroy
- Emit connection closed information from peer
* Edited Readme with destroy handler changes
* incremented version to 2.0.0
added idb-keyval to project
* Added indexedDB support, protect data from unexpected provider destroys
* re-build distribution files
  • Loading branch information
deathg0d authored Sep 1, 2024
1 parent ce6c899 commit 3d783ed
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 47 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ new FireProvider({

#### Methods

- **destroyHandler**: Destroys the y-fire instance. You may want to destroy the y-fire instance when navigating out of the page to avoid the initialization of duplicate instances. Use `provider.destroyHandler();` to destroy the instance.
- **destroy**: Destroys the y-fire instance. You may want to destroy the y-fire instance when navigating out of the page to avoid the initialization of duplicate instances. Use `provider.destroy();` to destroy the instance.
- ~~**destroyHandler**: Destroys the y-fire instance. You may want to destroy the y-fire instance when navigating out of the page to avoid the initialization of duplicate instances. Use `provider.destroyHandler();` to destroy the instance.~~ (Replaced with **destroy**)

#### Events

Expand Down
6 changes: 4 additions & 2 deletions dist/provider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ export declare class FireProvider extends ObservableV2<any> {
onReady: () => void;
onDeleted: () => void;
onSaving: (status: boolean) => void;
private destroyHandler;
init: () => Promise<void>;
syncLocal: () => Promise<void>;
saveToLocal: () => Promise<void>;
deleteLocal: () => Promise<void>;
initiateHandler: () => void;
trackData: () => void;
trackMesh: () => void;
Expand All @@ -73,7 +75,7 @@ export declare class FireProvider extends ObservableV2<any> {
message: unknown;
data: Uint8Array | null;
}) => void;
saveToFirestore: () => void;
saveToFirestore: () => Promise<void>;
sendToFirestoreQueue: () => void;
sendCache: (from: string) => void;
sendToQueue: ({ from, update }: {
Expand Down
2 changes: 1 addition & 1 deletion dist/provider.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 47 additions & 9 deletions dist/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { collection } from "firebase/firestore";
import * as Y from "yjs";
import { ObservableV2 } from "lib0/observable";
import * as awarenessProtocol from "y-protocols/awareness";
import { get as getLocal, set as setLocal, del as delLocal } from "idb-keyval";
import { deleteInstance, initiateInstance, refreshPeers } from "./utils";
import { WebRtc } from "./webrtc";
import { createGraph } from "./graph";
Expand Down Expand Up @@ -61,13 +62,41 @@ export class FireProvider extends ObservableV2 {
this.kill(true); // destroy provider but keep the read-only stream alive
}
});
this.syncLocal = () => __awaiter(this, void 0, void 0, function* () {
try {
const local = yield getLocal(this.documentPath);
if (local)
Y.applyUpdate(this.doc, local, { key: "local-sync" });
}
catch (e) {
this.consoleHandler("get local error", e);
}
});
this.saveToLocal = () => __awaiter(this, void 0, void 0, function* () {
try {
const currentDoc = Y.encodeStateAsUpdate(this.doc);
setLocal(this.documentPath, currentDoc);
}
catch (e) {
this.consoleHandler("set local error", e);
}
});
this.deleteLocal = () => __awaiter(this, void 0, void 0, function* () {
try {
delLocal(this.documentPath);
}
catch (e) {
this.consoleHandler("del local error", e);
}
});
this.initiateHandler = () => {
this.consoleHandler("FireProvider initiated!");
this.awareness.on("update", this.awarenessUpdateHandler);
// We will track the mesh document on Firestore to
// keep track of selected peers
this.trackMesh();
this.doc.on("update", this.updateHandler);
this.syncLocal(); // if there's any data in indexedDb, get and apply
};
this.trackData = () => {
// Whenever there are changes to the firebase document
Expand Down Expand Up @@ -121,7 +150,7 @@ export class FireProvider extends ObservableV2 {
if (this.recreateTimeout)
clearTimeout(this.recreateTimeout);
this.recreateTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
this.consoleHandler("triggering reconnect");
this.consoleHandler("triggering reconnect", this.uid);
this.destroy();
this.init();
}), 200);
Expand Down Expand Up @@ -204,11 +233,12 @@ export class FireProvider extends ObservableV2 {
}
}
};
this.saveToFirestore = () => {
this.saveToFirestore = () => __awaiter(this, void 0, void 0, function* () {
try {
// current document to firestore
const ref = doc(this.db, this.documentPath);
setDoc(ref, { content: Bytes.fromUint8Array(Y.encodeStateAsUpdate(this.doc)) }, { merge: true });
yield setDoc(ref, { content: Bytes.fromUint8Array(Y.encodeStateAsUpdate(this.doc)) }, { merge: true });
this.deleteLocal(); // We have successfully saved to Firestore, empty indexedDb for now
}
catch (error) {
this.consoleHandler("error saving to firestore", error);
Expand All @@ -217,7 +247,7 @@ export class FireProvider extends ObservableV2 {
if (this.onSaving)
this.onSaving(false);
}
};
});
this.sendToFirestoreQueue = () => {
// if cache settles down, save document to firebase
if (this.firestoreTimeout)
Expand Down Expand Up @@ -276,14 +306,25 @@ export class FireProvider extends ObservableV2 {
}
};
this.updateHandler = (update, origin) => {
// Origin can be of the following types
// 1. User typed something -> origin: object
// 2. User loaded something from local store -> origin: object
// 3. User received update from a peer -> origin: string = peer uid
// 4. User received update from Firestore -> origin: string = 'origin:firebase/update'
// 5. Update triggered because user applied updates from the above sources -> origin: string = uid
if (origin !== this.uid) {
// Only allow updates typed by the user, and updates sent by peers
// Disallow repeat updates that were sent back by the peers
// We will not allow no. 5. to propagate any further
// Apply updates received from no. 1 to 4. -> triggers no. 5
Y.applyUpdate(this.doc, update, this.uid); // the third parameter sets the transaction-origin
// Convert no. 1 and 2 to uid, because we want these to eventually trigger 'save' to Firestore
// sendToQueue method will either:
// 1. save origin:uid to Firestore (and send to peers through WebRtc)
// 2. send updates from other origins through WebRtc only
this.sendToQueue({
from: typeof origin === "string" ? origin : this.uid,
update,
});
this.saveToLocal(); // save data to local indexedDb
}
};
this.awarenessUpdateHandler = ({ added, updated, removed, }, origin) => {
Expand Down Expand Up @@ -350,8 +391,5 @@ export class FireProvider extends ObservableV2 {
this.awareness = new awarenessProtocol.Awareness(this.doc);
// Initialize the provider
const init = this.init();
this.destroyHandler = () => {
this.destroy();
};
}
}
11 changes: 11 additions & 0 deletions dist/webrtc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,21 @@ export declare class WebRtc extends ObservableV2<any> {
peerKey: CryptoKey;
connection: string;
clock: string | number | NodeJS.Timeout;
idleThreshold: number;
constructor({ firebaseApp, ydoc, awareness, instanceConnection, documentPath, uid, peerUid, isCaller, }: Parameters);
initPeer: () => void;
startInitClock: () => void;
createKey: () => Promise<void>;
createPeer: (config: {
initiator: boolean;
config: {
iceServers: {
urls: string;
}[];
};
trickle: boolean;
channelName?: string;
}) => void;
callPeer: () => void;
replyPeer: () => void;
handshake: () => void;
Expand Down
2 changes: 1 addition & 1 deletion dist/webrtc.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 11 additions & 8 deletions dist/webrtc.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class WebRtc extends ObservableV2 {
],
};
this.connection = "connecting";
this.idleThreshold = 20000;
this.initPeer = () => {
this.createKey();
if (this.isCaller) {
Expand All @@ -40,7 +41,7 @@ export class WebRtc extends ObservableV2 {
this.startInitClock = () => {
/**
* We will track how long it takes to connect to this peer
* if it takes longer than 30s, we assume that we are
* if it takes longer than idleThreshold, we assume that we are
* connected to a zombie peer. Thus we will attempt to
* kill the zombie instance
*/
Expand All @@ -49,19 +50,21 @@ export class WebRtc extends ObservableV2 {
this.clock = setTimeout(() => {
if (this.connection !== "connected") {
killZombie(this.db, this.documentPath, this.uid, this.peerUid);
if (this.peer)
this.peer.destroy();
this.handleOnClose();
}
}, 30 * 1000);
}, this.idleThreshold);
};
this.createKey = () => __awaiter(this, void 0, void 0, function* () {
this.peerKey = yield generateKey(this.isCaller ? this.uid : this.peerUid, this.isCaller ? this.peerUid : this.uid);
// this.consoleHandler("key", this.peerKey);
if (!this.peerKey)
this.destroy();
});
this.createPeer = (config) => {
this.peer = new SimplePeer(config);
};
this.callPeer = () => {
this.peer = new SimplePeer({
this.createPeer({
initiator: true,
config: this.ice,
trickle: false,
Expand All @@ -77,15 +80,15 @@ export class WebRtc extends ObservableV2 {
yield setDoc(callRef, { signal });
setTimeout(() => {
deleteDoc(callRef);
}, 30 * 1000); // delete call after 30 seconds, if handshake hasn't deleted it yet
}, this.idleThreshold); // delete call after defined miliseconds, if handshake hasn't deleted it yet
}
catch (error) {
this.errorHandler(error);
}
}));
};
this.replyPeer = () => {
this.peer = new SimplePeer({
this.createPeer({
initiator: false,
config: this.ice,
trickle: false,
Expand All @@ -100,7 +103,7 @@ export class WebRtc extends ObservableV2 {
yield setDoc(answerRef, { signal });
setTimeout(() => {
deleteDoc(answerRef);
}, 30 * 1000); // delete call after 30 seconds, if handshake hasn't deleted it yet
}, this.idleThreshold); // delete call after defined miliseconds, if handshake hasn't deleted it yet
}
catch (error) {
this.errorHandler(error);
Expand Down
Loading

0 comments on commit 3d783ed

Please sign in to comment.