feat: optional provider

This commit is contained in:
DarkSky
2022-08-12 16:54:47 +08:00
parent 57b8263acd
commit edfa95057b
11 changed files with 267 additions and 85 deletions
+56 -41
View File
@@ -2,6 +2,7 @@ import { Doc } from 'yjs';
import { Awareness } from 'y-protocols/awareness.js';
import {
IndexedDBProvider,
SQLiteProvider,
WebsocketProvider,
} from '@toeverything/datasource/jwt-rpc';
@@ -19,7 +20,10 @@ type YjsDefaultInstances = {
export type YjsProvider = (instances: YjsDefaultInstances) => Promise<void>;
type ProviderType = 'idb' | 'sqlite' | 'ws';
export type YjsProviderOptions = {
enabled: ProviderType[];
backend: typeof BucketBackend[keyof typeof BucketBackend];
params?: Record<string, string>;
importData?: () => Promise<Uint8Array> | Uint8Array | undefined;
@@ -30,53 +34,64 @@ export type YjsProviderOptions = {
export const getYjsProviders = (
options: YjsProviderOptions
): Record<string, YjsProvider> => {
console.log('getYjsProviders', options);
return {
indexeddb: async (instances: YjsDefaultInstances) => {
if (options.enabled.includes('idb')) {
await new IndexedDBProvider(instances.workspace, instances.doc)
.whenSynced;
}
},
sqlite: async (instances: YjsDefaultInstances) => {
const fsHandle = setInterval(async () => {
if (options.hasExporter?.()) {
clearInterval(fsHandle);
const fs = new SQLiteProvider(
instances.workspace,
instances.doc,
await options.importData?.()
);
if (options.exportData) {
fs.registerExporter(options.exportData);
if (options.enabled.includes('sqlite')) {
const fsHandle = setInterval(async () => {
if (options.hasExporter?.()) {
clearInterval(fsHandle);
const fs = new SQLiteProvider(
instances.workspace,
instances.doc,
await options.importData?.()
);
if (options.exportData) {
fs.registerExporter(options.exportData);
}
await fs.whenSynced;
}
await fs.whenSynced;
}
}, 500);
}, 500);
}
},
ws: async (instances: YjsDefaultInstances) => {
if (instances.token) {
const ws = new WebsocketProvider(
instances.token,
options.backend,
instances.workspace,
instances.doc,
{
awareness: instances.awareness,
params: options.params,
}
) as any; // TODO: type is erased after cascading references
if (options.enabled.includes('ws')) {
if (instances.token) {
const ws = new WebsocketProvider(
instances.token,
options.backend,
instances.workspace,
instances.doc,
{
awareness: instances.awareness,
params: options.params,
}
) as any; // TODO: type is erased after cascading references
// Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later
return new Promise<void>((resolve, reject) => {
// TODO: synced will also be triggered on reconnection after losing sync
// There needs to be an event mechanism to emit the synchronization state to the upper layer
ws.once('synced', () => resolve());
ws.once('lost-connection', () => resolve());
ws.once('connection-error', () => reject());
ws.on('synced', () => instances.emitState('connected'));
ws.on('lost-connection', () =>
instances.emitState('retry')
);
ws.on('connection-error', () =>
instances.emitState('retry')
);
});
} else {
return;
// Wait for ws synchronization to complete, otherwise the data will be modified in reverse, which can be optimized later
return new Promise<void>((resolve, reject) => {
// TODO: synced will also be triggered on reconnection after losing sync
// There needs to be an event mechanism to emit the synchronization state to the upper layer
ws.once('synced', () => resolve());
ws.once('lost-connection', () => resolve());
ws.once('connection-error', () => reject());
ws.on('synced', () => instances.emitState('connected'));
ws.on('lost-connection', () =>
instances.emitState('retry')
);
ws.on('connection-error', () =>
instances.emitState('retry')
);
});
} else {
return;
}
}
},
};