cry-synced-db-client 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/db/DexieDb.d.ts +32 -0
- package/dist/db/RestProxy.d.ts +109 -0
- package/dist/db/SyncedDb.d.ts +83 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1339 -0
- package/dist/types/CollectionConfig.d.ts +7 -0
- package/dist/types/DbEntity.d.ts +30 -0
- package/dist/types/I_DexieDb.d.ts +47 -0
- package/dist/types/I_InMemDb.d.ts +25 -0
- package/dist/types/I_RestInterface.d.ts +54 -0
- package/dist/types/I_ServerUpdateNotifier.d.ts +29 -0
- package/dist/types/I_SyncedDb.d.ts +149 -0
- package/dist/types/PublishRevsPayload.d.ts +66 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/utils/conflictResolution.d.ts +19 -0
- package/dist/utils/crashRecovery.d.ts +27 -0
- package/dist/utils/localQuery.d.ts +11 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# synced-db-client
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun run index.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This project was created using `bun init` in bun v1.3.5. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Dexie from "dexie";
|
|
2
|
+
import type { I_DexieDb, SyncMeta } from "../types/I_DexieDb";
|
|
3
|
+
import type { CollectionConfig } from "../types/CollectionConfig";
|
|
4
|
+
import type { Id, LocalDbEntity } from "../types/DbEntity";
|
|
5
|
+
/**
|
|
6
|
+
* Implementacija DexieDb interface
|
|
7
|
+
* Uporablja Dexie za dostop do IndexedDB
|
|
8
|
+
*/
|
|
9
|
+
export declare class DexieDb extends Dexie implements I_DexieDb {
|
|
10
|
+
private tenant;
|
|
11
|
+
private collections;
|
|
12
|
+
private syncMeta;
|
|
13
|
+
constructor(tenant: string, collectionConfigs: CollectionConfig<any>[]);
|
|
14
|
+
private getTable;
|
|
15
|
+
private idToString;
|
|
16
|
+
save<T extends LocalDbEntity>(collection: string, id: Id, data: Partial<T>): Promise<void>;
|
|
17
|
+
insert<T extends LocalDbEntity>(collection: string, data: T): Promise<void>;
|
|
18
|
+
saveMany<T extends LocalDbEntity>(collection: string, items: T[]): Promise<void>;
|
|
19
|
+
deleteOne(collection: string, id: Id): Promise<void>;
|
|
20
|
+
saveCollection<T extends LocalDbEntity>(collection: string, data: T[]): Promise<void>;
|
|
21
|
+
deleteCollection(collection: string): Promise<void>;
|
|
22
|
+
getById<T extends LocalDbEntity>(collection: string, id: Id): Promise<T | undefined>;
|
|
23
|
+
getAll<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
24
|
+
count(collection: string): Promise<number>;
|
|
25
|
+
getDirty<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
26
|
+
markDirty(collection: string, id: Id): Promise<void>;
|
|
27
|
+
markClean(collection: string, id: Id): Promise<void>;
|
|
28
|
+
getSyncMeta(collection: string): Promise<SyncMeta | undefined>;
|
|
29
|
+
setSyncMeta(collection: string, lastSyncTs: any): Promise<void>;
|
|
30
|
+
deleteSyncMeta(collection: string): Promise<void>;
|
|
31
|
+
getTenant(): string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Timestamp, AggregateOptions } from "mongodb";
|
|
2
|
+
import type { Id } from "../types/DbEntity";
|
|
3
|
+
import type { I_RestInterface, QuerySpec, QueryOpts, GetNewerSpec, UpdateSpec, InsertSpec, BatchSpec } from "../types/I_RestInterface";
|
|
4
|
+
/** Progress callback for tracking upload progress */
|
|
5
|
+
export type ProgressCallback = (sentBytes: number, totalBytes: number) => void;
|
|
6
|
+
export interface RestProxyConfig {
|
|
7
|
+
/** REST endpoint URL */
|
|
8
|
+
endpoint: string;
|
|
9
|
+
/** Tenant/database name */
|
|
10
|
+
tenant: string;
|
|
11
|
+
/** API key for authentication */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
/** Default timeout in ms (default: 5000) */
|
|
14
|
+
defaultTimeoutMs?: number;
|
|
15
|
+
/** Audit info for requests */
|
|
16
|
+
audit?: {
|
|
17
|
+
user?: string;
|
|
18
|
+
device?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Track request timing (lastRequestMs, totalRequestMs) */
|
|
21
|
+
timeRequests?: boolean;
|
|
22
|
+
/** Track and print request timing to console (implies timeRequests: true) */
|
|
23
|
+
timeRequestsPrint?: boolean;
|
|
24
|
+
/** Callback for upload progress */
|
|
25
|
+
onProgressCallback?: ProgressCallback;
|
|
26
|
+
/** Chunk size for progress notifications in bytes (default: 16KB) */
|
|
27
|
+
progressChunkSize?: number;
|
|
28
|
+
/** Global abort signal - aborts all requests when triggered */
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* REST proxy implementing I_RestInterface
|
|
33
|
+
* Communicates with cry-db server using messagepack encoding
|
|
34
|
+
*
|
|
35
|
+
* ## Browser/Node.js/Bun Compatibility
|
|
36
|
+
*
|
|
37
|
+
* This implementation uses the native `fetch` API which is available in:
|
|
38
|
+
* - All modern browsers
|
|
39
|
+
* - Node.js 18+
|
|
40
|
+
* - Bun
|
|
41
|
+
*
|
|
42
|
+
* ## Performance
|
|
43
|
+
*
|
|
44
|
+
* Benchmarked at ~5ms per request (after warmup) on localhost.
|
|
45
|
+
* Native fetch performs comparably to undici with connection pooling.
|
|
46
|
+
*
|
|
47
|
+
* ## Progress Callback Limitations
|
|
48
|
+
*
|
|
49
|
+
* Native `fetch` API doesn't support upload progress tracking. Progress callbacks
|
|
50
|
+
* are simulated by pre-chunking the buffer before sending. This means progress
|
|
51
|
+
* notifications happen before actual network transmission, not during.
|
|
52
|
+
*
|
|
53
|
+
* For real upload progress in browsers, you would need to use XMLHttpRequest
|
|
54
|
+
* with `upload.onprogress`, but that's not implemented here for simplicity
|
|
55
|
+
* and cross-platform compatibility.
|
|
56
|
+
*
|
|
57
|
+
* ## Dependencies
|
|
58
|
+
*
|
|
59
|
+
* - `notepack.io` - MessagePack encoding/decoding (browser compatible)
|
|
60
|
+
* - `cry-helpers` - Serialization helpers for complex types (Date, ObjectId, etc.)
|
|
61
|
+
*/
|
|
62
|
+
export declare class RestProxy implements I_RestInterface {
|
|
63
|
+
private endpoint;
|
|
64
|
+
private tenant;
|
|
65
|
+
private apiKey?;
|
|
66
|
+
private defaultTimeoutMs;
|
|
67
|
+
private audit;
|
|
68
|
+
private timeRequests;
|
|
69
|
+
private timeRequestsPrint;
|
|
70
|
+
private onProgressCallback?;
|
|
71
|
+
private progressChunkSize;
|
|
72
|
+
private globalSignal?;
|
|
73
|
+
private _lastRequestMs;
|
|
74
|
+
private _totalRequestMs;
|
|
75
|
+
private _requestCount;
|
|
76
|
+
constructor(config: RestProxyConfig);
|
|
77
|
+
/** Set global abort signal */
|
|
78
|
+
setSignal(signal: AbortSignal | undefined): void;
|
|
79
|
+
/** Close - no-op for browser fetch (no connection pool) */
|
|
80
|
+
close(): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Update audit info (e.g., when user logs in)
|
|
83
|
+
*/
|
|
84
|
+
setAudit(audit: {
|
|
85
|
+
user?: string;
|
|
86
|
+
device?: string;
|
|
87
|
+
}): void;
|
|
88
|
+
/** Get last request duration in ms */
|
|
89
|
+
getLastRequestMs(): number;
|
|
90
|
+
/** Get total request duration in ms */
|
|
91
|
+
getTotalRequestMs(): number;
|
|
92
|
+
/** Get total request count */
|
|
93
|
+
getRequestCount(): number;
|
|
94
|
+
/** Reset timing stats */
|
|
95
|
+
resetTimingStats(): void;
|
|
96
|
+
private restCall;
|
|
97
|
+
/** Combine multiple abort signals into one */
|
|
98
|
+
private combineSignals;
|
|
99
|
+
ping(): Promise<boolean>;
|
|
100
|
+
findNewer<T>(collection: string, timestamp: Timestamp | number | string | Date, query?: QuerySpec<T>, opts?: QueryOpts): Promise<T[]>;
|
|
101
|
+
findNewerMany<T>(spec?: GetNewerSpec<T>[]): Promise<Record<string, any[]>>;
|
|
102
|
+
latestTimestamp(collection: string): Promise<Timestamp | undefined>;
|
|
103
|
+
latestTimestamps(collections: string[]): Promise<Record<string, Timestamp>>;
|
|
104
|
+
save<T>(collection: string, update: UpdateSpec<T>, id?: Id): Promise<T>;
|
|
105
|
+
insert<T>(collection: string, insert: InsertSpec<T>): Promise<T>;
|
|
106
|
+
deleteOne<T>(collection: string, query: QuerySpec<T>): Promise<T>;
|
|
107
|
+
aggregate<T>(collection: string, pipeline: object[], opts?: AggregateOptions): Promise<T[]>;
|
|
108
|
+
upsertBatch<T>(collection: string, batch: BatchSpec<T>): Promise<T[]>;
|
|
109
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { AggregateOptions } from "mongodb";
|
|
2
|
+
import type { I_SyncedDb, SyncedDbConfig } from "../types/I_SyncedDb";
|
|
3
|
+
import type { QuerySpec, UpdateSpec, InsertSpec, BatchSpec } from "../types/I_RestInterface";
|
|
4
|
+
import type { Id, DbEntity } from "../types/DbEntity";
|
|
5
|
+
/**
|
|
6
|
+
* Glavna implementacija sinhronizirane baze podatkov
|
|
7
|
+
*/
|
|
8
|
+
export declare class SyncedDb implements I_SyncedDb {
|
|
9
|
+
private tenant;
|
|
10
|
+
private collections;
|
|
11
|
+
private dexieDb;
|
|
12
|
+
private inMemDb;
|
|
13
|
+
private restInterface;
|
|
14
|
+
private serverUpdateNotifier?;
|
|
15
|
+
private restTimeoutMs;
|
|
16
|
+
private syncTimeoutMs;
|
|
17
|
+
private debounceMs;
|
|
18
|
+
private onForcedOffline?;
|
|
19
|
+
private onSync?;
|
|
20
|
+
private online;
|
|
21
|
+
private syncing;
|
|
22
|
+
private syncLock;
|
|
23
|
+
private initialized;
|
|
24
|
+
private pendingChanges;
|
|
25
|
+
private unsubscribeServerUpdates?;
|
|
26
|
+
private beforeUnloadHandler?;
|
|
27
|
+
/** Unikatni ID te instance za detekcijo loopback */
|
|
28
|
+
private readonly updaterId;
|
|
29
|
+
constructor(config: SyncedDbConfig);
|
|
30
|
+
init(): Promise<void>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
isOnline(): boolean;
|
|
33
|
+
setOnline(online: boolean): void;
|
|
34
|
+
/**
|
|
35
|
+
* Prisilno preide v offline stanje in pokliče callback če je nastavljen
|
|
36
|
+
*/
|
|
37
|
+
private goOffline;
|
|
38
|
+
private tryGoOnline;
|
|
39
|
+
/** Ovije promise s timeout za REST operacije */
|
|
40
|
+
private withRestTimeout;
|
|
41
|
+
/** Ovije promise s timeout za sync operacije (daljši timeout) */
|
|
42
|
+
private withSyncTimeout;
|
|
43
|
+
findById<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
44
|
+
findByIds<T extends DbEntity>(collection: string, ids: Id[]): Promise<T[]>;
|
|
45
|
+
findOne<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<T | null>;
|
|
46
|
+
find<T extends DbEntity>(collection: string, query?: QuerySpec<T>): Promise<T[]>;
|
|
47
|
+
aggregate<T>(collection: string, pipeline: object[], opts?: AggregateOptions): Promise<T[]>;
|
|
48
|
+
save<T extends DbEntity>(collection: string, id: Id, update: Partial<T>): Promise<T>;
|
|
49
|
+
upsert<T extends DbEntity>(collection: string, query: QuerySpec<T>, update: UpdateSpec<T>): Promise<T>;
|
|
50
|
+
insert<T extends DbEntity>(collection: string, data: InsertSpec<T>): Promise<T>;
|
|
51
|
+
deleteOne<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
52
|
+
deleteMany<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<number>;
|
|
53
|
+
hardDeleteOne<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
54
|
+
hardDelete<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<number>;
|
|
55
|
+
ping(timeoutMs?: number): Promise<boolean>;
|
|
56
|
+
sync(): Promise<void>;
|
|
57
|
+
isSyncing(): boolean;
|
|
58
|
+
upsertBatch<T extends DbEntity>(collection: string, batch: BatchSpec<T>): Promise<T[]>;
|
|
59
|
+
getMemoryCollection<T extends DbEntity>(collection: string): T[];
|
|
60
|
+
getDebounceMs(): number;
|
|
61
|
+
private assertCollection;
|
|
62
|
+
private stripLocalFields;
|
|
63
|
+
private getPendingKey;
|
|
64
|
+
private schedulePendingChange;
|
|
65
|
+
private executePendingChange;
|
|
66
|
+
private flushAllPendingChanges;
|
|
67
|
+
private recoverPendingWrites;
|
|
68
|
+
/**
|
|
69
|
+
* Sinhronizira eno samo kolekcijo - uporabi se pri handleServerUpdate za updateMany/deleteMany
|
|
70
|
+
*/
|
|
71
|
+
private syncSingleCollection;
|
|
72
|
+
/**
|
|
73
|
+
* Sinhronizira kolekcijo s podatki, ki so že bili pridobljeni s serverja
|
|
74
|
+
* Vrne statistiko: { conflictsResolved, sentCount }
|
|
75
|
+
*/
|
|
76
|
+
private syncCollectionWithData;
|
|
77
|
+
private resolveCollectionConflict;
|
|
78
|
+
private compareTimestamps;
|
|
79
|
+
private handleServerUpdate;
|
|
80
|
+
private handleServerItemUpdate;
|
|
81
|
+
private getNewFieldsFromServer;
|
|
82
|
+
private handleServerItemDelete;
|
|
83
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Dexie from "dexie";
|
|
2
|
+
export { Dexie };
|
|
3
|
+
export * from "./types";
|
|
4
|
+
export { SyncedDb } from "./db/SyncedDb";
|
|
5
|
+
export { DexieDb } from "./db/DexieDb";
|
|
6
|
+
export { RestProxy } from "./db/RestProxy";
|
|
7
|
+
export type { RestProxyConfig } from "./db/RestProxy";
|
|
8
|
+
export { resolveConflict } from "./utils/conflictResolution";
|
|
9
|
+
export { filterByQuery, matchesQuery } from "./utils/localQuery";
|