feat: native sync state (#14190)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added indexed clock management capabilities for documents, enabling
get, set, and clear operations across Android, iOS, Electron, and web
platforms.

* **Refactor**
* Improved storage architecture to dynamically select platform-specific
implementations (SQLite for Electron, IndexedDB for others).

* **Bug Fixes**
* Enhanced document operations to properly maintain and clean up indexer
synchronization state during document lifecycle changes.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2025-12-31 04:09:32 +08:00
committed by GitHub
parent 95ef04f3e0
commit 99332228da
18 changed files with 375 additions and 12 deletions
@@ -3,6 +3,7 @@ import type {
BlobRecord,
CrawlResult,
DocClock,
DocIndexedClock,
DocRecord,
ListedBlobRecord,
} from '../../storage';
@@ -29,6 +30,17 @@ export interface NativeDBApis {
deleteDoc: (id: string, docId: string) => Promise<void>;
getDocClocks: (id: string, after?: Date | null) => Promise<DocClock[]>;
getDocClock: (id: string, docId: string) => Promise<DocClock | null>;
getDocIndexedClock: (
id: string,
docId: string
) => Promise<DocIndexedClock | null>;
setDocIndexedClock: (
id: string,
docId: string,
indexedClock: Date,
indexerVersion: number
) => Promise<void>;
clearDocIndexedClock: (id: string, docId: string) => Promise<void>;
getBlob: (id: string, key: string) => Promise<BlobRecord | null>;
setBlob: (id: string, blob: BlobRecord) => Promise<void>;
deleteBlob: (id: string, key: string, permanently: boolean) => Promise<void>;
@@ -4,6 +4,7 @@ import { SqliteBlobSyncStorage } from './blob-sync';
import { SqliteDocStorage } from './doc';
import { SqliteDocSyncStorage } from './doc-sync';
import { SqliteIndexerStorage } from './indexer';
import { SqliteIndexerSyncStorage } from './indexer-sync';
export * from './blob';
export * from './blob-sync';
@@ -11,6 +12,7 @@ export { bindNativeDBApis, type NativeDBApis } from './db';
export * from './doc';
export * from './doc-sync';
export * from './indexer';
export * from './indexer-sync';
export const sqliteStorages = [
SqliteDocStorage,
@@ -18,4 +20,5 @@ export const sqliteStorages = [
SqliteDocSyncStorage,
SqliteBlobSyncStorage,
SqliteIndexerStorage,
SqliteIndexerSyncStorage,
] satisfies StorageConstructor[];
@@ -0,0 +1,38 @@
import { share } from '../../connection';
import {
type DocIndexedClock,
IndexerSyncStorageBase,
} from '../../storage/indexer-sync';
import { NativeDBConnection, type SqliteNativeDBOptions } from './db';
export class SqliteIndexerSyncStorage extends IndexerSyncStorageBase {
static readonly identifier = 'SqliteIndexerSyncStorage';
override connection = share(new NativeDBConnection(this.options));
constructor(private readonly options: SqliteNativeDBOptions) {
super();
}
private get db() {
return this.connection.apis;
}
override async getDocIndexedClock(
docId: string
): Promise<DocIndexedClock | null> {
return this.db.getDocIndexedClock(docId);
}
override async setDocIndexedClock(clock: DocIndexedClock): Promise<void> {
await this.db.setDocIndexedClock(
clock.docId,
clock.timestamp,
clock.indexerVersion
);
}
override async clearDocIndexedClock(docId: string): Promise<void> {
await this.db.clearDocIndexedClock(docId);
}
}