@prabhask5/stellar-engine 1.1.7 → 1.1.8

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.
Files changed (191) hide show
  1. package/README.md +4 -1
  2. package/dist/actions/remoteChange.d.ts +143 -18
  3. package/dist/actions/remoteChange.d.ts.map +1 -1
  4. package/dist/actions/remoteChange.js +182 -58
  5. package/dist/actions/remoteChange.js.map +1 -1
  6. package/dist/actions/truncateTooltip.d.ts +26 -12
  7. package/dist/actions/truncateTooltip.d.ts.map +1 -1
  8. package/dist/actions/truncateTooltip.js +89 -34
  9. package/dist/actions/truncateTooltip.js.map +1 -1
  10. package/dist/auth/admin.d.ts +40 -3
  11. package/dist/auth/admin.d.ts.map +1 -1
  12. package/dist/auth/admin.js +45 -5
  13. package/dist/auth/admin.js.map +1 -1
  14. package/dist/auth/crypto.d.ts +55 -5
  15. package/dist/auth/crypto.d.ts.map +1 -1
  16. package/dist/auth/crypto.js +58 -5
  17. package/dist/auth/crypto.js.map +1 -1
  18. package/dist/auth/deviceVerification.d.ts +236 -20
  19. package/dist/auth/deviceVerification.d.ts.map +1 -1
  20. package/dist/auth/deviceVerification.js +293 -40
  21. package/dist/auth/deviceVerification.js.map +1 -1
  22. package/dist/auth/displayUtils.d.ts +98 -0
  23. package/dist/auth/displayUtils.d.ts.map +1 -0
  24. package/dist/auth/displayUtils.js +133 -0
  25. package/dist/auth/displayUtils.js.map +1 -0
  26. package/dist/auth/loginGuard.d.ts +108 -14
  27. package/dist/auth/loginGuard.d.ts.map +1 -1
  28. package/dist/auth/loginGuard.js +153 -31
  29. package/dist/auth/loginGuard.js.map +1 -1
  30. package/dist/auth/offlineCredentials.d.ts +132 -15
  31. package/dist/auth/offlineCredentials.d.ts.map +1 -1
  32. package/dist/auth/offlineCredentials.js +167 -23
  33. package/dist/auth/offlineCredentials.js.map +1 -1
  34. package/dist/auth/offlineLogin.d.ts +96 -10
  35. package/dist/auth/offlineLogin.d.ts.map +1 -1
  36. package/dist/auth/offlineLogin.js +82 -15
  37. package/dist/auth/offlineLogin.js.map +1 -1
  38. package/dist/auth/offlineSession.d.ts +83 -9
  39. package/dist/auth/offlineSession.d.ts.map +1 -1
  40. package/dist/auth/offlineSession.js +104 -13
  41. package/dist/auth/offlineSession.js.map +1 -1
  42. package/dist/auth/resolveAuthState.d.ts +70 -8
  43. package/dist/auth/resolveAuthState.d.ts.map +1 -1
  44. package/dist/auth/resolveAuthState.js +142 -46
  45. package/dist/auth/resolveAuthState.js.map +1 -1
  46. package/dist/auth/singleUser.d.ts +390 -37
  47. package/dist/auth/singleUser.d.ts.map +1 -1
  48. package/dist/auth/singleUser.js +500 -99
  49. package/dist/auth/singleUser.js.map +1 -1
  50. package/dist/bin/install-pwa.d.ts +18 -2
  51. package/dist/bin/install-pwa.d.ts.map +1 -1
  52. package/dist/bin/install-pwa.js +801 -25
  53. package/dist/bin/install-pwa.js.map +1 -1
  54. package/dist/config.d.ts +132 -12
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +87 -9
  57. package/dist/config.js.map +1 -1
  58. package/dist/conflicts.d.ts +246 -23
  59. package/dist/conflicts.d.ts.map +1 -1
  60. package/dist/conflicts.js +495 -46
  61. package/dist/conflicts.js.map +1 -1
  62. package/dist/data.d.ts +338 -18
  63. package/dist/data.d.ts.map +1 -1
  64. package/dist/data.js +385 -34
  65. package/dist/data.js.map +1 -1
  66. package/dist/database.d.ts +72 -14
  67. package/dist/database.d.ts.map +1 -1
  68. package/dist/database.js +120 -29
  69. package/dist/database.js.map +1 -1
  70. package/dist/debug.d.ts +77 -1
  71. package/dist/debug.d.ts.map +1 -1
  72. package/dist/debug.js +88 -1
  73. package/dist/debug.js.map +1 -1
  74. package/dist/deviceId.d.ts +38 -7
  75. package/dist/deviceId.d.ts.map +1 -1
  76. package/dist/deviceId.js +68 -10
  77. package/dist/deviceId.js.map +1 -1
  78. package/dist/engine.d.ts +175 -3
  79. package/dist/engine.d.ts.map +1 -1
  80. package/dist/engine.js +756 -109
  81. package/dist/engine.js.map +1 -1
  82. package/dist/entries/actions.d.ts +13 -0
  83. package/dist/entries/actions.d.ts.map +1 -1
  84. package/dist/entries/actions.js +26 -1
  85. package/dist/entries/actions.js.map +1 -1
  86. package/dist/entries/auth.d.ts +16 -0
  87. package/dist/entries/auth.d.ts.map +1 -1
  88. package/dist/entries/auth.js +73 -1
  89. package/dist/entries/auth.js.map +1 -1
  90. package/dist/entries/config.d.ts +12 -0
  91. package/dist/entries/config.d.ts.map +1 -1
  92. package/dist/entries/config.js +18 -1
  93. package/dist/entries/config.js.map +1 -1
  94. package/dist/entries/kit.d.ts +11 -0
  95. package/dist/entries/kit.d.ts.map +1 -1
  96. package/dist/entries/kit.js +52 -2
  97. package/dist/entries/kit.js.map +1 -1
  98. package/dist/entries/stores.d.ts +11 -0
  99. package/dist/entries/stores.d.ts.map +1 -1
  100. package/dist/entries/stores.js +43 -2
  101. package/dist/entries/stores.js.map +1 -1
  102. package/dist/entries/types.d.ts +10 -0
  103. package/dist/entries/types.d.ts.map +1 -1
  104. package/dist/entries/types.js +10 -0
  105. package/dist/entries/types.js.map +1 -1
  106. package/dist/entries/utils.d.ts +6 -0
  107. package/dist/entries/utils.d.ts.map +1 -1
  108. package/dist/entries/utils.js +22 -1
  109. package/dist/entries/utils.js.map +1 -1
  110. package/dist/entries/vite.d.ts +17 -0
  111. package/dist/entries/vite.d.ts.map +1 -1
  112. package/dist/entries/vite.js +24 -1
  113. package/dist/entries/vite.js.map +1 -1
  114. package/dist/index.d.ts +31 -0
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +175 -20
  117. package/dist/index.js.map +1 -1
  118. package/dist/kit/auth.d.ts +60 -5
  119. package/dist/kit/auth.d.ts.map +1 -1
  120. package/dist/kit/auth.js +45 -4
  121. package/dist/kit/auth.js.map +1 -1
  122. package/dist/kit/confirm.d.ts +93 -12
  123. package/dist/kit/confirm.d.ts.map +1 -1
  124. package/dist/kit/confirm.js +103 -16
  125. package/dist/kit/confirm.js.map +1 -1
  126. package/dist/kit/loads.d.ts +150 -23
  127. package/dist/kit/loads.d.ts.map +1 -1
  128. package/dist/kit/loads.js +140 -24
  129. package/dist/kit/loads.js.map +1 -1
  130. package/dist/kit/server.d.ts +142 -10
  131. package/dist/kit/server.d.ts.map +1 -1
  132. package/dist/kit/server.js +158 -15
  133. package/dist/kit/server.js.map +1 -1
  134. package/dist/kit/sw.d.ts +152 -23
  135. package/dist/kit/sw.d.ts.map +1 -1
  136. package/dist/kit/sw.js +182 -26
  137. package/dist/kit/sw.js.map +1 -1
  138. package/dist/queue.d.ts +274 -0
  139. package/dist/queue.d.ts.map +1 -1
  140. package/dist/queue.js +556 -38
  141. package/dist/queue.js.map +1 -1
  142. package/dist/realtime.d.ts +241 -27
  143. package/dist/realtime.d.ts.map +1 -1
  144. package/dist/realtime.js +633 -109
  145. package/dist/realtime.js.map +1 -1
  146. package/dist/runtime/runtimeConfig.d.ts +91 -8
  147. package/dist/runtime/runtimeConfig.d.ts.map +1 -1
  148. package/dist/runtime/runtimeConfig.js +146 -19
  149. package/dist/runtime/runtimeConfig.js.map +1 -1
  150. package/dist/stores/authState.d.ts +150 -11
  151. package/dist/stores/authState.d.ts.map +1 -1
  152. package/dist/stores/authState.js +169 -17
  153. package/dist/stores/authState.js.map +1 -1
  154. package/dist/stores/network.d.ts +39 -0
  155. package/dist/stores/network.d.ts.map +1 -1
  156. package/dist/stores/network.js +169 -16
  157. package/dist/stores/network.js.map +1 -1
  158. package/dist/stores/remoteChanges.d.ts +327 -52
  159. package/dist/stores/remoteChanges.d.ts.map +1 -1
  160. package/dist/stores/remoteChanges.js +337 -75
  161. package/dist/stores/remoteChanges.js.map +1 -1
  162. package/dist/stores/sync.d.ts +130 -0
  163. package/dist/stores/sync.d.ts.map +1 -1
  164. package/dist/stores/sync.js +167 -7
  165. package/dist/stores/sync.js.map +1 -1
  166. package/dist/supabase/auth.d.ts +325 -18
  167. package/dist/supabase/auth.d.ts.map +1 -1
  168. package/dist/supabase/auth.js +374 -26
  169. package/dist/supabase/auth.js.map +1 -1
  170. package/dist/supabase/client.d.ts +79 -6
  171. package/dist/supabase/client.d.ts.map +1 -1
  172. package/dist/supabase/client.js +158 -15
  173. package/dist/supabase/client.js.map +1 -1
  174. package/dist/supabase/validate.d.ts +101 -7
  175. package/dist/supabase/validate.d.ts.map +1 -1
  176. package/dist/supabase/validate.js +117 -8
  177. package/dist/supabase/validate.js.map +1 -1
  178. package/dist/sw/build/vite-plugin.d.ts +55 -10
  179. package/dist/sw/build/vite-plugin.d.ts.map +1 -1
  180. package/dist/sw/build/vite-plugin.js +77 -18
  181. package/dist/sw/build/vite-plugin.js.map +1 -1
  182. package/dist/sw/sw.js +99 -44
  183. package/dist/types.d.ts +150 -26
  184. package/dist/types.d.ts.map +1 -1
  185. package/dist/types.js +12 -10
  186. package/dist/types.js.map +1 -1
  187. package/dist/utils.d.ts +55 -13
  188. package/dist/utils.d.ts.map +1 -1
  189. package/dist/utils.js +83 -22
  190. package/dist/utils.js.map +1 -1
  191. package/package.json +1 -1
