import { BehaviorSubject, type Observable } from 'rxjs'; import { Model, Patch } from 'json-joy/lib/json-crdt'; import type { ServerBatch, ServerHistory } from '../../remote/types'; import type { BlockId, LocalRepo, LocalRepoEvent, LocalRepoSyncRequest, LocalRepoSyncResponse, LocalRepoGetResponse, LocalRepoGetRequest, LocalRepoCreateResponse, LocalRepoCreateRequest, LocalRepoGetIfResponse, LocalRepoGetIfRequest, LocalRepoPullResponse } from '../types'; import type { BinStrLevel, BinStrLevelOperation, BlockMeta, SyncResult, LevelLocalRepoPubSub, LevelLocalRepoCursor, BlockSyncRecord } from './types'; import type { CrudLocalRepoCipher } from './types'; import type { Locks } from 'thingies/lib/Locks'; import type { JsonValueCodec } from '@jsonjoy.com/json-pack/lib/codecs/types'; export interface LevelLocalRepoOpts { /** * Session ID of the user on this device. The same session ID is reused across * all tabs. */ readonly sid: number; /** * Local persistance LevelDB API. */ readonly kv: BinStrLevel; /** * Optional content encryption/decryption API. */ readonly cipher?: CrudLocalRepoCipher; /** * Cross-tab locking API. */ readonly locks: Locks; /** * Optional observable that emits `true` when the device is connected to the * server and `false` when it's not. */ readonly connected$?: BehaviorSubject; /** * RPC API for communication with the server. */ readonly rpc: ServerHistory; /** * Event bus. */ readonly pubsub?: LevelLocalRepoPubSub; /** * Number of milliseconds after which remote calls are considered timed out. */ readonly remoteTimeout?: number; /** * Minimum backoff time in milliseconds for the sync loop. */ readonly syncLoopMinBackoff?: number; /** * Maximum backoff time in milliseconds for the sync loop. */ readonly syncLoopMaxBackoff?: number; /** * If specified, background sync errors will be emitted here. */ readonly onSyncError?: (error: Error | unknown) => void; } export declare class LevelLocalRepo implements LocalRepo { protected readonly opts: LevelLocalRepoOpts; readonly kv: BinStrLevel; readonly locks: Locks; readonly sid: number; readonly connected$: BehaviorSubject; protected readonly pubsub: LevelLocalRepoPubSub; protected readonly cipher?: CrudLocalRepoCipher; protected readonly codec: JsonValueCodec; constructor(opts: LevelLocalRepoOpts); private _conSub; private _stopped; stop(): void; protected encrypt(blob: Uint8Array, zip: boolean): Promise; protected decrypt(blob: Uint8Array, zip: boolean): Promise; protected encode(value: unknown, zip: boolean): Promise; protected decode(blob: Uint8Array, zip: boolean): Promise; /** @todo Encrypt collection and key. */ blockKeyBase(id: BlockId): Promise; frontierKeyBase(blockKeyBase: string): string; frontierKey(blockKeyBase: string, time: number): string; batchKeyBase(blockKeyBase: string): string; batchKey(blockKeyBase: string, seq: number): string; snapshotKeyBase(blockKeyBase: string): string; snapshotKey(blockKeyBase: string, seq: number): string; protected syncKey(keyBase: string): string; protected _exists(keyBase: string): Promise; protected _modelWrOp(keyBase: string, model: Uint8Array): Promise; protected _metaWrOp(keyBase: string, meta?: BlockMeta): Promise; protected _modelWrOps(keyBase: string, model: Uint8Array, meta?: BlockMeta): Promise; protected _wrModel(keyBase: string, model: Uint8Array, meta?: BlockMeta): Promise; protected load(id: BlockId): Promise<{ model: Model; cursor: number; }>; protected readMeta(keyBase: string): Promise; readModel0(keyBase: string): Promise; readModel(keyBase: string): Promise; readFrontierBlobs0(keyBase: string): AsyncGenerator], void, unknown>; readFrontier0(keyBase: string): Promise; readFrontierTip(keyBase: string): Promise; protected lockBlock(keyBase: string, fn: () => Promise): Promise; protected lockBlockForSync(keyBase: string, fn: () => Promise): Promise; protected isBlockLockedForSync(keyBase: string): boolean; protected markDirty(keyBase: string, id: BlockId): Promise; protected markDirtyAndSync(keyBase: string, id: BlockId): Promise; protected remoteTimeout(): number; /** * Pushes to remote. * @param id Block ID. * @param pull Whether to pull if there are no patches to push. */ protected push(keyBase: string, id: BlockId, doPull?: boolean): Promise; /** * Iterates over all blocks marked as dirty. */ protected listDirty(): AsyncIterableIterator; protected listDirtyBatch(batchSize?: number): AsyncIterableIterator; syncItem(keyBase: string, id: BlockId): Promise; remoteSyncAll(): Promise; protected _remoteSyncLoopActive: boolean; protected _remoteSyncDelayTimer: unknown; protected runRemoteSyncLoop(): void; /** ----------------------------------------------------- {@link LocalRepo} */ create({ id, patches }: LocalRepoCreateRequest): Promise; get({ id, remote }: LocalRepoGetRequest): Promise; getIf(request: LocalRepoGetIfRequest): Promise; sync(req: LocalRepoSyncRequest): Promise; private _syncCreate; private _syncMerge; protected readLocal0(keyBase: string): Promise<[model: Model, cursor: LevelLocalRepoCursor]>; private _syncRead0; private _syncRead; del(id: BlockId): Promise; /** * Pull from remote. */ pull(id: BlockId): Promise; protected pullNew(id: BlockId, keyBase: string): Promise<{ model: Model; meta: BlockMeta; }>; protected pullExisting(id: BlockId, keyBase: string): Promise<{ model: Model; meta: BlockMeta; }>; change$(id: BlockId): Observable; private _subs; protected _subRemote(id: BlockId): Observable; protected _onUpd(id: BlockId, batch: ServerBatch): Promise; }