feat: dc provider

This commit is contained in:
DarkSky 2022-12-29 21:20:26 +08:00 committed by DarkSky
parent e6e4c775a8
commit 2bf6bf7ed8
9 changed files with 155 additions and 42 deletions

View File

@ -21,6 +21,7 @@
},
"devDependencies": {
"@playwright/test": "^1.29.1",
"fake-indexeddb": "4.0.1",
"typescript": "^4.8.4"
},
"dependencies": {
@ -30,6 +31,7 @@
"ky": "^0.33.0",
"lib0": "^0.2.58",
"swr": "^2.0.0",
"yjs": "^13.5.43",
"y-protocols": "^1.0.5"
}
}

View File

@ -1,18 +1,48 @@
import { DCProvider, MemoryProvider } from './provider.js';
import { Doc } from 'yjs';
import type { BaseProvider } from './provider/index.js';
import { MemoryProvider } from './provider/index.js';
import { getKVConfigure } from './store.js';
export class DataCenter {
private readonly _providers = new Map<string, DCProvider>();
private readonly _providers = new Map<string, typeof BaseProvider>();
private readonly _workspaces = new Map<string, Promise<Doc | undefined>>();
private readonly _config;
static async init() {
static async init(): Promise<DataCenter> {
const dc = new DataCenter();
dc.addProvider(MemoryProvider);
return dc;
}
private constructor() {
// TODO
this._config = getKVConfigure('sys');
}
addProvider<P extends DCProvider>(provider: P) {
addProvider(provider: typeof BaseProvider) {
this._providers.set(provider.id, provider);
}
private async _initWorkspace(id: string): Promise<Doc> {
const workspace = new Doc();
const providerId = await this._config.get(`workspace:${id}:provider`);
if (this._providers.has(providerId)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const Provider = this._providers.get(providerId)!;
const provider = new Provider();
provider.init(getKVConfigure(id));
}
return workspace;
}
async getWorkspace(id: string): Promise<Doc | undefined> {
if (!this._workspaces.has(id)) {
const workspace = this._initWorkspace(id);
this._workspaces.set(id, workspace);
}
return this._workspaces.get(id);
}
}

View File

@ -1,19 +0,0 @@
import { IdbInstance } from './store.js';
export interface DCProvider {
id: string;
init(config: IdbInstance<any>): void;
}
export class MemoryProvider implements DCProvider {
static readonly id = 'memory';
private _config: IdbInstance<any> | undefined;
constructor() {
// TODO
}
init(config: IdbInstance<any>) {
this._config = config;
}
}

View File

@ -0,0 +1,14 @@
import type { ConfigStore } from '../store.js';
export class BaseProvider {
static id = 'memory';
protected _config: ConfigStore | undefined;
constructor() {
// TODO
}
init(config: ConfigStore) {
this._config = config;
}
}

View File

@ -0,0 +1,2 @@
export { BaseProvider } from './base.js';
export { MemoryProvider } from './memory.js';

View File

@ -0,0 +1,7 @@
import { BaseProvider } from './base.js';
export class MemoryProvider extends BaseProvider {
constructor() {
super();
}
}

View File

@ -1,13 +1,13 @@
import { createStore, del, get, keys, set } from 'idb-keyval';
export type IdbInstance<T = any> = {
export type ConfigStore<T = any> = {
get: (key: string) => Promise<T | undefined>;
set: (key: string, value: T) => Promise<void>;
keys: () => Promise<string[]>;
delete: (key: string) => Promise<void>;
};
const initialIndexedDB = <T = any>(database: string): IdbInstance<T> => {
const initialIndexedDB = <T = any>(database: string): ConfigStore<T> => {
const store = createStore(`affine:${database}`, 'database');
return {
get: (key: string) => get<T>(key, store),
@ -17,22 +17,31 @@ const initialIndexedDB = <T = any>(database: string): IdbInstance<T> => {
};
};
const globalIndexedDB = () => {
const scopedIndexedDB = () => {
const idb = initialIndexedDB('global');
const storeCache = new Map<string, Readonly<ConfigStore>>();
return <T = any>(scope: string): IdbInstance<T> => ({
get: (key: string) => idb.get(`${scope}:${key}`),
set: (key: string, value: T) => idb.set(`${scope}:${key}`, value),
keys: () =>
idb
.keys()
.then(keys =>
keys
.filter(k => k.startsWith(`${scope}:`))
.map(k => k.replace(`${scope}:`, ''))
),
delete: (key: string) => del(`${scope}:${key}`),
});
return <T = any>(scope: string): Readonly<ConfigStore<T>> => {
if (!storeCache.has(scope)) {
const store = {
get: (key: string) => idb.get(`${scope}:${key}`),
set: (key: string, value: T) => idb.set(`${scope}:${key}`, value),
keys: () =>
idb
.keys()
.then(keys =>
keys
.filter(k => k.startsWith(`${scope}:`))
.map(k => k.replace(`${scope}:`, ''))
),
delete: (key: string) => del(`${scope}:${key}`),
};
storeCache.set(scope, store);
}
return storeCache.get(scope) as ConfigStore<T>;
};
};
export const getKVConfigure = globalIndexedDB();
export const getKVConfigure = scopedIndexedDB();

View File

@ -1,8 +1,12 @@
import { test, expect } from '@playwright/test';
import { getDataCenter } from './utils.js';
import 'fake-indexeddb/auto';
test('can init data center', async () => {
const dataCenter = await getDataCenter();
expect(dataCenter).toBeTruthy();
const workspace = await dataCenter.getWorkspace('test');
expect(workspace).toBeTruthy();
});

64
pnpm-lock.yaml generated
View File

@ -133,6 +133,7 @@ importers:
specifiers:
'@playwright/test': ^1.29.1
encoding: ^0.1.13
fake-indexeddb: 4.0.1
firebase: ^9.15.0
idb-keyval: ^6.2.0
ky: ^0.33.0
@ -140,6 +141,7 @@ importers:
swr: ^2.0.0
typescript: ^4.8.4
y-protocols: ^1.0.5
yjs: ^13.5.43
dependencies:
encoding: 0.1.13
firebase: 9.15.0_encoding@0.1.13
@ -148,8 +150,10 @@ importers:
lib0: 0.2.58
swr: 2.0.0
y-protocols: 1.0.5
yjs: 13.5.44
devDependencies:
'@playwright/test': 1.29.1
fake-indexeddb: 4.0.1
typescript: 4.9.3
packages/logger:
@ -4278,6 +4282,11 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
/base64-arraybuffer-es6/0.7.0:
resolution: {integrity: sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==}
engines: {node: '>=6.0.0'}
dev: true
/base64-js/1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
@ -4858,6 +4867,12 @@ packages:
csstype: 3.1.1
dev: false
/domexception/1.0.1:
resolution: {integrity: sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==}
dependencies:
webidl-conversions: 4.0.2
dev: true
/domino/2.1.6:
resolution: {integrity: sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==}
dev: false
@ -5617,6 +5632,12 @@ packages:
tmp: 0.0.33
dev: true
/fake-indexeddb/4.0.1:
resolution: {integrity: sha512-hFRyPmvEZILYgdcLBxVdHLik4Tj3gDTu/g7s9ZDOiU3sTNiGx+vEu1ri/AMsFJUZ/1sdRbAVrEcKndh3sViBcA==}
dependencies:
realistic-structured-clone: 3.0.0
dev: true
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
@ -8183,6 +8204,14 @@ packages:
util-deprecate: 1.0.2
dev: false
/realistic-structured-clone/3.0.0:
resolution: {integrity: sha512-rOjh4nuWkAqf9PWu6JVpOWD4ndI+JHfgiZeMmujYcPi+fvILUu7g6l26TC1K5aBIp34nV+jE1cDO75EKOfHC5Q==}
dependencies:
domexception: 1.0.1
typeson: 6.1.0
typeson-registry: 1.0.0-alpha.39
dev: true
/redent/3.0.0:
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
engines: {node: '>=8'}
@ -8944,6 +8973,13 @@ packages:
punycode: 2.1.1
dev: true
/tr46/2.1.0:
resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
engines: {node: '>=8'}
dependencies:
punycode: 2.1.1
dev: true
/trim-newlines/3.0.1:
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
engines: {node: '>=8'}
@ -9098,6 +9134,20 @@ packages:
hasBin: true
dev: true
/typeson-registry/1.0.0-alpha.39:
resolution: {integrity: sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==}
engines: {node: '>=10.0.0'}
dependencies:
base64-arraybuffer-es6: 0.7.0
typeson: 6.1.0
whatwg-url: 8.7.0
dev: true
/typeson/6.1.0:
resolution: {integrity: sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==}
engines: {node: '>=0.1.14'}
dev: true
/unbox-primitive/1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
dependencies:
@ -9263,6 +9313,11 @@ packages:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
dev: true
/webidl-conversions/6.1.0:
resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
engines: {node: '>=10.4'}
dev: true
/webpack-sources/1.4.3:
resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==}
dependencies:
@ -9299,6 +9354,15 @@ packages:
webidl-conversions: 4.0.2
dev: true
/whatwg-url/8.7.0:
resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
engines: {node: '>=10'}
dependencies:
lodash: 4.17.21
tr46: 2.1.0
webidl-conversions: 6.1.0
dev: true
/which-boxed-primitive/1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies: