import { AutoReconnectConnection } from '../../connection'; import type { BlobRecord, DocClock, DocRecord, ListedBlobRecord, } from '../../storage'; import { type SpaceType, universalId } from '../../utils/universal-id'; export interface SqliteNativeDBOptions { readonly flavour: string; readonly type: SpaceType; readonly id: string; } export interface NativeDBApis { connect: (id: string) => Promise; disconnect: (id: string) => Promise; pushUpdate: (id: string, docId: string, update: Uint8Array) => Promise; getDocSnapshot: (id: string, docId: string) => Promise; setDocSnapshot: (id: string, snapshot: DocRecord) => Promise; getDocUpdates: (id: string, docId: string) => Promise; markUpdatesMerged: ( id: string, docId: string, updates: Date[] ) => Promise; deleteDoc: (id: string, docId: string) => Promise; getDocClocks: (id: string, after?: Date | null) => Promise; getDocClock: (id: string, docId: string) => Promise; getBlob: (id: string, key: string) => Promise; setBlob: (id: string, blob: BlobRecord) => Promise; deleteBlob: (id: string, key: string, permanently: boolean) => Promise; releaseBlobs: (id: string) => Promise; listBlobs: (id: string) => Promise; getPeerRemoteClocks: (id: string, peer: string) => Promise; getPeerRemoteClock: ( id: string, peer: string, docId: string ) => Promise; setPeerRemoteClock: ( id: string, peer: string, docId: string, clock: Date ) => Promise; getPeerPulledRemoteClocks: (id: string, peer: string) => Promise; getPeerPulledRemoteClock: ( id: string, peer: string, docId: string ) => Promise; setPeerPulledRemoteClock: ( id: string, peer: string, docId: string, clock: Date ) => Promise; getPeerPushedClocks: (id: string, peer: string) => Promise; getPeerPushedClock: ( id: string, peer: string, docId: string ) => Promise; setPeerPushedClock: ( id: string, peer: string, docId: string, clock: Date ) => Promise; clearClocks: (id: string) => Promise; setBlobUploadedAt: ( id: string, peer: string, blobId: string, uploadedAt: Date | null ) => Promise; getBlobUploadedAt: ( id: string, peer: string, blobId: string ) => Promise; } type NativeDBApisWrapper = NativeDBApis extends infer APIs ? { [K in keyof APIs]: APIs[K] extends (...args: any[]) => any ? Parameters extends [string, ...infer Rest] ? (...args: Rest) => ReturnType : never : never; } : never; let apis: NativeDBApis | null = null; export function bindNativeDBApis(a: NativeDBApis) { apis = a; } export class NativeDBConnection extends AutoReconnectConnection { readonly apis: NativeDBApisWrapper; readonly flavour = this.options.flavour; readonly type = this.options.type; readonly id = this.options.id; constructor(private readonly options: SqliteNativeDBOptions) { super(); if (!apis) { throw new Error('Not in native context.'); } this.apis = this.warpApis(apis); } override get shareId(): string { return `sqlite:${this.flavour}:${this.type}:${this.id}`; } warpApis(originalApis: NativeDBApis): NativeDBApisWrapper { const id = universalId({ peer: this.flavour, type: this.type, id: this.id, }); return new Proxy( {}, { get: (_target, key: keyof NativeDBApisWrapper) => { const v = originalApis[key]; return async (...args: any[]) => { return v.call( originalApis, id, // @ts-expect-error I don't know why it complains ts(2556) ...args ); }; }, } ) as unknown as NativeDBApisWrapper; } override async doConnect() { await this.apis.connect(); } override doDisconnect() { this.apis.disconnect().catch(err => { console.error('NativeDBConnection close failed', err); }); } }