diff --git a/packages/data-center/package.json b/packages/data-center/package.json index c809066819..bbd463e080 100644 --- a/packages/data-center/package.json +++ b/packages/data-center/package.json @@ -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" } } diff --git a/packages/data-center/src/datacenter/datacenter.ts b/packages/data-center/src/datacenter/datacenter.ts index 7b898a6973..e8e4dbc0c1 100644 --- a/packages/data-center/src/datacenter/datacenter.ts +++ b/packages/data-center/src/datacenter/datacenter.ts @@ -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(); + private readonly _providers = new Map(); + private readonly _workspaces = new Map>(); + private readonly _config; - static async init() { + static async init(): Promise { const dc = new DataCenter(); dc.addProvider(MemoryProvider); + + return dc; } private constructor() { - // TODO + this._config = getKVConfigure('sys'); } - addProvider

(provider: P) { + addProvider(provider: typeof BaseProvider) { this._providers.set(provider.id, provider); } + + private async _initWorkspace(id: string): Promise { + 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 { + if (!this._workspaces.has(id)) { + const workspace = this._initWorkspace(id); + this._workspaces.set(id, workspace); + } + + return this._workspaces.get(id); + } } diff --git a/packages/data-center/src/datacenter/provider.ts b/packages/data-center/src/datacenter/provider.ts deleted file mode 100644 index 4cb0367933..0000000000 --- a/packages/data-center/src/datacenter/provider.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IdbInstance } from './store.js'; - -export interface DCProvider { - id: string; - init(config: IdbInstance): void; -} - -export class MemoryProvider implements DCProvider { - static readonly id = 'memory'; - private _config: IdbInstance | undefined; - - constructor() { - // TODO - } - - init(config: IdbInstance) { - this._config = config; - } -} diff --git a/packages/data-center/src/datacenter/provider/base.ts b/packages/data-center/src/datacenter/provider/base.ts new file mode 100644 index 0000000000..805d0e867b --- /dev/null +++ b/packages/data-center/src/datacenter/provider/base.ts @@ -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; + } +} diff --git a/packages/data-center/src/datacenter/provider/index.ts b/packages/data-center/src/datacenter/provider/index.ts new file mode 100644 index 0000000000..dcec48039b --- /dev/null +++ b/packages/data-center/src/datacenter/provider/index.ts @@ -0,0 +1,2 @@ +export { BaseProvider } from './base.js'; +export { MemoryProvider } from './memory.js'; diff --git a/packages/data-center/src/datacenter/provider/memory.ts b/packages/data-center/src/datacenter/provider/memory.ts new file mode 100644 index 0000000000..58ad54bd65 --- /dev/null +++ b/packages/data-center/src/datacenter/provider/memory.ts @@ -0,0 +1,7 @@ +import { BaseProvider } from './base.js'; + +export class MemoryProvider extends BaseProvider { + constructor() { + super(); + } +} diff --git a/packages/data-center/src/datacenter/store.ts b/packages/data-center/src/datacenter/store.ts index f27e252895..dc3fb75d42 100644 --- a/packages/data-center/src/datacenter/store.ts +++ b/packages/data-center/src/datacenter/store.ts @@ -1,13 +1,13 @@ import { createStore, del, get, keys, set } from 'idb-keyval'; -export type IdbInstance = { +export type ConfigStore = { get: (key: string) => Promise; set: (key: string, value: T) => Promise; keys: () => Promise; delete: (key: string) => Promise; }; -const initialIndexedDB = (database: string): IdbInstance => { +const initialIndexedDB = (database: string): ConfigStore => { const store = createStore(`affine:${database}`, 'database'); return { get: (key: string) => get(key, store), @@ -17,22 +17,31 @@ const initialIndexedDB = (database: string): IdbInstance => { }; }; -const globalIndexedDB = () => { +const scopedIndexedDB = () => { const idb = initialIndexedDB('global'); + const storeCache = new Map>(); - return (scope: string): IdbInstance => ({ - 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 (scope: string): Readonly> => { + 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; + }; }; -export const getKVConfigure = globalIndexedDB(); +export const getKVConfigure = scopedIndexedDB(); diff --git a/packages/data-center/tests/datacenter.spec.ts b/packages/data-center/tests/datacenter.spec.ts index d5a4a267ef..de43f3e1cd 100644 --- a/packages/data-center/tests/datacenter.spec.ts +++ b/packages/data-center/tests/datacenter.spec.ts @@ -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(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7d59807b1..e6b2984183 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: