[]
        
(Showing Draft Content)

Custom Database Adapter

If the built-in adapters do not meet your needs, you can implement the IDatabaseAdapter interface to create a custom adapter.

Below is an example.

import { IOp, ISnapshot, IDocument, ICommitSnapshot, IDatabaseAdapter, Db } from '@mescius/js-collaboration-ot';

export class SampleDb<S = unknown, T = unknown> extends Db<S, T> implements IDatabaseAdapter<S, T> {
    private ops: Map<string, IOp<T>[]> = new Map();
    private documents: Map<string, IDocument> = new Map();
    private fragments: Map<string, Map<string, S>> = new Map();

    async getOps(id: string, from: number, to?: number): Promise<IOp<T>[]> {
        const allOps = this.ops.get(id) || [];
        const toVersion = to ?? Number.MAX_SAFE_INTEGER;
        return allOps.filter(op => op.v >= from && op.v < toVersion);
    }

    async getDocument(id: string): Promise<IDocument | undefined> {
        return this.documents.get(id);
    }

    async getSnapshot(id: string): Promise<ISnapshot<S> | undefined> {
        const doc = this.documents.get(id);
        if (!doc) return;
        
        const fragments = await this.getFragments(id);
        return {
            id,
            v: doc.snapshotVersion,
            type: doc.type,
            fragments
        };
    }

    async getFragment(id: string, fragmentId: string): Promise<S | undefined> {
        return this.fragments.get(id)?.get(fragmentId);
    }

    async getFragments(id: string, fragmentIds?: string[]): Promise<{ [id: string]: S }> {
        const allFragments = this.fragments.get(id) || new Map();
        const result: { [id: string]: S } = {};
        
        if (fragmentIds) {
            fragmentIds.forEach(id => {
                const fragment = allFragments.get(id);
                if (fragment) result[id] = fragment;
            });
        } else {
            allFragments.forEach((value, key) => {
                result[key] = value;
            });
        }
        return result;
    }

    async commitOp(id: string, op: IOp<T>, document: IDocument): Promise<boolean> {
        if (op.create) {
            if (this.documents.has(id)) return false;
            this.documents.set(id, document);
            this.ops.set(id, [op]);
            this.fragments.set(id, new Map());
            return true;
        }

        const currentOps = this.ops.get(id) || [];
        currentOps.push(op);
        this.ops.set(id, currentOps);
        this.documents.set(id, document);
        return true;
    }

    async commitSnapshot(id: string, snapshot: ICommitSnapshot<S>): Promise<boolean> {
        const doc = this.documents.get(id);
        if (!doc) return false;
        
        doc.snapshotVersion = snapshot.v;
        const fragments = this.fragments.get(id) || new Map();
        
        if (snapshot.fragmentsChanges) {
            const { createFragments, updateFragments, deleteFragments } = snapshot.fragmentsChanges;
            
            // Handle creates and updates
            Object.entries({ ...createFragments, ...updateFragments }).forEach(([key, value]) => {
                fragments.set(key, value as S);
            });
            
            // Handle deletes
            deleteFragments?.forEach(key => fragments.delete(key));
        }
        
        return true;
    }

    async close(): Promise<void> {
        // Nothing to do for in-memory Database
    }
}

Use a Custom Database Adapter

import { DocumentServices, IDatabaseAdapter, MemoryDb } from '@mescius/js-collaboration-ot';

const dbAdapter: IDatabaseAdapter = new SampleDb();

const docService = new DocumentServices({ db: dbAdapter });

More Example

File Database Adapter