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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ObjectId, Timestamp } from "mongodb";
|
|
2
|
+
export type Id = ObjectId | string | number;
|
|
3
|
+
export type Entity = {
|
|
4
|
+
_id: Id;
|
|
5
|
+
};
|
|
6
|
+
export type IdOrEntity = ObjectId | string | number | Entity;
|
|
7
|
+
/**
|
|
8
|
+
* Osnovni objekt v nekem collection (serverski tip)
|
|
9
|
+
* Vsebuje sistemska polja za sinhronizacijo
|
|
10
|
+
*/
|
|
11
|
+
export interface DbEntity {
|
|
12
|
+
_id: Id;
|
|
13
|
+
_rev?: number;
|
|
14
|
+
_ts?: Timestamp;
|
|
15
|
+
_csq?: number;
|
|
16
|
+
_deleted?: Date;
|
|
17
|
+
_blocked?: boolean;
|
|
18
|
+
/** ID zadnjega updaterja - za detekcijo loopback */
|
|
19
|
+
_lastUpdaterId?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Razširitev DbEntity z lokalnimi polji za sync tracking
|
|
23
|
+
* Uporablja se v dexie bazi
|
|
24
|
+
*/
|
|
25
|
+
export interface LocalDbEntity extends DbEntity {
|
|
26
|
+
/** Označuje, da ima objekt lokalne spremembe, ki čakajo na sync */
|
|
27
|
+
_dirty?: boolean;
|
|
28
|
+
/** Čas zadnje lokalne spremembe */
|
|
29
|
+
_localChangedAt?: Date;
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Id, LocalDbEntity } from "./DbEntity";
|
|
2
|
+
/**
|
|
3
|
+
* Metapodatki o sinhronizaciji za vsako kolekcijo
|
|
4
|
+
*/
|
|
5
|
+
export interface SyncMeta {
|
|
6
|
+
tenant: string;
|
|
7
|
+
collection: string;
|
|
8
|
+
lastSyncTs?: any;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Interface za Dexie (IndexedDB) bazo podatkov
|
|
12
|
+
* Vse metode so async ker IndexedDB je asinhrona
|
|
13
|
+
*/
|
|
14
|
+
export interface I_DexieDb {
|
|
15
|
+
/** Shrani/posodobi objekt v kolekciji */
|
|
16
|
+
save<T extends LocalDbEntity>(collection: string, id: Id, data: Partial<T>): Promise<void>;
|
|
17
|
+
/** Shrani/posodobi več objektov naenkrat (bulk upsert) */
|
|
18
|
+
saveMany<T extends LocalDbEntity>(collection: string, items: T[]): Promise<void>;
|
|
19
|
+
/** Vstavi nov objekt v kolekcijo */
|
|
20
|
+
insert<T extends LocalDbEntity>(collection: string, data: T): Promise<void>;
|
|
21
|
+
/** Izbriše objekt iz kolekcije */
|
|
22
|
+
deleteOne(collection: string, id: Id): Promise<void>;
|
|
23
|
+
/** Shrani celotno kolekcijo (nadomesti obstoječo) */
|
|
24
|
+
saveCollection<T extends LocalDbEntity>(collection: string, data: T[]): Promise<void>;
|
|
25
|
+
/** Izbriše celotno kolekcijo */
|
|
26
|
+
deleteCollection(collection: string): Promise<void>;
|
|
27
|
+
/** Vrne objekt po ID-ju */
|
|
28
|
+
getById<T extends LocalDbEntity>(collection: string, id: Id): Promise<T | undefined>;
|
|
29
|
+
/** Vrne vse objekte v kolekciji */
|
|
30
|
+
getAll<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
31
|
+
/** Vrne število objektov v kolekciji */
|
|
32
|
+
count(collection: string): Promise<number>;
|
|
33
|
+
/** Vrne vse dirty objekte (z lokalnimi spremembami) */
|
|
34
|
+
getDirty<T extends LocalDbEntity>(collection: string): Promise<T[]>;
|
|
35
|
+
/** Označi objekt kot umazan (ima lokalne spremembe) */
|
|
36
|
+
markDirty(collection: string, id: Id): Promise<void>;
|
|
37
|
+
/** Označi objekt kot čist (sinhroniziran s serverjem) */
|
|
38
|
+
markClean(collection: string, id: Id): Promise<void>;
|
|
39
|
+
/** Vrne sync metapodatke za kolekcijo */
|
|
40
|
+
getSyncMeta(collection: string): Promise<SyncMeta | undefined>;
|
|
41
|
+
/** Nastavi sync metapodatke za kolekcijo */
|
|
42
|
+
setSyncMeta(collection: string, lastSyncTs: any): Promise<void>;
|
|
43
|
+
/** Izbriše sync metapodatke za kolekcijo */
|
|
44
|
+
deleteSyncMeta(collection: string): Promise<void>;
|
|
45
|
+
/** Vrne tenant */
|
|
46
|
+
getTenant(): string;
|
|
47
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Id, DbEntity } from "./DbEntity";
|
|
2
|
+
/**
|
|
3
|
+
* Interface za in-memory bazo podatkov
|
|
4
|
+
* UI samo bere iz te baze, posodablja jo samo sync-db
|
|
5
|
+
*/
|
|
6
|
+
export interface I_InMemDb {
|
|
7
|
+
/** Shrani/posodobi objekt v kolekciji */
|
|
8
|
+
save<T extends DbEntity>(collection: string, id: Id, data: Partial<T>): void;
|
|
9
|
+
/** Shrani/posodobi več objektov naenkrat (bulk upsert) */
|
|
10
|
+
saveMany<T extends DbEntity>(collection: string, items: T[]): void;
|
|
11
|
+
/** Vstavi nov objekt v kolekcijo */
|
|
12
|
+
insert<T extends DbEntity>(collection: string, data: T): void;
|
|
13
|
+
/** Izbriše objekt iz kolekcije */
|
|
14
|
+
deleteOne(collection: string, id: Id): void;
|
|
15
|
+
/** Shrani celotno kolekcijo (nadomesti obstoječo) */
|
|
16
|
+
saveCollection<T extends DbEntity>(collection: string, data: T[]): void;
|
|
17
|
+
/** Izbriše celotno kolekcijo */
|
|
18
|
+
deleteCollection(collection: string): void;
|
|
19
|
+
/** Vrne objekt po ID-ju */
|
|
20
|
+
getById<T extends DbEntity>(collection: string, id: Id): T | undefined;
|
|
21
|
+
/** Vrne vse objekte v kolekciji */
|
|
22
|
+
getAll<T extends DbEntity>(collection: string): T[];
|
|
23
|
+
/** Vrne število objektov v kolekciji */
|
|
24
|
+
count(collection: string): number;
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Timestamp, Document, SchemaMember, CollationOptions, ReadPreference, FindOneAndUpdateOptions, AggregateOptions } from "mongodb";
|
|
2
|
+
import type { Id } from "./DbEntity";
|
|
3
|
+
export type Obj = {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
};
|
|
6
|
+
export type QuerySpec<T> = Partial<Record<keyof T | "_deleted" | "_blocked" | "_ts" | "_id", any>>;
|
|
7
|
+
export type Projection = SchemaMember<any, Document | number | boolean | any>;
|
|
8
|
+
export type QueryOpts = Partial<{
|
|
9
|
+
project: Projection;
|
|
10
|
+
sort: Record<string, -1 | 1>;
|
|
11
|
+
limit: number;
|
|
12
|
+
skip: number;
|
|
13
|
+
collation: CollationOptions;
|
|
14
|
+
readPreference: ReadPreference;
|
|
15
|
+
/** Če je true, vrne tudi soft-deleted objekte (z _deleted poljem) */
|
|
16
|
+
returnDeleted: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
export type KeyOf<T> = keyof T | "$bit" | "$set" | "$inc" | "$currentDate" | "$min" | "$max" | "$mul" | "$rename" | "$setOnInsert" | "$unset" | "$pull" | "$push" | "$pop" | "$addToSet" | "$pushAll" | "_rev" | "_ts" | "_csq" | "_deleted";
|
|
19
|
+
export type InsertKeyOf<T> = keyof T | "_rev" | "_ts" | "_csq" | "_deleted";
|
|
20
|
+
export type InsertSpec<T> = Partial<Record<InsertKeyOf<T>, any>>;
|
|
21
|
+
export type UpdateSpec<T> = Partial<Record<KeyOf<T>, any>>;
|
|
22
|
+
export type BatchSpec<T> = {
|
|
23
|
+
query: QuerySpec<T>;
|
|
24
|
+
update: UpdateSpec<T>;
|
|
25
|
+
opts?: UpsertOptions;
|
|
26
|
+
}[];
|
|
27
|
+
export interface UpsertOptions extends FindOneAndUpdateOptions {
|
|
28
|
+
returnFullObject?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface GetNewerSpec<T> {
|
|
31
|
+
collection: string;
|
|
32
|
+
timestamp: number | Date | string | Timestamp;
|
|
33
|
+
query?: QuerySpec<T>;
|
|
34
|
+
opts?: QueryOpts;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Minimalni interface za komunikacijo s serverjem (cry-db Mongo)
|
|
38
|
+
* Vsebuje samo metode, potrebne za sinhronizacijo
|
|
39
|
+
*/
|
|
40
|
+
export interface I_RestInterface {
|
|
41
|
+
/** Preveri povezljivost s serverjem - vrne true če je server dosegljiv */
|
|
42
|
+
ping(): Promise<boolean>;
|
|
43
|
+
findNewer<T>(collection: string, timestamp: Timestamp | number | string | Date, query?: QuerySpec<T>, opts?: QueryOpts): Promise<T[]>;
|
|
44
|
+
findNewerMany<T>(spec?: GetNewerSpec<T>[]): Promise<Record<string, any[]>>;
|
|
45
|
+
latestTimestamp(collection: string): Promise<Timestamp | undefined>;
|
|
46
|
+
latestTimestamps(collections: string[]): Promise<Record<string, Timestamp>>;
|
|
47
|
+
save<T>(collection: string, update: UpdateSpec<T>, id?: Id): Promise<T>;
|
|
48
|
+
insert<T>(collection: string, insert: InsertSpec<T>): Promise<T>;
|
|
49
|
+
deleteOne<T>(collection: string, query: QuerySpec<T>): Promise<T>;
|
|
50
|
+
/** Izvede agregacijo na serverju */
|
|
51
|
+
aggregate<T>(collection: string, pipeline: object[], opts?: AggregateOptions): Promise<T[]>;
|
|
52
|
+
/** Izvede batch upsert na serverju - ne posodablja lokalne baze */
|
|
53
|
+
upsertBatch<T>(collection: string, batch: BatchSpec<T>): Promise<T[]>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PublishRevsPayload } from "./PublishRevsPayload";
|
|
2
|
+
/**
|
|
3
|
+
* Callback za prejemanje server update notifikacij
|
|
4
|
+
*/
|
|
5
|
+
export type ServerUpdateCallback = (payload: PublishRevsPayload) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Interface za prejemanje notifikacij o spremembah na serverju
|
|
8
|
+
* Implementacija je odvisna od transporta (WebSocket, SSE, polling, etc.)
|
|
9
|
+
*/
|
|
10
|
+
export interface I_ServerUpdateNotifier {
|
|
11
|
+
/**
|
|
12
|
+
* Registriraj callback za prejemanje update notifikacij
|
|
13
|
+
* @param callback Funkcija, ki se pokliče ob vsaki notifikaciji
|
|
14
|
+
* @returns Funkcija za odstranitev callbacka (unsubscribe)
|
|
15
|
+
*/
|
|
16
|
+
subscribe(callback: ServerUpdateCallback): () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Začni poslušati za notifikacije
|
|
19
|
+
*/
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Prenehaj poslušati za notifikacije
|
|
23
|
+
*/
|
|
24
|
+
disconnect(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Ali je povezava aktivna
|
|
27
|
+
*/
|
|
28
|
+
isConnected(): boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { AggregateOptions } from "mongodb";
|
|
2
|
+
import type { Id, DbEntity } from "./DbEntity";
|
|
3
|
+
import type { QuerySpec, UpdateSpec, InsertSpec, BatchSpec, I_RestInterface } from "./I_RestInterface";
|
|
4
|
+
import type { I_DexieDb } from "./I_DexieDb";
|
|
5
|
+
import type { I_InMemDb } from "./I_InMemDb";
|
|
6
|
+
import type { I_ServerUpdateNotifier } from "./I_ServerUpdateNotifier";
|
|
7
|
+
/**
|
|
8
|
+
* Informacije o sinhronizaciji za debugging/logging
|
|
9
|
+
*/
|
|
10
|
+
export interface SyncInfo {
|
|
11
|
+
/** Trajanje sinhronizacije v ms */
|
|
12
|
+
durationMs: number;
|
|
13
|
+
/** Število prejetih objektov s serverja */
|
|
14
|
+
receivedCount: number;
|
|
15
|
+
/** Število poslanih dirty objektov na server */
|
|
16
|
+
sentCount: number;
|
|
17
|
+
/** Število konfliktov, ki so bili razrešeni */
|
|
18
|
+
conflictsResolved: number;
|
|
19
|
+
/** Ali je sync uspel */
|
|
20
|
+
success: boolean;
|
|
21
|
+
/** Napaka, če sync ni uspel */
|
|
22
|
+
error?: Error;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Konfiguracija za posamezno kolekcijo
|
|
26
|
+
*/
|
|
27
|
+
export interface CollectionConfig {
|
|
28
|
+
/** Ime kolekcije */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Opcijski filter za velike tabele - omeji obseg sinhronizacije */
|
|
31
|
+
query?: QuerySpec<any>;
|
|
32
|
+
/** Dodatni indexi poleg _id */
|
|
33
|
+
indexes?: string[];
|
|
34
|
+
/** Opcijska funkcija za razreševanje konfliktov */
|
|
35
|
+
resolveSyncConflict?<T extends DbEntity>(local: T, external: T): T;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Konfiguracija za SyncedDb - dependencies so podani kot parametri (DI)
|
|
39
|
+
*/
|
|
40
|
+
export interface SyncedDbConfig {
|
|
41
|
+
/** Identifikator tenanta */
|
|
42
|
+
tenant: string;
|
|
43
|
+
/** Seznam kolekcij za sinhronizacijo */
|
|
44
|
+
collections: CollectionConfig[];
|
|
45
|
+
/** Dexie (IndexedDB) baza - dependency injection */
|
|
46
|
+
dexieDb: I_DexieDb;
|
|
47
|
+
/** In-memory baza - dependency injection */
|
|
48
|
+
inMemDb: I_InMemDb;
|
|
49
|
+
/** REST interface za komunikacijo s serverjem - dependency injection */
|
|
50
|
+
restInterface: I_RestInterface;
|
|
51
|
+
/** Server update notifier - dependency injection (opcijsko) */
|
|
52
|
+
serverUpdateNotifier?: I_ServerUpdateNotifier;
|
|
53
|
+
/** Timeout za splošne REST klice v ms (default: 10000) */
|
|
54
|
+
restTimeoutMs?: number;
|
|
55
|
+
/** Timeout za sync REST klice v ms (default: 30000) - daljši ker sync prenaša več podatkov */
|
|
56
|
+
syncTimeoutMs?: number;
|
|
57
|
+
/** Debounce čas za zapis v Dexie v ms (default: 1000) */
|
|
58
|
+
debounceMs?: number;
|
|
59
|
+
/** Callback ki se pokliče, ko SyncedDb sam preide v offline stanje (npr. ob sync napaki) */
|
|
60
|
+
onForcedOffline?: (reason: string) => void;
|
|
61
|
+
/** Callback za debugging/logging - pokliče se po vsaki sinhronizaciji */
|
|
62
|
+
onSync?: (info: SyncInfo) => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Glavna logika za sinhronizirano bazo podatkov
|
|
66
|
+
*
|
|
67
|
+
* - Deluje offline-first, kjer so offline podatki v Dexie (IndexedDB)
|
|
68
|
+
* - Vzdržuje in-mem-db sinhroniziran z Dexie
|
|
69
|
+
* - Spremlja server updates in posodablja lokalno bazo
|
|
70
|
+
* - Ko preide v online stanje, sinhronizira s serverjem
|
|
71
|
+
*/
|
|
72
|
+
export interface I_SyncedDb {
|
|
73
|
+
/** Inicializira bazo in izvede začetno sinhronizacijo */
|
|
74
|
+
init(): Promise<void>;
|
|
75
|
+
/** Zapre bazo in počisti resurse */
|
|
76
|
+
close(): Promise<void>;
|
|
77
|
+
/** Ali je povezan na server */
|
|
78
|
+
isOnline(): boolean;
|
|
79
|
+
/** Nastavi online/offline status */
|
|
80
|
+
setOnline(online: boolean): void;
|
|
81
|
+
/** Poišče objekt po ID-ju */
|
|
82
|
+
findById<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
83
|
+
/** Poišče objekte po ID-jih */
|
|
84
|
+
findByIds<T extends DbEntity>(collection: string, ids: Id[]): Promise<T[]>;
|
|
85
|
+
/** Poišče prvi objekt, ki ustreza poizvedbi */
|
|
86
|
+
findOne<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<T | null>;
|
|
87
|
+
/** Poišče vse objekte, ki ustrezajo poizvedbi */
|
|
88
|
+
find<T extends DbEntity>(collection: string, query?: QuerySpec<T>): Promise<T[]>;
|
|
89
|
+
/** Izvede agregacijo na serverju (offline vrne []) */
|
|
90
|
+
aggregate<T>(collection: string, pipeline: object[], opts?: AggregateOptions): Promise<T[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Posodobi objekt - doda spremembe na objekt
|
|
93
|
+
* Če objekt ne obstaja, ga ustvari in izpiše warning
|
|
94
|
+
*/
|
|
95
|
+
save<T extends DbEntity>(collection: string, id: Id, update: Partial<T>): Promise<T>;
|
|
96
|
+
/**
|
|
97
|
+
* Posodobi objekt po poizvedbi, če je treba ga ustvari
|
|
98
|
+
*/
|
|
99
|
+
upsert<T extends DbEntity>(collection: string, query: QuerySpec<T>, update: UpdateSpec<T>): Promise<T>;
|
|
100
|
+
/**
|
|
101
|
+
* Vstavi nov objekt
|
|
102
|
+
* Če objekt že obstaja, ga prepiše in izpiše warning
|
|
103
|
+
*/
|
|
104
|
+
insert<T extends DbEntity>(collection: string, data: InsertSpec<T>): Promise<T>;
|
|
105
|
+
/**
|
|
106
|
+
* Izbriše objekt (soft delete)
|
|
107
|
+
* - Nastavi _deleted = new Date()
|
|
108
|
+
* - Objekt izbriše iz in-mem-db
|
|
109
|
+
* - Pusti ga v dexie do sinhronizacije s serverjem
|
|
110
|
+
*/
|
|
111
|
+
deleteOne<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
112
|
+
/**
|
|
113
|
+
* Izbriše več objektov po poizvedbi (soft delete)
|
|
114
|
+
*/
|
|
115
|
+
deleteMany<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<number>;
|
|
116
|
+
/**
|
|
117
|
+
* Trajno izbriše objekt po ID-ju
|
|
118
|
+
* - Deluje samo online, offline vrže napako
|
|
119
|
+
* - Izbriše iz serverja, dexie in in-mem
|
|
120
|
+
*/
|
|
121
|
+
hardDeleteOne<T extends DbEntity>(collection: string, id: Id): Promise<T | null>;
|
|
122
|
+
/**
|
|
123
|
+
* Trajno izbriše več objektov po poizvedbi
|
|
124
|
+
* - Deluje samo online, offline vrže napako
|
|
125
|
+
* - Izbriše iz serverja, dexie in in-mem
|
|
126
|
+
*/
|
|
127
|
+
hardDelete<T extends DbEntity>(collection: string, query: QuerySpec<T>): Promise<number>;
|
|
128
|
+
/**
|
|
129
|
+
* Preveri povezljivost s serverjem
|
|
130
|
+
* @param timeoutMs Timeout v milisekundah
|
|
131
|
+
* @returns true če je server dosegljiv, false sicer
|
|
132
|
+
*/
|
|
133
|
+
ping(timeoutMs?: number): Promise<boolean>;
|
|
134
|
+
/** Sproži sinhronizacijo s serverjem */
|
|
135
|
+
sync(): Promise<void>;
|
|
136
|
+
/** Ali je sinhronizacija v teku */
|
|
137
|
+
isSyncing(): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Izvede batch upsert na serverju
|
|
140
|
+
* - Deluje samo online, offline vrže napako
|
|
141
|
+
* - Ne posodablja dexie ali in-mem baze
|
|
142
|
+
* - Uporabljeno za operacije, ki ne potrebujejo lokalne sinhronizacije
|
|
143
|
+
*/
|
|
144
|
+
upsertBatch<T extends DbEntity>(collection: string, batch: BatchSpec<T>): Promise<T[]>;
|
|
145
|
+
/** Vrne vse objekte iz in-mem baze za dano kolekcijo */
|
|
146
|
+
getMemoryCollection<T extends DbEntity>(collection: string): T[];
|
|
147
|
+
/** Vrne konfigurirani debounce čas v ms */
|
|
148
|
+
getDebounceMs(): number;
|
|
149
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Timestamp } from "mongodb";
|
|
2
|
+
import type { Id } from "./DbEntity";
|
|
3
|
+
export type PublishableOperation = 'insert' | 'update' | 'updateMany' | 'deleteMany' | 'delete' | 'batch';
|
|
4
|
+
type PublishRevsPayloadBase = {
|
|
5
|
+
db: string;
|
|
6
|
+
collection: string;
|
|
7
|
+
};
|
|
8
|
+
export type PublishRevsPayloadInsert = PublishRevsPayloadBase & {
|
|
9
|
+
operation: 'insert';
|
|
10
|
+
_id: Id;
|
|
11
|
+
_ts: Timestamp;
|
|
12
|
+
_rev: number;
|
|
13
|
+
enquireLastTs?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type PublishRevsPayloadUpdate = PublishRevsPayloadBase & {
|
|
16
|
+
operation: 'update';
|
|
17
|
+
_id: Id;
|
|
18
|
+
_ts: Timestamp;
|
|
19
|
+
_rev: number;
|
|
20
|
+
enquireLastTs?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type PublishRevsPayloadDelete = PublishRevsPayloadBase & {
|
|
23
|
+
operation: 'delete';
|
|
24
|
+
_id: Id;
|
|
25
|
+
_ts: Timestamp;
|
|
26
|
+
_rev: number;
|
|
27
|
+
enquireLastTs?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type PublishRevsPayloadUpdateMany = PublishRevsPayloadBase & {
|
|
30
|
+
operation: 'updateMany';
|
|
31
|
+
enquireLastTs: true;
|
|
32
|
+
};
|
|
33
|
+
export type PublishRevsPayloadDeleteMany = PublishRevsPayloadBase & {
|
|
34
|
+
operation: 'deleteMany';
|
|
35
|
+
enquireLastTs: true;
|
|
36
|
+
};
|
|
37
|
+
export type PublishRevsPayloadBatchItem = {
|
|
38
|
+
operation: PublishableOperation;
|
|
39
|
+
_id: Id;
|
|
40
|
+
_ts: Timestamp;
|
|
41
|
+
};
|
|
42
|
+
export type PublishRevsPayloadBatch = PublishRevsPayloadBase & {
|
|
43
|
+
operation: 'batch';
|
|
44
|
+
data: PublishRevsPayloadBatchItem[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Notifikacija serverja, da je bil objekt spremenjen
|
|
48
|
+
*/
|
|
49
|
+
export type PublishRevsPayload = PublishRevsPayloadInsert | PublishRevsPayloadUpdate | PublishRevsPayloadDelete | PublishRevsPayloadUpdateMany | PublishRevsPayloadDeleteMany | PublishRevsPayloadBatch;
|
|
50
|
+
export type PublishRevsSpec = {
|
|
51
|
+
channel: string;
|
|
52
|
+
payload: PublishRevsPayload;
|
|
53
|
+
user?: string;
|
|
54
|
+
};
|
|
55
|
+
export type PublishDataSpec = {
|
|
56
|
+
channel: string;
|
|
57
|
+
payload: {
|
|
58
|
+
db: string;
|
|
59
|
+
collection: string;
|
|
60
|
+
operation: PublishableOperation;
|
|
61
|
+
data: any;
|
|
62
|
+
};
|
|
63
|
+
user?: string;
|
|
64
|
+
};
|
|
65
|
+
export type PublishSpec = PublishDataSpec | PublishRevsSpec;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { Id, Entity, IdOrEntity, DbEntity, LocalDbEntity } from "./DbEntity";
|
|
2
|
+
export type { PublishableOperation, PublishRevsPayloadInsert, PublishRevsPayloadUpdate, PublishRevsPayloadDelete, PublishRevsPayloadUpdateMany, PublishRevsPayloadDeleteMany, PublishRevsPayloadBatchItem, PublishRevsPayloadBatch, PublishRevsPayload, PublishRevsSpec, PublishDataSpec, PublishSpec, } from "./PublishRevsPayload";
|
|
3
|
+
export type { Obj, QuerySpec, Projection, QueryOpts, KeyOf, InsertKeyOf, InsertSpec, UpdateSpec, BatchSpec, UpsertOptions, GetNewerSpec, I_RestInterface as RestInterface, } from "./I_RestInterface";
|
|
4
|
+
export type { I_InMemDb as InMemDb } from "./I_InMemDb";
|
|
5
|
+
export type { I_DexieDb as DexieDb, SyncMeta } from "./I_DexieDb";
|
|
6
|
+
export type { I_ServerUpdateNotifier as ServerUpdateNotifier, ServerUpdateCallback } from "./I_ServerUpdateNotifier";
|
|
7
|
+
export type { I_SyncedDb as SyncedDb, SyncedDbConfig, CollectionConfig, SyncInfo } from "./I_SyncedDb";
|
|
8
|
+
export type { CollectionConfig as CollectionConfigFull } from "./CollectionConfig";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DbEntity } from "../types/DbEntity";
|
|
2
|
+
/**
|
|
3
|
+
* Razreši konflikt med lokalnim in serverskim objektom po SPEC logiki:
|
|
4
|
+
*
|
|
5
|
+
* 1. Če ima lokalni objekt _rev in ima objekt s serverja nižji ali isti _rev, ignoriramo server
|
|
6
|
+
* 2. Polja, ki niso array in niso object:
|
|
7
|
+
* - če lokalna verzija nima polja, zunanja pa ga ima, polje dodamo
|
|
8
|
+
* - če imata oba objekta polje, vrednost s serverja zavržemo
|
|
9
|
+
* 3. Polja tipa array:
|
|
10
|
+
* - če lokalni objekt nima polja, ga dodamo
|
|
11
|
+
* - če je tip podatkov string[], shranimo deduplicirano unijo
|
|
12
|
+
* - če je tip podatkov Object[]:
|
|
13
|
+
* - elemente brez _id dodamo v lokalni array
|
|
14
|
+
* - objekte z _id primerjamo rekurzivno
|
|
15
|
+
* 4. Če je tip podatkov Record<string, Object>:
|
|
16
|
+
* - za vsak key, ki ga ni v lokalnem objektu, dodamo vrednost
|
|
17
|
+
* - za vsak key, ki obstaja, primerjamo rekurzivno
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveConflict<T extends DbEntity>(local: T, external: T): T;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LocalDbEntity, Id } from "../types/DbEntity";
|
|
2
|
+
interface PendingWrite {
|
|
3
|
+
tenant: string;
|
|
4
|
+
collection: string;
|
|
5
|
+
id: string;
|
|
6
|
+
data: LocalDbEntity;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Shrani pending write v localStorage pred zapisom v Dexie
|
|
11
|
+
* Zagotavlja crash resilience - če se tab zapre ali crashne med debounce
|
|
12
|
+
*/
|
|
13
|
+
export declare function savePendingWrite(tenant: string, collection: string, id: Id, data: LocalDbEntity): void;
|
|
14
|
+
/**
|
|
15
|
+
* Odstrani pending write iz localStorage po uspešnem zapisu v Dexie
|
|
16
|
+
*/
|
|
17
|
+
export declare function clearPendingWrite(tenant: string, collection: string, id: Id): void;
|
|
18
|
+
/**
|
|
19
|
+
* Pridobi vse pending writes za danega tenanta
|
|
20
|
+
* Pokliče se ob inicializaciji za recovery
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPendingWrites(tenant: string): PendingWrite[];
|
|
23
|
+
/**
|
|
24
|
+
* Počisti vse pending writes za danega tenanta
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearAllPendingWrites(tenant: string): void;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { QuerySpec } from "../types/I_RestInterface";
|
|
2
|
+
import type { DbEntity } from "../types/DbEntity";
|
|
3
|
+
/**
|
|
4
|
+
* Preveri, ali objekt ustreza MongoDB-style query specifikaciji
|
|
5
|
+
* Podpira osnovne operatorje: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex
|
|
6
|
+
*/
|
|
7
|
+
export declare function matchesQuery<T extends DbEntity>(item: T, query: QuerySpec<T>): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Filtriraj array objektov po query
|
|
10
|
+
*/
|
|
11
|
+
export declare function filterByQuery<T extends DbEntity>(items: T[], query?: QuerySpec<T>): T[];
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cry-synced-db-client",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"build": "bun run clean && bun run build:js && bun run build:types",
|
|
20
|
+
"build:js": "bun build ./src/index.ts --outdir ./dist --target browser --format esm --external dexie --external bson --external cry-helpers --external notepack.io",
|
|
21
|
+
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
22
|
+
"test": "bun test",
|
|
23
|
+
"prepublishOnly": "bun run build"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/bun": "latest",
|
|
27
|
+
"bson": "^7.0.0",
|
|
28
|
+
"cry-db": "^2.4.11",
|
|
29
|
+
"dexie": "^4.2.1",
|
|
30
|
+
"fake-indexeddb": "^6.2.5",
|
|
31
|
+
"typescript": "^5"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"cry-helpers": "^2.1.180",
|
|
35
|
+
"notepack": "^0.0.2",
|
|
36
|
+
"notepack.io": "^3.0.1",
|
|
37
|
+
"undici": "^7.18.2"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"bson": "^6.0.0 || ^7.0.0",
|
|
41
|
+
"dexie": "^4.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|