apical-store
Advanced tools
Comparing version 0.0.81 to 0.0.82
{ | ||
"name": "apical-store", | ||
"version": "0.0.81", | ||
"version": "0.0.82", | ||
"description": "reactive-syncable-datastore", | ||
@@ -5,0 +5,0 @@ "main": "dist/bundle.js", |
@@ -18,7 +18,9 @@ import { ObservableMeta } from "./meta"; | ||
export function findGrandParent<T extends object>( | ||
observable: ObservableMeta<T> | ||
): ObservableMeta<T> { | ||
if (observable.parent) return findGrandParent(observable.parent); | ||
else return observable; | ||
export function findGrandParent<T extends object>(observable: ObservableMeta<T>, visited = new Set()): ObservableMeta<T> { | ||
if (visited.has(observable)) { | ||
throw new Error("Circular reference detected in observable structure"); | ||
} | ||
visited.add(observable); | ||
if (observable.parent) return findGrandParent(observable.parent, visited); | ||
else return observable; | ||
} | ||
@@ -25,0 +27,0 @@ |
import { Persistence } from "./type"; | ||
export type deferredArray = { ts: number; data: string }[]; | ||
export type deferredArray = { ts: number; id: string }[]; | ||
export interface Dump { | ||
data: [string, string][]; | ||
metadata: { | ||
version: number; | ||
deferred: deferredArray; | ||
}; | ||
} | ||
export interface LocalPersistence extends Persistence { | ||
getAll(): Promise<string[]> | ||
getAll(): Promise<string[]>; | ||
getOne(id: string): Promise<string>; | ||
putVersion(number: number): Promise<void>; | ||
getDeferred(): Promise<deferredArray>; | ||
putDeferred(arr: deferredArray): Promise<void>; | ||
dump(): Promise<Dump>; | ||
} | ||
@@ -21,3 +31,3 @@ | ||
constructor({name}:{name: string}) { | ||
constructor({ name }: { name: string }) { | ||
const request = indexedDB.open(name); | ||
@@ -104,6 +114,12 @@ request.onupgradeneeded = function (event) { | ||
} | ||
return rows | ||
return rows; | ||
}); | ||
} | ||
getOne(id: string): Promise<string> { | ||
return this.store("readonly", (store) => | ||
this.pr(store.get(id) as IDBRequest<string>) | ||
); | ||
} | ||
async getVersion() { | ||
@@ -158,2 +174,28 @@ return Number((await this.getMetadata("version")) || 0); | ||
} | ||
dump() { | ||
return this.store("readonly", async (store) => { | ||
let data: [string, string][] = []; | ||
if (store.getAll && store.getAllKeys) { | ||
const keys: string[] = await this.pr( | ||
store.getAllKeys() as IDBRequest<string[]> | ||
); | ||
const values: string[] = await this.pr( | ||
store.getAll() as IDBRequest<string[]> | ||
); | ||
data = keys.map((key, index) => [key, values[index]]); | ||
} else { | ||
await this.eachCursor(store, (cursor) => { | ||
data.push([cursor.key as string, cursor.value as string]); | ||
}); | ||
} | ||
return { | ||
data, | ||
metadata: { | ||
version: await this.getVersion(), | ||
deferred: await this.getDeferred(), | ||
}, | ||
}; | ||
}); | ||
} | ||
} |
import { Change, Observable } from "./observable"; | ||
import { deferredArray, LocalPersistence } from "./persistence/local"; | ||
import { deferredArray, Dump, LocalPersistence } from "./persistence/local"; | ||
import { debounce } from "./debounce"; | ||
@@ -142,3 +142,3 @@ import { Document } from "./model"; | ||
ts: Date.now(), | ||
data: serializedLine, | ||
id: item.id, | ||
}); | ||
@@ -170,6 +170,8 @@ } | ||
*/ | ||
await this.$$localPersistence.putDeferred( | ||
deferredArray.concat(...toDeffer) | ||
); | ||
this.deferredPresent = true; | ||
if (this.$$remotePersistence) { | ||
await this.$$localPersistence.putDeferred( | ||
deferredArray.concat(...toDeffer) | ||
); | ||
this.deferredPresent = true; | ||
} | ||
this.onSyncEnd(); | ||
@@ -272,4 +274,3 @@ } | ||
deferredArray = deferredArray.filter((x) => { | ||
let item = this.$$deserialize(x.data); | ||
const conflict = remoteUpdates.rows.findIndex((y) => y.id === item.id); | ||
const conflict = remoteUpdates.rows.findIndex((y) => y.id === x.id); | ||
// take row-specific version if available, otherwise rely on latest version | ||
@@ -303,5 +304,4 @@ const comparison = Number( | ||
const updatedRows = new Map(); | ||
for (const local of deferredArray) { | ||
let item = this.$$deserialize(local.data); | ||
updatedRows.set(item.id, local.data); | ||
for (const d of deferredArray) { | ||
updatedRows.set(d.id, await this.$$localPersistence.getOne(d.id)); | ||
// latest deferred write wins since it would overwrite the previous one | ||
@@ -364,2 +364,24 @@ } | ||
async backup() { | ||
if (!this.$$localPersistence) { | ||
throw new Error("Local persistence not available"); | ||
} | ||
return JSON.stringify(await this.$$localPersistence.dump()); | ||
} | ||
async restoreBackup(input: string) { | ||
const dump = JSON.parse(input) as Dump; | ||
if (!this.$$localPersistence) { | ||
throw new Error("Local persistence not available"); | ||
} | ||
await this.$$localPersistence.put(dump.data); | ||
await this.$$localPersistence.putDeferred(dump.metadata.deferred); | ||
await this.$$localPersistence.putVersion(dump.metadata.version); | ||
await this.$$loadFromLocal(); | ||
if (this.$$remotePersistence) { | ||
await this.$$remotePersistence.put(dump.data); | ||
await this.sync(); // to get latest version number | ||
} | ||
} | ||
/** | ||
@@ -379,3 +401,5 @@ * Public methods, to be used by the application | ||
*/ | ||
copy = this.$$observableObject.copy; | ||
get copy() { | ||
return this.$$observableObject.copy; | ||
} | ||
@@ -395,3 +419,3 @@ getByID(id: string) { | ||
restore(id: string) { | ||
restoreItem(id: string) { | ||
const item = this.$$observableObject.target.find((x) => x.id === id); | ||
@@ -439,2 +463,13 @@ if (!item) { | ||
updateByID(id: string, item: T) { | ||
const index = this.$$observableObject.target.findIndex((x) => x.id === id); | ||
if (index === -1) { | ||
throw new Error("Item not found."); | ||
} | ||
if (this.$$observableObject.target[index].id !== item.id) { | ||
throw new Error("ID mismatch."); | ||
} | ||
this.updateByIndex(index, item); | ||
} | ||
sync = debounce(this.$$sync.bind(this), this.$$debounceRate); | ||
@@ -441,0 +476,0 @@ |
@@ -20,4 +20,3 @@ import { Store } from "../src/store"; | ||
beforeEach(async () => { | ||
store = new Store({ | ||
}); | ||
store = new Store({}); | ||
@@ -237,3 +236,3 @@ let workerFile = readFileSync( | ||
}); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -247,3 +246,3 @@ { | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -298,5 +297,5 @@ const rows = (await env.DB.prepare("SELECT * FROM staff").all()).results; | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.delete({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -348,5 +347,5 @@ const rows = (await env.DB.prepare("SELECT * FROM staff").all()).results; | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.updateByIndex(0, { id: "1", name: "john" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -458,3 +457,3 @@ const rows = (await env.DB.prepare("SELECT * FROM staff").all()).results; | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -525,3 +524,3 @@ const version = Number( | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -589,7 +588,7 @@ const version = Number( | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.isOnline = false; | ||
store.add({ id: "2", name: "john" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -606,3 +605,3 @@ expect(store.list.length).toBe(2); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -664,7 +663,7 @@ expect(store.list.length).toBe(2); | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.isOnline = false; | ||
store.updateByIndex(0, { id: "1", name: "john" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -685,3 +684,3 @@ expect(store.list.length).toBe(1); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -751,3 +750,3 @@ expect(store.list.length).toBe(1); | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -767,7 +766,7 @@ { | ||
token: "any", | ||
name: "staff" | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff" | ||
}) | ||
name: "staff", | ||
}), | ||
}); | ||
@@ -786,4 +785,4 @@ store.isOnline = false; | ||
token: "any", | ||
name: "staff" | ||
}) | ||
name: "staff", | ||
}), | ||
}); | ||
@@ -845,3 +844,3 @@ { | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -916,3 +915,3 @@ const version = Number( | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -988,3 +987,3 @@ // this is more recent | ||
} | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1012,3 +1011,3 @@ store.isOnline = false; | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.isOnline = true; | ||
@@ -1197,10 +1196,10 @@ | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.isOnline = false; | ||
store.updateByIndex(0, { id: "1", name: "john" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.updateByIndex(0, { id: "1", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1221,3 +1220,3 @@ expect(store.list.length).toBe(1); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1287,7 +1286,7 @@ expect(store.list.length).toBe(1); | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
store.isOnline = false; | ||
store.updateByIndex(0, { id: "1", name: "john" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1308,3 +1307,3 @@ expect(store.list.length).toBe(1); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1323,3 +1322,3 @@ expect(store.list.length).toBe(1); | ||
store.updateByIndex(0, { id: "1", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1382,3 +1381,3 @@ expect( | ||
store.add({ id: "1", name: "alex" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1394,3 +1393,3 @@ { | ||
store.updateByIndex(0, { id: "1", name: "mathew" }); | ||
await new Promise((r) => setTimeout(r, 300)); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
@@ -1410,3 +1409,5 @@ expect(store.deferredPresent).toBe(true); | ||
); | ||
const localVersion = Number(await (store as any).$$localPersistence.getVersion()); | ||
const localVersion = Number( | ||
await (store as any).$$localPersistence.getVersion() | ||
); | ||
expect(deferredVersion).toBeGreaterThan(localVersion); | ||
@@ -1443,2 +1444,348 @@ const remoteConflictVersion = (deferredVersion + localVersion) / 2; | ||
}); | ||
describe("Backup and restore", () => { | ||
it("should throw an error when local persistence is not available", async () => { | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store<{ name: string; id: string }>({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
}); | ||
(store as any).$$localPersistence = undefined; | ||
await expect(() => store.backup()).rejects.toThrow( | ||
"Local persistence not available" | ||
); | ||
}); | ||
it("should return a JSON string of the local persistence dump", async () => { | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store<{ name: string; id: string }>({ | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
expect(await store.backup()).toBe( | ||
`{"data":[],"metadata":{"version":0,"deferred":[]}}` | ||
); | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
expect(await store.backup()).toBe( | ||
JSON.stringify({ | ||
data: [ | ||
["1", '{"id":"1","name":"alex"}'], | ||
["2", '{"id":"2","name":"mark"}'], | ||
], | ||
metadata: { | ||
version: 0, | ||
deferred: [], | ||
}, | ||
}) | ||
); | ||
}); | ||
it("deferred updates should be present on backup dump", async () => { | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
store.add({ id: "0", name: "ali" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
store.isOnline = false; | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
const backup = JSON.parse(await store.backup()); | ||
expect(backup.data.length).toBe(3); | ||
expect(backup.data[0][0]).toBe("0"); | ||
expect(backup.data[1][0]).toBe("1"); | ||
expect(backup.data[2][0]).toBe("2"); | ||
expect(backup.metadata.deferred.length).toBe(2); | ||
expect(backup.metadata.deferred[0].id).toBe("1"); | ||
expect(backup.metadata.deferred[1].id).toBe("2"); | ||
}); | ||
it("should restore the local persistence from a JSON string", async () => { | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store<{ name: string; id: string }>({ | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
}); | ||
await store.loaded; | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
const backup = await store.backup(); | ||
store.updateByID("1", { name: "mathew", id: "1" }); | ||
store.updateByID("2", { name: "ron", id: "2" }); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
expect(store.copy).toEqual([ | ||
{ id: "1", name: "mathew" }, | ||
{ id: "2", name: "ron" }, | ||
]); | ||
await store.restoreBackup(backup); | ||
await new Promise((r) => setTimeout(r, 150)); | ||
expect(store.copy).toEqual([ | ||
{ id: "1", name: "alex" }, | ||
{ id: "2", name: "mark" }, | ||
]); | ||
}); | ||
it("should restore deferred", async ()=>{ | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await store.loaded; | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
store.isOnline = false; | ||
await new Promise((r) => setTimeout(r, 100)); | ||
store.updateByID("1", { name: "mathew", id: "1" }); | ||
store.updateByID("2", { name: "ron", id: "2" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(store.copy).toEqual([ | ||
{ id: "1", name: "mathew" }, | ||
{ id: "2", name: "ron" }, | ||
]); | ||
const deferred = (await (store as any).$$localPersistence?.getDeferred()); | ||
expect(store.deferredPresent).toBe(true); | ||
const backup = await store.backup(); | ||
store.isOnline = true; | ||
await store.sync(); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(await (store as any).$$localPersistence?.getDeferred()).toEqual([]); | ||
store.isOnline = false; | ||
await store.restoreBackup(backup); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(await (store as any).$$localPersistence?.getDeferred()).toEqual(deferred); | ||
}); | ||
it("should restore and process deferred", async ()=>{ | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await store.loaded; | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
store.isOnline = false; | ||
await new Promise((r) => setTimeout(r, 100)); | ||
store.updateByID("1", { name: "mathew", id: "1" }); | ||
store.updateByID("2", { name: "ron", id: "2" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(store.copy).toEqual([ | ||
{ id: "1", name: "mathew" }, | ||
{ id: "2", name: "ron" }, | ||
]); | ||
const deferred = (await (store as any).$$localPersistence?.getDeferred()); | ||
expect(store.deferredPresent).toBe(true); | ||
const backup = await store.backup(); | ||
store.isOnline = true; | ||
await store.sync(); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(await (store as any).$$localPersistence?.getDeferred()).toEqual([]); | ||
await store.restoreBackup(backup); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
expect(await (store as any).$$localPersistence?.getDeferred()).toEqual([]); | ||
}); | ||
it("should get a new version after restore is completed", async ()=>{ | ||
{ | ||
// clearing local database before starting | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await (store as any).$$localPersistence.clear(); | ||
await (store as any).$$localPersistence.clearMetadata(); | ||
} | ||
store = new Store({ | ||
remotePersistence: new CloudFlareApexoDB({ | ||
token, | ||
endpoint: "https://apexo-database.vercel.app", | ||
name: "staff", | ||
}), | ||
localPersistence: new IDB({ | ||
name: "staff", | ||
}), | ||
}); | ||
await store.loaded; | ||
store.add({ id: "1", name: "alex" }); | ||
store.add({ id: "2", name: "mark" }); | ||
await new Promise((r) => setTimeout(r, 100)); | ||
await store.sync(); | ||
const version1 = (await (store as any).$$localPersistence?.getVersion()); | ||
const version2 = (await (store as any).$$remotePersistence?.getVersion()); | ||
expect(version1).toBe(version2); | ||
const backup = await store.backup(); | ||
await store.restoreBackup(backup); | ||
const version3 = (await (store as any).$$localPersistence?.getVersion()); | ||
const version4 = (await (store as any).$$remotePersistence?.getVersion()); | ||
expect(version3).toBe(version4); | ||
expect(version3).greaterThan(version1); | ||
}); | ||
}); | ||
}); |
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
272516
7422