@@ -1,70 +1,293 @@
1
1
  /**
2
- * Conflict Resolution Engine
2
+ * @fileoverview Three-Tier Conflict Resolution Engine for Multi-Device Sync
3
3
  *
4
- * Implements three-tier conflict resolution for multi-device sync:
4
+ * This module implements a deterministic, three-tier conflict resolution
5
+ * architecture designed for offline-first multi-device synchronization.
6
+ * When a remote entity arrives that diverges from the local state, the engine
7
+ * walks through progressively finer granularity to produce a merged result:
5
8
  *
6
- * Tier 1: Non-overlapping (different entities) AUTO-MERGE
7
- * Tier 2: Different fields AUTO-MERGE FIELDS
8
- * Tier 3: Same field Apply resolution strategy
9
+ * **Tier 1 -- Non-overlapping entities (AUTO-MERGE)**
10
+ * Different entities changed on different devices never conflict; each side's
11
+ * changes are accepted wholesale. This tier is handled upstream by the sync
12
+ * pull logic before this module is invoked.
9
13
  *
10
- * Resolution strategies for Tier 3:
11
- * - Numeric fields with pending increments: Merge (sum deltas)
12
- * - Delete operations: Delete wins over edits
13
- * - All other cases: Last-write-wins (with deviceId tiebreaker)
14
+ * **Tier 2 -- Different fields on the same entity (AUTO-MERGE FIELDS)**
15
+ * When two devices edit *different* fields of the same entity, both changes
16
+ * are preserved automatically. The per-field loop inside {@link resolveConflicts}
17
+ * only emits a {@link FieldConflictResolution} when the local and remote
18
+ * values for a given field actually differ.
19
+ *
20
+ * **Tier 3 -- Same field on the same entity (STRATEGY-BASED)**
21
+ * When the exact same field was modified on both sides, a resolution strategy
22
+ * is selected based on the field's nature and any pending local operations:
23
+ * - **local_pending** -- The field has unsynced local ops; local value wins
24
+ * so user intent is never silently discarded.
25
+ * - **numeric_merge** -- Reserved for fields declared in
26
+ * `numericMergeFields`; currently falls through to last-write-wins
27
+ * because true delta-merge requires an operation-inbox system.
28
+ * - **delete_wins** -- A delete on either side trumps edits to prevent
29
+ * accidental resurrection of soft-deleted entities.
30
+ * - **last_write** -- The default fallback: the later `updated_at` wins,
31
+ * with a deterministic deviceId tiebreaker for simultaneous writes.
32
+ *
33
+ * ## Design Decisions
34
+ *
35
+ * - **Remote as base layer:** The merged entity starts as a copy of the remote
36
+ * entity, with local-winning fields overwritten on top. This bias toward
37
+ * remote is intentional: in the common case the remote version is newer (the
38
+ * conflict arose because a remote change was detected), so starting from
39
+ * remote minimizes the number of field overwrites.
40
+ * - **Version bumping:** After resolution, the merged entity's `_version` is
41
+ * set to `max(local, remote) + 1`. This ensures any device receiving the
42
+ * merged entity recognizes it as strictly newer than either input.
43
+ * - **Timestamp preservation:** The later `updated_at` is preserved so the
44
+ * merged entity sorts correctly in "recently modified" queries.
45
+ *
46
+ * ## Security Considerations
47
+ *
48
+ * - **Device ID as tiebreaker:** The `device_id` field is used only for
49
+ * deterministic tiebreaking when timestamps are identical. It is not used
50
+ * for authorization or access control. Lexicographic comparison ensures
51
+ * every device converges on the same winner without coordination.
52
+ * - **No data exfiltration:** Conflict history is stored locally in IndexedDB
53
+ * (`conflictHistory` table). It never leaves the device and is automatically
54
+ * purged after 30 days by {@link cleanupConflictHistory}.
55
+ *
56
+ * ## Auditability
57
+ *
58
+ * All resolution outcomes are recorded via {@link storeConflictHistory} for
59
+ * auditability and potential future undo support. Each field-level decision
60
+ * is stored as a separate {@link ConflictHistoryEntry}, enabling fine-grained
61
+ * queries like "show me every time `title` was overwritten by a remote value."
62
+ *
63
+ * @see {@link ./types.ts} for {@link SyncOperationItem} and {@link ConflictHistoryEntry}
64
+ * @see {@link ./config.ts} for per-table `excludeFromConflict` and `numericMergeFields`
65
+ * @see {@link ./deviceId.ts} for the stable device identifier used as tiebreaker
66
+ * @see {@link ./realtime.ts} for the primary consumer of this module
67
+ * @see {@link ./queue.ts} for the sync queue that provides pending operations
14
68
  */
15
69
  import type { SyncOperationItem } from './types';
16
70
  import type { ConflictHistoryEntry } from './types';
17
71
  export type { ConflictHistoryEntry };
18
72
  /**
19
- * Conflict resolution result for a single field
73
+ * Conflict resolution result for a single field.
74
+ *
75
+ * Each instance captures the before/after state for one field where the local
76
+ * and remote values diverged, along with metadata about which side won and why.
77
+ * This granular record enables:
78
+ * - **UI display:** Showing the user exactly what changed and why.
79
+ * - **Audit trail:** Persisted via {@link storeConflictHistory} for post-hoc analysis.
80
+ * - **Potential undo:** Retaining the "losing" value so it can be restored.
81
+ *
82
+ * @see {@link ConflictResolution} for the entity-level container
20
83
  */
21
84
  export interface FieldConflictResolution {
85
+ /** The name of the conflicting field (e.g., `"title"`, `"target_amount"`). */
22
86
  field: string;
87
+ /** The value of this field in the local (device) copy of the entity. */
23
88
  localValue: unknown;
89
+ /** The value of this field in the remote (server) copy of the entity. */
24
90
  remoteValue: unknown;
91
+ /** The value chosen (or computed) by the resolution strategy. */
25
92
  resolvedValue: unknown;
93
+ /**
94
+ * Which side's value was accepted.
95
+ * - `'local'` -- local device value was kept
96
+ * - `'remote'` -- server value was kept
97
+ * - `'merged'` -- a new value was computed from both sides (e.g., numeric merge)
98
+ */
26
99
  winner: 'local' | 'remote' | 'merged';
100
+ /**
101
+ * The strategy that determined the outcome.
102
+ * - `'last_write'` -- timestamp comparison (with deviceId tiebreaker)
103
+ * - `'numeric_merge'` -- reserved for additive delta merge
104
+ * - `'delete_wins'` -- delete operation trumps edits
105
+ * - `'local_pending'` -- unsynced local operation takes priority
106
+ *
107
+ * @see the Tier 3 description in the file-level JSDoc for details
108
+ */
27
109
  strategy: 'last_write' | 'numeric_merge' | 'delete_wins' | 'local_pending';
28
110
  }
29
111
  /**
30
- * Full conflict resolution result for an entity
112
+ * Full conflict resolution result for a single entity.
113
+ *
114
+ * Aggregates every field-level decision and provides the final merged entity
115
+ * ready to be written back to the local database.
116
+ *
117
+ * **Invariant:** `hasConflicts === (fieldResolutions.length > 0)`. If no fields
118
+ * diverged, both are false/empty and the merged entity is simply the remote copy.
119
+ *
120
+ * @see {@link resolveConflicts} which produces this structure
121
+ * @see {@link storeConflictHistory} which persists it for auditing
31
122
  */
32
123
  export interface ConflictResolution {
124
+ /** UUID of the entity that was resolved. */
33
125
  entityId: string;
126
+ /** Supabase table name the entity belongs to (e.g., `"goals"`). */
34
127
  entityType: string;
128
+ /** ISO 8601 `updated_at` from the local copy (empty string if no local copy existed). */
35
129
  localUpdatedAt: string;
130
+ /** ISO 8601 `updated_at` from the incoming remote copy. */
36
131
  remoteUpdatedAt: string;
132
+ /** Per-field resolution details; empty when there were no diverging fields. */
37
133
  fieldResolutions: FieldConflictResolution[];
134
+ /** The fully merged entity payload, ready to be persisted locally. */
38
135
  mergedEntity: Record<string, unknown>;
136
+ /** `true` when at least one field required a resolution decision. */
39
137
  hasConflicts: boolean;
138
+ /** ISO 8601 timestamp of when this resolution was computed. */
40
139
  timestamp: string;
41
140
  }
42
141
  /**
43
142
  * Resolve conflicts between local and remote entity states.
44
143
  *
45
- * @param entityType The type of entity (table name)
46
- * @param entityId The entity's ID
47
- * @param local The local entity state (may be null if entity doesn't exist locally)
48
- * @param remote The remote entity state
49
- * @param pendingOps Pending operations for this entity from the sync queue
50
- * @returns The merged entity with conflict resolution applied
144
+ * This is the main entry point for the three-tier conflict resolution engine.
145
+ * It compares every field of the local and remote copies, applies the
146
+ * appropriate resolution strategy for each divergence, and returns a fully
147
+ * merged entity along with an audit trail of every decision.
148
+ *
149
+ * **Resolution flow:**
150
+ * 1. If no local entity exists, the remote is accepted wholesale (no conflict).
151
+ * 2. Pending delete operations are checked first -- deletes always win to
152
+ * prevent entity resurrection.
153
+ * 3. Each field is compared; equal values are skipped (Tier 2 auto-merge).
154
+ * 4. Divergent fields are resolved via the Tier 3 strategy cascade:
155
+ * pending local ops --> numeric merge --> last-write-wins.
156
+ * 5. The merged entity's `_version` is bumped past both sides so downstream
157
+ * sync recognizes the merge as the newest state.
158
+ *
159
+ * **Delete handling rationale:**
160
+ * Deletes are resolved before the per-field loop because a delete affects the
161
+ * entire entity, not a single field. The "delete wins" policy prevents entity
162
+ * resurrection, which is almost always the correct UX: if a user explicitly
163
+ * deleted something on one device, they don't want it reappearing because
164
+ * another device had pending edits.
165
+ *
166
+ * @param entityType - Supabase table name (e.g., `"goals"`)
167
+ * @param entityId - UUID of the entity being resolved
168
+ * @param local - The local entity state, or `null` if the entity does
169
+ * not yet exist on this device
170
+ * @param remote - The incoming remote entity state from the server
171
+ * @param pendingOps - Unsynced operations for this entity from the local
172
+ * sync queue (used to detect local user intent)
173
+ * @returns A {@link ConflictResolution} containing the merged entity and
174
+ * per-field audit trail
175
+ *
176
+ * @throws Never throws directly -- but callers should handle potential Dexie
177
+ * errors when persisting the returned `mergedEntity`.
178
+ *
179
+ * @see {@link resolveByTimestamp} for the last-write-wins tiebreaker logic
180
+ * @see {@link storeConflictHistory} for persisting the resolution outcome
181
+ * @see {@link ./realtime.ts} and {@link ./engine.ts} which call this function
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const result = await resolveConflicts(
186
+ * 'goals',
187
+ * 'abc-123',
188
+ * { id: 'abc-123', title: 'Local Title', target: 100, updated_at: '2026-01-01T00:00:00Z' },
189
+ * { id: 'abc-123', title: 'Remote Title', target: 100, updated_at: '2026-01-02T00:00:00Z' },
190
+ * []
191
+ * );
192
+ * // result.mergedEntity.title === 'Remote Title' (remote is newer)
193
+ * // result.hasConflicts === true
194
+ * ```
51
195
  */
52
196
  export declare function resolveConflicts(entityType: string, entityId: string, local: Record<string, unknown> | null, remote: Record<string, unknown>, pendingOps: SyncOperationItem[]): Promise<ConflictResolution>;
53
197
  /**
54
- * Store conflict resolution history for review/undo.
198
+ * Store conflict resolution history for review and potential undo.
55
199
  *
56
- * @param resolution The conflict resolution result
200
+ * Each {@link FieldConflictResolution} within the given resolution is persisted
201
+ * as a separate {@link ConflictHistoryEntry} row in the `conflictHistory`
202
+ * IndexedDB table. This enables fine-grained auditing (e.g., "show me every
203
+ * time field X was overwritten by a remote value").
204
+ *
205
+ * No-ops silently when there are no actual conflicts (`hasConflicts === false`).
206
+ *
207
+ * **Storage cost:** Each entry is a small JSON object (~200-500 bytes). With
208
+ * 30-day retention and typical usage patterns, the `conflictHistory` table
209
+ * rarely exceeds a few hundred KB. {@link cleanupConflictHistory} handles
210
+ * periodic pruning.
211
+ *
212
+ * **Error handling:** Failures are caught and logged but do not propagate.
213
+ * Conflict history is a best-effort audit trail; a failure to record it must
214
+ * not block the critical path of applying the merged entity to the local DB.
215
+ *
216
+ * @param resolution - The entity-level resolution result produced by
217
+ * {@link resolveConflicts}
218
+ * @returns Resolves when all entries have been written (or on error, after
219
+ * logging the failure)
220
+ *
221
+ * @throws Never throws -- all errors are caught internally and logged.
222
+ *
223
+ * @see {@link ConflictHistoryEntry} in `./types.ts` for the persisted schema
224
+ * @see {@link cleanupConflictHistory} for expiring old entries
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * const resolution = await resolveConflicts('goals', 'abc', local, remote, []);
229
+ * await storeConflictHistory(resolution);
230
+ * ```
57
231
  */
