@instantdb/core
Version:
Instant's core local abstraction
131 lines • 4.29 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
// PersistedObjects save data outside of memory.
//
// When we load a persisted object, it's possible we call `set`
// before we finish loading. To address we handle set in two ways:
//
// 1. Before load
// We simply update currentValue in memory
//
// 2. After load
// We update currentValue in memory and in storage
//
// Each PersistedObject provides it's own `onMerge`
// function to handle the merge of data from storage and memory
// on load
export class PersistedObject {
constructor(persister, key, defaultValue, onMerge, toJSON = (x) => {
return JSON.stringify(x);
}, fromJSON = (x) => {
return JSON.parse(x);
}, saveThrottleMs = 100) {
this._subs = [];
this._persister = persister;
this._key = key;
this._onMerge = onMerge;
this._loadedCbs = [];
this._isLoading = true;
this.currentValue = defaultValue;
this.toJSON = toJSON;
this.fromJSON = fromJSON;
this._saveThrottleMs = saveThrottleMs;
this._pendingSaveCbs = [];
this._version = 0;
this._load();
}
_load() {
return __awaiter(this, void 0, void 0, function* () {
const fromStorage = this.fromJSON(yield this._persister.getItem(this._key));
this._isLoading = false;
this._onMerge(fromStorage, this.currentValue);
for (const cb of this._loadedCbs) {
cb();
}
});
}
waitForLoaded() {
return __awaiter(this, void 0, void 0, function* () {
if (!this._isLoading) {
return;
}
const loadedPromise = new Promise((resolve) => {
this._loadedCbs.push(resolve);
});
yield loadedPromise;
});
}
isLoading() {
return this._isLoading;
}
version() {
return this._version;
}
waitForSync() {
return __awaiter(this, void 0, void 0, function* () {
if (!this._nextSave) {
return;
}
const syncedPromise = new Promise((resolve) => {
this._pendingSaveCbs.push(resolve);
});
yield syncedPromise;
});
}
_writeToStorage() {
this._persister.setItem(this._key, this.toJSON(this.currentValue));
for (const cb of this._pendingSaveCbs) {
cb();
}
this._pendingSaveCbs.length = 0;
}
flush() {
return __awaiter(this, void 0, void 0, function* () {
if (!this._nextSave) {
return;
}
clearTimeout(this._nextSave);
this._writeToStorage();
});
}
_enqueuePersist(cb) {
if (this._nextSave) {
if (cb) {
this._pendingSaveCbs.push(cb);
}
return;
}
this._nextSave = setTimeout(() => {
this._nextSave = null;
this._writeToStorage();
}, this._saveThrottleMs);
}
set(f, cb) {
this._version++;
this.currentValue = f(this.currentValue);
if (this._isLoading) {
this._loadedCbs.push(() => this._enqueuePersist(cb));
}
else {
this._enqueuePersist(cb);
}
for (const sub of this._subs) {
sub(this.currentValue);
}
}
subscribe(cb) {
this._subs.push(cb);
cb(this.currentValue);
return () => {
this._subs = this._subs.filter((x) => x !== cb);
};
}
}
//# sourceMappingURL=PersistedObject.js.map