import { AbstractType as YAbstractType } from 'yjs'; import { BlockItem } from '../types'; export type ChangedStateKeys = 'add' | 'update' | 'delete'; export type ChangedStates = Map; export type BlockListener = ( states: ChangedStates ) => Promise | R; export type Connectivity = 'disconnect' | 'connecting' | 'connected' | 'retry'; export type Operable> = T extends Base ? ContentOperation : T; export interface InternalPlainObject {} export type BaseTypes = string | number | boolean | InternalPlainObject; export type ContentTypes = BaseTypes | ContentOperation; interface ContentOperation { get length(): number; createText(): TextOperation; createArray(): ArrayOperation; createMap(): MapOperation; asText(): TextOperation | undefined; asArray(): | ArrayOperation | undefined; asMap(): | MapOperation | undefined; autoGet( root: ThisType | Record, path: string[] ): unknown | undefined; autoSet( root: ThisType, path: string[], data: unknown, partial?: boolean ): void; } type TextAttributes = Record; export type TextToken = { insert: string; attributes?: TextAttributes; }; export interface TextOperation extends ContentOperation { insert( index: number, content: string, format?: Record ): void; format(index: number, length: number, format: Record): void; delete(index: number, length: number): void; setAttribute(name: string, value: BaseTypes): void; getAttribute(name: string): T | undefined; toString(): TextToken[]; } export interface ArrayOperation extends ContentOperation { insert(index: number, content: Array>): void; delete(index: number, length: number): void; push(content: Array>): void; unshift(content: Array>): void; get(index: number): Operable | undefined; slice(start?: number, end?: number): Array>; map(callback: (value: T, index: number) => R): Array; forEach(callback: (value: T, index: number) => boolean | void): void; find( callback: (value: T, index: number) => boolean ): R | undefined; findIndex(callback: (value: T, index: number) => boolean): number; } export interface MapOperation extends ContentOperation { set(key: string, value: Operable): void; get(key: string): Operable | undefined; delete(key: string): void; has(key: string): boolean; } export type HistoryCallback = (map: Map) => void; export interface HistoryManager { onPush(name: string, callback: HistoryCallback): void; offPush(name: string): boolean; onPop(name: string, callback: HistoryCallback): void; offPop(name: string): boolean; undo(): Map | undefined; redo(): Map | undefined; clear(): void; } type BlockPosition = { pos?: number; before?: string; after?: string }; interface BlockInstance { get id(): string; get type(): BlockItem['type']; get flavor(): BlockItem['flavor']; // TODO: flavor needs optimization setFlavor(flavor: BlockItem['flavor']): void; get created(): BlockItem['created']; get updated(): number; // update time, UTC timestamp, read only get creator(): string | undefined; // creator id get children(): string[]; getChildren(block_ids: (string | undefined)[]): BlockInstance[]; hasChildren(block_id: string): boolean; insertChildren( block: ThisType>, pos?: BlockPosition ): void; removeChildren(block_ids: (string | undefined)[]): void; get content(): BlockItem['content']; on( key: 'children' | 'content', name: string, listener: BlockListener ): void; off(key: 'children' | 'content', name: string): void; addChildrenListener(name: string, listener: BlockListener): void; removeChildrenListener(name: string): void; addContentListener(name: string, listener: BlockListener): void; removeContentListener(name: string): void; scopedHistory(scope: any[]): HistoryManager; } interface AsyncDatabaseAdapter { inspector(): Record; reload(): void; createBlock( options: Pick, 'type' | 'flavor'> & { binary?: ArrayBuffer; uuid?: string; } ): Promise>; getBlock(id: string): Promise | undefined>; getBlockByFlavor(flavor: BlockItem['flavor']): Promise; getBlockByType(type: BlockItem['type']): Promise; checkBlocks(keys: string[]): Promise; deleteBlocks(keys: string[]): Promise; on( key: 'editing' | 'updated' | 'connectivity', listener: BlockListener ): void; suspend(suspend: boolean): void; history(): HistoryManager; getUserId(): string; } export type DataExporter = (binary: Uint8Array) => Promise; export const getDataExporter = () => { let exporter: DataExporter | undefined = undefined; let importer: (() => Uint8Array | undefined) | undefined = undefined; const importData = () => importer?.(); const exportData = (binary: Uint8Array) => exporter?.(binary); const hasExporter = () => !!exporter; const installExporter = ( initialData: Uint8Array | undefined, cb: DataExporter ) => { return new Promise(resolve => { importer = () => initialData; exporter = async (data: Uint8Array) => { exporter = cb; await cb(data); resolve(); }; }); }; return { importData, exportData, hasExporter, installExporter }; }; export { YjsAdapter } from './yjs'; export type { YjsContentOperation, YjsInitOptions } from './yjs'; export type { AsyncDatabaseAdapter, BlockPosition, BlockInstance, ContentOperation, };