58
232
  export declare function storeConflictHistory(resolution: ConflictResolution): Promise<void>;
59
233
  /**
60
- * Get pending operations for a specific entity from the sync queue.
234
+ * Get all pending (unsynced) operations for a specific entity from the
235
+ * local sync queue.
236
+ *
237
+ * This is used by {@link resolveConflicts} to detect Tier 3a situations where
238
+ * the user has local changes that have not yet been pushed to the server.
239
+ * Those pending operations take priority so user intent is never silently lost.
240
+ *
241
+ * **Query strategy:** Uses Dexie's `where('entityId').equals(...)` for an
242
+ * indexed lookup rather than scanning the entire queue. This is efficient
243
+ * even for large queues because `entityId` is indexed.
61
244
  *
62
- * @param entityId The entity ID to check
63
- * @returns Array of pending operations for this entity
245
+ * @param entityId - UUID of the entity to look up
246
+ * @returns An array of {@link SyncOperationItem} entries queued for this entity
247
+ * (may be empty if nothing is pending)
248
+ *
249
+ * @see {@link SyncOperationItem} in `./types.ts` for the operation schema
250
+ * @see {@link resolveConflicts} which consumes these pending ops
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * const ops = await getPendingOpsForEntity('abc-123');
255
+ * // [{ table: 'goals', entityId: 'abc-123', operationType: 'set', ... }]
256
+ * ```
64
257
  */
65
258
  export declare function getPendingOpsForEntity(entityId: string): Promise<SyncOperationItem[]>;
66
259
  /**
67
- * Clean up old conflict history entries (older than 30 days).
260
+ * Clean up old conflict history entries older than 30 days.
261
+ *
262
+ * Should be called periodically (e.g., on app launch or after a successful
263
+ * full sync) to prevent the `conflictHistory` IndexedDB table from growing
264
+ * unboundedly. The 30-day retention window provides enough runway for users
265
+ * to notice and investigate unexpected data changes.
266
+ *
267
+ * **Why 30 days?** This balances storage cost against audit usefulness:
268
+ * - Too short (e.g., 7 days) and users may not notice a conflict before
269
+ * the history is purged.
270
+ * - Too long (e.g., 1 year) and the table could grow to several MB on
271
+ * devices with frequent multi-device conflicts.
272
+ * - 30 days covers typical usage patterns where users check in at least
273
+ * once a month.
274
+ *
275
+ * **Performance note:** Uses Dexie's `.filter()` (client-side scan) rather
276
+ * than an indexed query because the `conflictHistory` table is small and
277
+ * does not have a `timestamp` index. If the table grows significantly,
278
+ * adding an index on `timestamp` and using `.where()` would be faster.
279
+ *
280
+ * @returns The number of entries deleted, or `0` if an error occurred
281
+ *
282
+ * @throws Never throws -- all errors are caught internally and logged.
283
+ *
284
+ * @see {@link storeConflictHistory} which creates the entries being cleaned
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * const deleted = await cleanupConflictHistory();
289
+ * console.log(`Purged ${deleted} stale conflict records`);
290
+ * ```
68
291
  */
69
292
  export declare function cleanupConflictHistory(): Promise<number>;
70
293
  //# sourceMappingURL=conflicts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGpD,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACtC,QAAQ,EAAE,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;CAC5E;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,uBAAuB,EAAE,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,EAAE,iBAAiB,EAAE,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CA4K7B;AA8FD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBxF;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAO3F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAoB9D"}
1
+ {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAIpD,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAMrC;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IAEd,wEAAwE;IACxE,UAAU,EAAE,OAAO,CAAC;IAEpB,yEAAyE;IACzE,WAAW,EAAE,OAAO,CAAC;IAErB,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAEtC;;;;;;;;OAQG;IACH,QAAQ,EAAE,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;CAC5E;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IAEjB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IAEnB,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;IAEvB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IAExB,+EAA+E;IAC/E,gBAAgB,EAAE,uBAAuB,EAAE,CAAC;IAE5C,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEtC,qEAAqE;IACrE,YAAY,EAAE,OAAO,CAAC;IAEtB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AA4ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,EAAE,iBAAiB,EAAE,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAoQ7B;AAqND;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAO3F;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAsB9D"}