From b3a8e6298439304fdbfa80aee87996e99b717902 Mon Sep 17 00:00:00 2001 From: EYHN Date: Tue, 30 Jan 2024 06:31:15 +0000 Subject: [PATCH] feat(infra): standard storage service (#5563) --- packages/common/infra/package.json | 1 + packages/common/infra/src/index.ts | 3 + .../src/storage/__tests__/memento.spec.ts | 40 +++++++++++++ packages/common/infra/src/storage/index.ts | 1 + packages/common/infra/src/storage/memento.ts | 57 +++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 packages/common/infra/src/storage/__tests__/memento.spec.ts create mode 100644 packages/common/infra/src/storage/index.ts create mode 100644 packages/common/infra/src/storage/memento.ts diff --git a/packages/common/infra/package.json b/packages/common/infra/package.json index 03486b0badcc2..6303208aa5281 100644 --- a/packages/common/infra/package.json +++ b/packages/common/infra/package.json @@ -9,6 +9,7 @@ "./app-config-storage": "./src/app-config-storage.ts", "./di": "./src/di/index.ts", "./livedata": "./src/livedata/index.ts", + "./storage": "./src/storage/index.ts", ".": "./src/index.ts" }, "dependencies": { diff --git a/packages/common/infra/src/index.ts b/packages/common/infra/src/index.ts index 6e94b1bdc7560..fe3362374dc2d 100644 --- a/packages/common/infra/src/index.ts +++ b/packages/common/infra/src/index.ts @@ -2,3 +2,6 @@ export * from './app-config-storage'; export * from './atom'; export * from './blocksuite'; export * from './command'; +export * from './di'; +export * from './livedata'; +export * from './storage'; diff --git a/packages/common/infra/src/storage/__tests__/memento.spec.ts b/packages/common/infra/src/storage/__tests__/memento.spec.ts new file mode 100644 index 0000000000000..b8cfe96db553c --- /dev/null +++ b/packages/common/infra/src/storage/__tests__/memento.spec.ts @@ -0,0 +1,40 @@ +import { describe, expect, test } from 'vitest'; + +import { ServiceCollection } from '../../di'; +import { GlobalCache, GlobalState, MemoryMemento } from '..'; + +describe('memento', () => { + test('memory', () => { + const memento = new MemoryMemento(); + + expect(memento.get('foo')).toBeNull(); + memento.set('foo', 'bar'); + expect(memento.get('foo')).toEqual('bar'); + + let subscribed = null; + const subscription = memento.watch('foo').subscribe(v => { + subscribed = v; + }); + expect(subscribed).toEqual('bar'); + memento.set('foo', 'baz'); + expect(subscribed).toEqual('baz'); + + subscription.unsubscribe(); + memento.set('foo', 'hello'); + expect(subscribed).toEqual('baz'); + }); + + test('service', () => { + const services = new ServiceCollection(); + + services + .addImpl(GlobalCache, MemoryMemento) + .addImpl(GlobalState, MemoryMemento); + + const provider = services.provider(); + const cache = provider.get(GlobalCache); + expect(cache).toBeInstanceOf(MemoryMemento); + const state = provider.get(GlobalState); + expect(state).toBeInstanceOf(MemoryMemento); + }); +}); diff --git a/packages/common/infra/src/storage/index.ts b/packages/common/infra/src/storage/index.ts new file mode 100644 index 0000000000000..482b604413bd8 --- /dev/null +++ b/packages/common/infra/src/storage/index.ts @@ -0,0 +1 @@ +export * from './memento'; diff --git a/packages/common/infra/src/storage/memento.ts b/packages/common/infra/src/storage/memento.ts new file mode 100644 index 0000000000000..73b34c893c424 --- /dev/null +++ b/packages/common/infra/src/storage/memento.ts @@ -0,0 +1,57 @@ +import type { Observable } from 'rxjs'; + +import { createIdentifier } from '../di'; +import { LiveData } from '../livedata'; + +/** + * A memento represents a storage utility. It can store and retrieve values, and observe changes. + */ +export interface Memento { + get(key: string): T | null; + watch(key: string): Observable; + set(key: string, value: T | null): void; +} + +/** + * A memento object that stores the entire application state. + * + * State is persisted, even the application is closed. + */ +export interface GlobalState extends Memento {} + +export const GlobalState = createIdentifier('GlobalState'); + +/** + * A memento object that stores the entire application cache. + * + * Cache may be deleted from time to time, business logic should not rely on cache. + */ +export interface GlobalCache extends Memento {} + +export const GlobalCache = createIdentifier('GlobalCache'); + +/** + * A simple implementation of Memento. Used for testing. + */ +export class MemoryMemento implements Memento { + private readonly data = new Map>(); + + private getLiveData(key: string): LiveData { + let data = this.data.get(key); + if (!data) { + data = new LiveData(null); + this.data.set(key, data); + } + return data; + } + + get(key: string): T | null { + return this.getLiveData(key).value; + } + watch(key: string): Observable { + return this.getLiveData(key).asObservable(); + } + set(key: string, value: T | null): void { + this.getLiveData(key).next(value); + } +}