cry-synced-db-client 0.1.170 → 0.1.172

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.
@@ -1,5 +1,5 @@
1
1
  import type { AggregateOptions } from "mongodb";
2
- import type { I_SyncedDb, SyncedDbConfig, WsNotificationInfo, EvictionInfo, EvictionCollectionInfo } from "../types/I_SyncedDb";
2
+ import type { I_SyncedDb, SyncedDbConfig, CollectionConfig, WsNotificationInfo, EvictionInfo, EvictionCollectionInfo } from "../types/I_SyncedDb";
3
3
  import type { DirtyMeta, MetaUpdateBroadcast } from "../types/I_DexieDb";
4
4
  import type { QuerySpec, QueryOpts, UpdateSpec, InsertSpec, BatchSpec } from "../types/I_RestInterface";
5
5
  import type { Id, DbEntity } from "../types/DbEntity";
@@ -36,6 +36,7 @@ export declare class SyncedDb implements I_SyncedDb {
36
36
  private unsubscribeServerUpdates?;
37
37
  private cleanupNotifierCallbacks?;
38
38
  private beforeUnloadHandler?;
39
+ private visibilityFlushHandler?;
39
40
  private readonly defaultReturnDeleted;
40
41
  private readonly defaultReturnArchived;
41
42
  private readonly onDatabaseCreated?;
@@ -62,6 +63,22 @@ export declare class SyncedDb implements I_SyncedDb {
62
63
  getInstanceId(): string;
63
64
  getCrossTabSyncDebounceMs(): number;
64
65
  isLeaderTab(): boolean;
66
+ leaderSince(): Date | undefined;
67
+ followerSince(): Date | undefined;
68
+ /**
69
+ * Register a collection for sync at runtime. See `I_SyncedDb.addCollectionToSync`.
70
+ */
71
+ addCollectionToSync(spec: CollectionConfig): Promise<void>;
72
+ /**
73
+ * Replace the collection config and re-sync. See `I_SyncedDb.replaceSyncCollection`.
74
+ */
75
+ replaceSyncCollection(spec: CollectionConfig): Promise<boolean>;
76
+ /**
77
+ * Shared install path for addCollectionToSync / replaceSyncCollection:
78
+ * write config, extend active sync filter, load Dexie cursor + in-mem,
79
+ * fire a targeted one-shot download for just this collection.
80
+ */
81
+ private _installCollectionConfig;
65
82
  /**
66
83
  * Restrict sync to only these collections. When non-empty, only the listed
67
84
  * collections load from Dexie→in-mem and download from server. Pass an empty
@@ -87,6 +104,18 @@ export declare class SyncedDb implements I_SyncedDb {
87
104
  * Does NOT upload to server — call sync() for that.
88
105
  */
89
106
  flush(): Promise<void>;
107
+ /**
108
+ * Push dirty data to the server immediately, bypassing the upload debounce.
109
+ *
110
+ * Flushes pending Dexie writes, awaits any in-flight REST upload, then fires
111
+ * a fresh `uploadDirtyItems()`. Called automatically when the tab becomes
112
+ * hidden so data is not stranded by browser tab-throttling / suspension.
113
+ *
114
+ * No-op when offline, forced offline, or not yet initialized.
115
+ *
116
+ * @param calledFrom Diagnostic tag threaded through to upload callbacks
117
+ */
118
+ flushToServer(calledFrom?: string): Promise<void>;
90
119
  private _lastFullSyncDate?;
91
120
  private _lastInitialSyncDate?;
92
121
  /**
@@ -12,6 +12,8 @@ export declare class LeaderElectionManager implements I_LeaderElectionManager {
12
12
  private isLeaderFlag;
13
13
  private closing;
14
14
  private releasingDueToVisibility;
15
+ private _leaderSince?;
16
+ private _followerSince?;
15
17
  private releaseLeaderLockResolve?;
16
18
  private becameLeaderPromise?;
17
19
  private becameLeaderResolve?;
@@ -36,6 +38,17 @@ export declare class LeaderElectionManager implements I_LeaderElectionManager {
36
38
  * Check if this instance is currently the leader.
37
39
  */
38
40
  isLeader(): boolean;
41
+ /**
42
+ * Returns the timestamp when this tab became leader, or undefined if currently a follower.
43
+ * Cleared on transition to follower.
44
+ */
45
+ leaderSince(): Date | undefined;
46
+ /**
47
+ * Returns the timestamp when this tab became follower, or undefined if currently a leader.
48
+ * Initialized at construction (every tab starts as a follower until it claims the lock).
49
+ * Cleared on transition to leader.
50
+ */
51
+ followerSince(): Date | undefined;
39
52
  /**
40
53
  * Cleanup resources.
41
54
  */
@@ -26,6 +26,10 @@ export interface I_LeaderElectionManager {
26
26
  releaseLeaderLock(): void;
27
27
  /** Check if this instance is currently the leader. */
28
28
  isLeader(): boolean;
29
+ /** Timestamp when this tab became leader, or undefined if currently a follower. */
30
+ leaderSince(): Date | undefined;
31
+ /** Timestamp when this tab became follower, or undefined if currently a leader. */
32
+ followerSince(): Date | undefined;
29
33
  /** Initialize (setup visibility listeners, BroadcastChannel). */
30
34
  init(): void;
31
35
  /** Cleanup resources. */
@@ -402,6 +402,13 @@ export interface CollectionConfig<T extends DbEntity = any, M = any> {
402
402
  * Read operations throw. Server sync and WS notifications are skipped.
403
403
  */
404
404
  writeOnly?: boolean;
405
+ /**
406
+ * Marks this config as provisional — a later `addCollectionToSync()` with
407
+ * a permanent (non-temporary) config will replace it. Permanent configs
408
+ * (the default) are immutable: subsequent `addCollectionToSync()` calls
409
+ * for the same collection are no-ops once a permanent config is in place.
410
+ */
411
+ temporaryConfig?: boolean;
405
412
  /** Whether this collection uses in-memory metadata */
406
413
  hasMetadata?: boolean;
407
414
  /** Callback called when a single object is written to in-mem. Returns metadata to store. */
@@ -737,6 +744,53 @@ export interface I_SyncedDb {
737
744
  * Does NOT upload to server — call sync() for that.
738
745
  */
739
746
  flush(): Promise<void>;
747
+ /**
748
+ * Push dirty data to the server immediately, bypassing the upload debounce.
749
+ * Flushes pending Dexie writes, awaits any in-flight REST upload, then fires
750
+ * a fresh upload. Called automatically on `visibilitychange` to `hidden` so
751
+ * data is not stranded by browser tab-throttling / suspension.
752
+ * No-op when offline, forced offline, or not yet initialized.
753
+ * @param calledFrom Diagnostic tag threaded through to upload callbacks
754
+ */
755
+ flushToServer(calledFrom?: string): Promise<void>;
756
+ /**
757
+ * Register a collection for sync at runtime.
758
+ *
759
+ * Behavior depends on whether a config already exists for `spec.name`:
760
+ * - permanent (existing config has `temporaryConfig !== true`): no-op
761
+ * - temporary (existing config has `temporaryConfig === true`): replaced by `spec`
762
+ * - none: `spec` is added
763
+ *
764
+ * On add or replace: loads the Dexie cursor for this collection into the
765
+ * sync-meta cache, hydrates in-mem from Dexie, and (when online + not
766
+ * `writeOnly`) fires a one-shot download sync for just this collection.
767
+ * If a `syncOnlyTheseCollections` filter is active, the new collection is
768
+ * added to it so future syncs include it.
769
+ *
770
+ * @param spec Collection config to register
771
+ */
772
+ addCollectionToSync(spec: CollectionConfig): Promise<void>;
773
+ /**
774
+ * Replace the collection config and re-sync.
775
+ *
776
+ * Like `addCollectionToSync` but more aggressive — overrides even a
777
+ * permanent existing config. The only blocked transition is
778
+ * **permanent → temporary** (cannot downgrade an already-established
779
+ * config to provisional):
780
+ *
781
+ * - no existing config: install `spec`, return `true`
782
+ * - existing temporary: replace with `spec` (any type), return `true`
783
+ * - existing permanent, `spec.temporaryConfig === true`: return `false`, no-op
784
+ * - existing permanent, `spec` permanent: replace with `spec`, return `true`
785
+ *
786
+ * On replace: extends active `syncOnlyTheseCollections` filter, reloads
787
+ * the Dexie cursor, re-hydrates in-mem, and (when online + not `writeOnly`)
788
+ * fires a one-shot targeted download.
789
+ *
790
+ * @param spec Collection config to install
791
+ * @returns `true` if installed, `false` if blocked (permanent → temporary)
792
+ */
793
+ replaceSyncCollection(spec: CollectionConfig): Promise<boolean>;
740
794
  /**
741
795
  * Returns when all collections were last successfully synced
742
796
  * from the server, or undefined if never synced.
@@ -954,6 +1008,19 @@ export interface I_SyncedDb {
954
1008
  * @returns true if this instance holds the leader lock
955
1009
  */
956
1010
  isLeaderTab(): boolean;
1011
+ /**
1012
+ * Timestamp when this tab became the leader.
1013
+ * Set on transition from follower to leader; cleared when leadership is lost.
1014
+ * @returns Date of leader transition, or undefined if currently a follower
1015
+ */
1016
+ leaderSince(): Date | undefined;
1017
+ /**
1018
+ * Timestamp when this tab became a follower.
1019
+ * Initialized at construction (every tab starts as a follower until it claims
1020
+ * the lock); cleared when leadership is gained, set again on transition to follower.
1021
+ * @returns Date of follower transition, or undefined if currently the leader
1022
+ */
1023
+ followerSince(): Date | undefined;
957
1024
  /**
958
1025
  * Get metadata for a single object.
959
1026
  * @param collection Collection name
@@ -84,9 +84,15 @@ export declare function deleteByPath(target: any, path: string): boolean;
84
84
  * wrapped element[0], not into the array itself. The pending insert
85
85
  * absorbs the sub-field edit.
86
86
  *
87
- * 2. New path is an ANCESTOR of existing keys (e.g. existing has "koraki.0.diag",
88
- * new is "koraki" with full array): remove the now-redundant descendants and
89
- * set the parent path. The new full value supersedes any field-level deltas.
87
+ * 2. New path is an ANCESTOR of existing keys.
88
+ * - Plain ancestor (e.g. existing "koraki.0.diag", new "koraki" with full
89
+ * array): drop the descendants; the new full value supersedes any
90
+ * field-level deltas.
91
+ * - TERMINAL-bracket ancestor whose `newValue` is element-shaped
92
+ * (`arr[a].sub[b]` with `[<el>]` or plain object): LAYER pending
93
+ * descendant sub-field edits INTO `newValue` before dropping them,
94
+ * so the element-level write absorbs the prior sub-field-level
95
+ * writes instead of silently overwriting them.
90
96
  *
91
97
  * 3. New path is ORTHOGONAL to existing keys: simple set (Object.assign-equivalent).
92
98
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.170",
3
+ "version": "0.1.172",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",