XO wallet integration
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { BaseStore } from './base-store.js';
|
||||
import { StoreInMemory } from './store-inmemory.js';
|
||||
|
||||
/**
|
||||
* A store that accepts multiple stores and syncs them by key.
|
||||
@@ -12,127 +13,128 @@ import { BaseStore } from './base-store.js';
|
||||
*/
|
||||
export class StoreComposite extends BaseStore {
|
||||
static async from(
|
||||
localStore: BaseStore,
|
||||
remoteStores: Array<BaseStore> = [],
|
||||
stores: Array<BaseStore> = [],
|
||||
primaryStore: BaseStore = StoreInMemory.from(),
|
||||
) {
|
||||
const compositeStore = new this(localStore, remoteStores);
|
||||
const compositeStore = new this(stores, primaryStore);
|
||||
await compositeStore.sync();
|
||||
return compositeStore;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private localStore: BaseStore,
|
||||
private remoteStores: Array<BaseStore> = [],
|
||||
private stores: Array<BaseStore> = [],
|
||||
private primaryStore: BaseStore,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async all<T>() {
|
||||
return await this.localStore.all<T>();
|
||||
return await this.primaryStore.all<T>();
|
||||
}
|
||||
|
||||
async keys() {
|
||||
return await this.localStore.keys();
|
||||
return await this.primaryStore.keys();
|
||||
}
|
||||
|
||||
async get<T>(key: string) {
|
||||
return await this.localStore.get<T>(key);
|
||||
return await this.primaryStore.get<T>(key);
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T) {
|
||||
await this.localStore.set(key, value);
|
||||
await this.primaryStore.set(key, value);
|
||||
this.emit('set', { key, value });
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
await this.localStore.delete(key);
|
||||
await this.primaryStore.delete(key);
|
||||
this.emit('delete', { key });
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await this.localStore.clear();
|
||||
await this.primaryStore.clear();
|
||||
this.emit('clear', undefined);
|
||||
}
|
||||
|
||||
async has(key: string) {
|
||||
return await this.localStore.has(key);
|
||||
return await this.primaryStore.has(key);
|
||||
}
|
||||
|
||||
async close() {
|
||||
await Promise.all([
|
||||
this.localStore.close(),
|
||||
...this.remoteStores.map((store) => store.close()),
|
||||
this.primaryStore.close(),
|
||||
...this.stores.map((store) => store.close()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new remote store and set up synchronization
|
||||
*/
|
||||
public async addRemoteStore(remoteStore: BaseStore): Promise<void> {
|
||||
this.remoteStores.push(remoteStore);
|
||||
async addStore(remoteStore: BaseStore): Promise<void> {
|
||||
this.stores.push(remoteStore);
|
||||
|
||||
// Propagate existing local events to the new remote
|
||||
this.localStore.on('set', async (payload) => {
|
||||
this.primaryStore.on('set', async (payload) => {
|
||||
if (await remoteStore.has(payload.key)) return;
|
||||
await remoteStore.set(payload.key, payload.value);
|
||||
});
|
||||
|
||||
this.localStore.on('delete', async (payload) => {
|
||||
await remoteStore.delete(payload.key);
|
||||
this.primaryStore.on('delete', async (payload) => {
|
||||
if (await remoteStore.has(payload.key)) {
|
||||
await remoteStore.delete(payload.key);
|
||||
}
|
||||
});
|
||||
|
||||
this.localStore.on('clear', async () => {
|
||||
this.primaryStore.on('clear', async () => {
|
||||
await remoteStore.clear();
|
||||
});
|
||||
|
||||
// Set up remote to local propagation
|
||||
remoteStore.on('set', async (payload) => {
|
||||
if (await this.localStore.has(payload.key)) return;
|
||||
await this.localStore.set(payload.key, payload.value);
|
||||
if (await this.primaryStore.has(payload.key)) return;
|
||||
await this.primaryStore.set(payload.key, payload.value);
|
||||
});
|
||||
|
||||
remoteStore.on('delete', async (payload) => {
|
||||
if (await this.localStore.has(payload.key)) {
|
||||
await this.localStore.delete(payload.key);
|
||||
if (await this.primaryStore.has(payload.key)) {
|
||||
await this.primaryStore.delete(payload.key);
|
||||
}
|
||||
});
|
||||
|
||||
remoteStore.on('clear', async () => {
|
||||
await this.localStore.clear();
|
||||
await this.primaryStore.clear();
|
||||
});
|
||||
|
||||
// Sync with the new remote
|
||||
await this.syncWithRemote(remoteStore);
|
||||
}
|
||||
|
||||
private async syncWithRemote(remoteStore: BaseStore): Promise<void> {
|
||||
const remoteKeys = await remoteStore.keys();
|
||||
const localKeys = await this.localStore.keys();
|
||||
|
||||
const missingLocalKeys = remoteKeys.filter(
|
||||
(key) => !localKeys.includes(key),
|
||||
);
|
||||
const missingRemoteKeys = localKeys.filter(
|
||||
(key) => !remoteKeys.includes(key),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
...missingLocalKeys.map(async (key) => {
|
||||
const value = await remoteStore.get(key);
|
||||
await this.localStore.set(key, value);
|
||||
}),
|
||||
...missingRemoteKeys.map(async (key) => {
|
||||
const value = await this.localStore.get(key);
|
||||
await remoteStore.set(key, value);
|
||||
}),
|
||||
]);
|
||||
await this.syncStore(remoteStore);
|
||||
}
|
||||
|
||||
async sync(): Promise<void> {
|
||||
// Sync all remote stores in parallel
|
||||
await Promise.all(
|
||||
this.remoteStores.map(async (remoteStore) =>
|
||||
this.syncWithRemote(remoteStore),
|
||||
),
|
||||
this.stores.map(async (remoteStore) => this.syncStore(remoteStore)),
|
||||
);
|
||||
}
|
||||
|
||||
private async syncStore(store: BaseStore): Promise<void> {
|
||||
const storeKeys = await store.keys();
|
||||
const primaryStoreKeys = await this.primaryStore.keys();
|
||||
|
||||
const missingLocalKeys = storeKeys.filter(
|
||||
(key) => !primaryStoreKeys.includes(key),
|
||||
);
|
||||
const missingRemoteKeys = primaryStoreKeys.filter(
|
||||
(key) => !storeKeys.includes(key),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
...missingLocalKeys.map(async (key) => {
|
||||
const value = await store.get(key);
|
||||
await this.set(key, value);
|
||||
}),
|
||||
...missingRemoteKeys.map(async (key) => {
|
||||
const value = await this.primaryStore.get(key);
|
||||
await store.set(key, value);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user