adjust sync native package

This commit is contained in:
Nikita Sivukhin
2025-09-24 18:43:50 +04:00
parent 28c9850b57
commit afbfa98a8d
7 changed files with 284 additions and 154 deletions

View File

@@ -1,5 +1,15 @@
import { run, memoryIO, SyncEngineGuards } from "./run.js"
import { SyncOpts, ProtocolIo, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats } from "./types.js"
import { DatabaseOpts, ProtocolIo, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, DatabaseStats, DatabaseChangeType } from "./types.js"
export { run, memoryIO, SyncEngineGuards }
export type { SyncOpts, ProtocolIo, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats }
export type {
DatabaseStats,
DatabaseOpts,
DatabaseChangeType,
DatabaseRowMutation,
DatabaseRowStatement,
DatabaseRowTransformResult,
ProtocolIo,
RunOpts,
}

View File

@@ -3,9 +3,6 @@
import { GeneratorResponse, ProtocolIo, RunOpts } from "./types.js";
import { AsyncLock } from "@tursodatabase/database-common";
const GENERATOR_RESUME_IO = 0;
const GENERATOR_RESUME_DONE = 1;
interface TrackPromise<T> {
promise: Promise<T>,
finished: boolean
@@ -26,7 +23,7 @@ async function process(opts: RunOpts, io: ProtocolIo, request: any) {
const completion = request.completion();
if (requestType.type == 'Http') {
try {
let headers = opts.headers;
let headers = typeof opts.headers === "function" ? opts.headers() : opts.headers;
if (requestType.headers != null && requestType.headers.length > 0) {
headers = { ...opts.headers };
for (let header of requestType.headers) {
@@ -128,11 +125,8 @@ export async function run(opts: RunOpts, io: ProtocolIo, engine: any, generator:
tasks = tasks.filter(t => !t.finished);
}
return generator.take();
}
export class SyncEngineGuards {
waitLock: AsyncLock;
pushLock: AsyncLock;

View File

@@ -1,25 +1,136 @@
export declare const enum DatabaseChangeType {
Insert = 0,
Update = 1,
Delete = 2
Insert = 'insert',
Update = 'update',
Delete = 'delete'
}
export interface DatabaseRowStatement {
/**
* SQL statements with positional placeholders (?)
*/
sql: string
/**
* values to substitute placeholders
*/
values: Array<any>
}
/**
* transformation result:
* - skip: ignore the mutation completely and do not apply it
* - rewrite: replace mutation with the provided statement
* - null: do not change mutation and keep it as is
*/
export type DatabaseRowTransformResult = { operation: 'skip' } | { operation: 'rewrite', stmt: DatabaseRowStatement } | null;
export interface DatabaseRowMutation {
/**
* unix seconds timestamp of the change
*/
changeTime: number
/**
* table name of the change
*/
tableName: string
/**
* rowid of the change
*/
id: number
/**
* type of the change (insert/delete/update)
*/
changeType: DatabaseChangeType
/**
* columns of the row before the change
*/
before?: Record<string, any>
/**
* columns of the row after the change
*/
after?: Record<string, any>
/**
* only updated columns of the row after the change
*/
updates?: Record<string, any>
}
export type DatabaseRowTransformResult = { operation: 'skip' } | { operation: 'rewrite', stmt: DatabaseRowStatement } | null;
export type Transform = (arg: DatabaseRowMutation) => DatabaseRowTransformResult;
export interface DatabaseOpts {
/**
* local path where to store all synced database files (e.g. local.db)
* note, that synced database will write several files with that prefix
* (e.g. local.db-info, local.db-wal, etc)
* */
path: string;
/**
* optional url of the remote database (e.g. libsql://db-org.turso.io)
* (if omitted - local-only database will be created)
*/
url?: string;
/**
* auth token for the remote database
* (can be either static string or function which will provide short-lived credentials for every new request)
*/
authToken?: string | (() => string);
/**
* arbitrary client name which can be used to distinguish clients internally
* the library will gurantee uniquiness of the clientId by appending unique suffix to the clientName
*/
clientName?: string;
/**
* optional key if cloud database were encrypted by default
*/
encryptionKey?: string;
/**
* optional callback which will be called for every mutation before sending it to the remote
* this callback can transform the update in order to support complex conflict resolution strategy
*/
transform?: Transform,
/**
* optional long-polling timeout for pull operation
* if not set - no timeout is applied
*/
longPollTimeoutMs?: number,
/**
* optional parameter to enable internal logging for the database
*/
tracing?: 'error' | 'warn' | 'info' | 'debug' | 'trace',
}
export interface DatabaseStats {
/**
* amount of local changes not sent to the remote
*/
operations: number;
/**
* size of the main WAL file in bytes
*/
mainWal: number;
/**
* size of the revert WAL file in bytes
*/
revertWal: number;
/**
* unix timestamp of last successful pull time
*/
lastPullUnixTime: number;
/**
* unix timestamp of last successful push time
*/
lastPushUnixTime: number | null;
/**
* opaque revision of the changes pulled locally from remote
* (can be used as e-tag, but string must not be interpreted in any way and must be used as opaque value)
*/
revision: string | null;
}
/* internal types used in the native/browser packages */
export interface RunOpts {
preemptionMs: number,
url: string,
headers: { [K: string]: string }
headers: { [K: string]: string } | (() => { [K: string]: string })
transform?: Transform,
}
@@ -27,31 +138,4 @@ export interface ProtocolIo {
read(path: string): Promise<Buffer | Uint8Array | null>;
write(path: string, content: Buffer | Uint8Array): Promise<void>;
}
export interface SyncOpts {
path: string;
clientName?: string;
url: string;
authToken?: string;
encryptionKey?: string;
tablesIgnore?: string[],
transform?: Transform,
longPollTimeoutMs?: number,
tracing?: string,
}
export interface DatabaseRowStatement {
sql: string
values: Array<any>
}
export interface SyncEngineStats {
operations: number;
mainWal: number;
revertWal: number;
lastPullUnixTime: number;
lastPushUnixTime: number | null;
revision: string | null;
}
export type GeneratorResponse = { type: 'IO' } | { type: 'Done' } | ({ type: 'SyncEngineStats' } & SyncEngineStats) | { type: 'SyncEngineChanges', changes: any }
export type GeneratorResponse = { type: 'IO' } | { type: 'Done' } | ({ type: 'SyncEngineStats' } & DatabaseStats) | { type: 'SyncEngineChanges', changes: any }