cry-synced-db-client 0.1.137 → 0.1.139

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Versions
2
2
 
3
+ ## Unreleased
4
+
5
+ - Add `refreshInBackground` `QueryOpts` option for `findById` / `findByIds`
6
+ - Stale-while-revalidate: cache-hit returns local result immediately and
7
+ triggers a background fetch that updates Dexie + in-mem through conflict
8
+ resolution (`processCollectionServerData`).
9
+ - Orthogonal to `referToServer` — does not change miss behaviour. With
10
+ defaults (`referToServer: true`) misses are still awaited; with
11
+ `referToServer: false` misses return `null` and bg fetch loads them async.
12
+ - Dedupes against `referToServer`: IDs fetched blockingly are NOT re-fetched
13
+ in the background (no double-round-trip).
14
+ - Noop when offline or on writeOnly collections.
15
+ - Ignored on `find` / `findOne` (use `referToServer` there).
16
+
3
17
  ## 0.1.136 (2026-04-20)
4
18
 
5
19
  - `DexieDb.saveMany` is now fail-safe:
package/dist/index.js CHANGED
@@ -3110,6 +3110,7 @@ var ServerUpdateHandler = class {
3110
3110
  if (updates.length > 0) {
3111
3111
  const updateIds = updates.map((u) => u._id);
3112
3112
  const localItems = await this.dexieDb.getByIds(collectionName, updateIds);
3113
+ const missingIds = [];
3113
3114
  for (let i = 0; i < updates.length; i++) {
3114
3115
  const deltaData = updates[i];
3115
3116
  const localItem = localItems[i];
@@ -3117,14 +3118,18 @@ var ServerUpdateHandler = class {
3117
3118
  await this.handleServerItemUpdate(collectionName, localItem, deltaData);
3118
3119
  updatedIds.push(String(deltaData._id));
3119
3120
  } else {
3120
- const fullItem = await this.restInterface.findById(
3121
- collectionName,
3122
- deltaData._id
3123
- );
3124
- if (fullItem) {
3125
- await this.handleServerItemInsert(collectionName, fullItem);
3126
- updatedIds.push(String(fullItem._id));
3127
- }
3121
+ missingIds.push(deltaData._id);
3122
+ }
3123
+ }
3124
+ if (missingIds.length > 0) {
3125
+ const fullItems = await this.restInterface.findByIds(
3126
+ collectionName,
3127
+ missingIds
3128
+ );
3129
+ for (const fullItem of fullItems) {
3130
+ if (!fullItem) continue;
3131
+ await this.handleServerItemInsert(collectionName, fullItem);
3132
+ updatedIds.push(String(fullItem._id));
3128
3133
  }
3129
3134
  }
3130
3135
  }
@@ -3926,7 +3931,12 @@ var _SyncedDb = class _SyncedDb {
3926
3931
  }
3927
3932
  if (!(opts == null ? void 0 : opts.returnDeleted) && !(opts == null ? void 0 : opts.returnArchived)) {
3928
3933
  const memItem = this.inMemDb.getById(collection, id);
3929
- if (memItem) return memItem;
3934
+ if (memItem) {
3935
+ if ((opts == null ? void 0 : opts.refreshInBackground) && this.isOnline()) {
3936
+ this.refreshByIdsInBackground(collection, [id]);
3937
+ }
3938
+ return memItem;
3939
+ }
3930
3940
  }
3931
3941
  if ((opts == null ? void 0 : opts.referToServer) !== false && this.isOnline()) {
3932
3942
  await this.ensureItemsAreLoaded(
@@ -3934,6 +3944,8 @@ var _SyncedDb = class _SyncedDb {
3934
3944
  [id],
3935
3945
  opts == null ? void 0 : opts.returnDeleted
3936
3946
  );
3947
+ } else if ((opts == null ? void 0 : opts.refreshInBackground) && this.isOnline()) {
3948
+ this.refreshByIdsInBackground(collection, [id]);
3937
3949
  }
3938
3950
  if (((opts == null ? void 0 : opts.returnDeleted) || (opts == null ? void 0 : opts.returnArchived)) && this.isOnline()) {
3939
3951
  try {
@@ -3970,12 +3982,28 @@ var _SyncedDb = class _SyncedDb {
3970
3982
  return this.writeOnlyFindByIds(collection, ids);
3971
3983
  }
3972
3984
  if (ids.length === 0) return [];
3973
- if ((opts == null ? void 0 : opts.referToServer) !== false && this.isOnline()) {
3985
+ const wantsBgRefresh = (opts == null ? void 0 : opts.refreshInBackground) === true && this.isOnline();
3986
+ const referToServerEnabled = (opts == null ? void 0 : opts.referToServer) !== false && this.isOnline();
3987
+ let hitIds = null;
3988
+ if (wantsBgRefresh && referToServerEnabled && !(opts == null ? void 0 : opts.returnDeleted) && !(opts == null ? void 0 : opts.returnArchived)) {
3989
+ hitIds = [];
3990
+ for (const id of ids) {
3991
+ if (this.inMemDb.getById(collection, id)) {
3992
+ hitIds.push(id);
3993
+ }
3994
+ }
3995
+ }
3996
+ if (referToServerEnabled) {
3974
3997
  await this.ensureItemsAreLoaded(
3975
3998
  collection,
3976
3999
  ids,
3977
4000
  opts == null ? void 0 : opts.returnDeleted
3978
4001
  );
4002
+ if (wantsBgRefresh && hitIds && hitIds.length > 0) {
4003
+ this.refreshByIdsInBackground(collection, hitIds);
4004
+ }
4005
+ } else if (wantsBgRefresh) {
4006
+ this.refreshByIdsInBackground(collection, ids);
3979
4007
  }
3980
4008
  if (((opts == null ? void 0 : opts.returnDeleted) || (opts == null ? void 0 : opts.returnArchived)) && this.isOnline()) {
3981
4009
  try {
@@ -4130,6 +4158,27 @@ var _SyncedDb = class _SyncedDb {
4130
4158
  console.error(`referToServer failed for ${collection}:`, err);
4131
4159
  });
4132
4160
  }
4161
+ /**
4162
+ * Fire-and-forget background fetch of specific IDs from the server.
4163
+ * Used by findById/findByIds when `refreshInBackground: true`.
4164
+ * Always fetches from server (does not skip when items exist locally),
4165
+ * and routes results through conflict resolution so local dirty changes
4166
+ * are preserved.
4167
+ */
4168
+ refreshByIdsInBackground(collection, ids) {
4169
+ var _a;
4170
+ if (ids.length === 0) return;
4171
+ if ((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly) return;
4172
+ this.connectionManager.withRestTimeout(
4173
+ this.restInterface.findByIds(collection, ids),
4174
+ "refreshInBackground"
4175
+ ).then(async (serverItems) => {
4176
+ if (!serverItems || serverItems.length === 0) return;
4177
+ await this.syncEngine.processCollectionServerData(collection, serverItems);
4178
+ }).catch((err) => {
4179
+ console.error(`refreshInBackground failed for ${collection}:`, err);
4180
+ });
4181
+ }
4133
4182
  async ensureItemsAreLoaded(collection, ids, withDeleted) {
4134
4183
  var _a;
4135
4184
  this.assertCollection(collection);
@@ -131,6 +131,14 @@ export declare class SyncedDb implements I_SyncedDb {
131
131
  * Dexie + in-mem.
132
132
  */
133
133
  private referToServerSync;
134
+ /**
135
+ * Fire-and-forget background fetch of specific IDs from the server.
136
+ * Used by findById/findByIds when `refreshInBackground: true`.
137
+ * Always fetches from server (does not skip when items exist locally),
138
+ * and routes results through conflict resolution so local dirty changes
139
+ * are preserved.
140
+ */
141
+ private refreshByIdsInBackground;
134
142
  ensureItemsAreLoaded(collection: string, ids: string[], withDeleted?: boolean): Promise<void>;
135
143
  save<T extends DbEntity>(collection: string, id: Id, update: Partial<T>): Promise<T>;
136
144
  upsert<T extends DbEntity>(collection: string, query: QuerySpec<T>, update: UpdateSpec<T>): Promise<T>;
@@ -26,6 +26,21 @@ export type QueryOpts = Partial<{
26
26
  * pošlje query na server in posodobi in-mem z rezultati.
27
27
  */
28
28
  referToServer: boolean;
29
+ /**
30
+ * findById/findByIds (privzeto false): če je zapis lokalno prisoten,
31
+ * ga vrne takoj, nato pa v ozadju ponovno pridobi iste ID-je s serverja
32
+ * in jih posodobi v Dexie + in-mem (skozi konflikt resolucijo).
33
+ *
34
+ * Ortogonalno do `referToServer`:
35
+ * - hit + refreshInBackground: vrne lokalno + bg revalidacija
36
+ * - miss + referToServer:true: await fetch (privzeto); bg refresh NE sprozi
37
+ * (misses so ravno bili pobrani — izognemo se duplikatu)
38
+ * - miss + referToServer:false + refreshInBackground:true: vrne null,
39
+ * bg fetch prinese zapis asinhrono
40
+ *
41
+ * Ignorirano na find/findOne (tam uporabi `referToServer`).
42
+ */
43
+ refreshInBackground: boolean;
29
44
  }>;
30
45
  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";
31
46
  export type InsertKeyOf<T> = keyof T | "_rev" | "_ts" | "_csq" | "_deleted";
@@ -556,11 +556,19 @@ export interface I_SyncedDb {
556
556
  /**
557
557
  * Poišče objekt po ID-ju.
558
558
  * referToServer (privzeto true): če ne obstaja lokalno, prebere s serverja.
559
+ * refreshInBackground (privzeto false): lokalne zadetke vrne takoj, nato
560
+ * jih v ozadju revalidira s serverja (Dexie + in-mem skozi konflikt
561
+ * resolucijo). Ortogonalno do referToServer; ne povzroči duplikata
562
+ * server klicev za missing ID-je.
559
563
  */
560
564
  findById<T extends DbEntity>(collection: string, id: Id, opts?: QueryOpts): Promise<T | null>;
561
565
  /**
562
566
  * Poišče objekte po ID-jih.
563
567
  * referToServer (privzeto true): manjkajoče prebere s serverja.
568
+ * refreshInBackground (privzeto false): lokalne zadetke vrne takoj, nato
569
+ * jih v ozadju revalidira s serverja (Dexie + in-mem skozi konflikt
570
+ * resolucijo). Ortogonalno do referToServer; ne povzroči duplikata
571
+ * server klicev za missing ID-je.
564
572
  */
565
573
  findByIds<T extends DbEntity>(collection: string, ids: Id[], opts?: QueryOpts): Promise<T[]>;
566
574
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.137",
3
+ "version": "0.1.139",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",