stellar-drive 1.0.0

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 (246) hide show
  1. package/README.md +607 -0
  2. package/dist/actions/remoteChange.d.ts +204 -0
  3. package/dist/actions/remoteChange.d.ts.map +1 -0
  4. package/dist/actions/remoteChange.js +424 -0
  5. package/dist/actions/remoteChange.js.map +1 -0
  6. package/dist/actions/truncateTooltip.d.ts +56 -0
  7. package/dist/actions/truncateTooltip.d.ts.map +1 -0
  8. package/dist/actions/truncateTooltip.js +312 -0
  9. package/dist/actions/truncateTooltip.js.map +1 -0
  10. package/dist/auth/crypto.d.ts +41 -0
  11. package/dist/auth/crypto.d.ts.map +1 -0
  12. package/dist/auth/crypto.js +50 -0
  13. package/dist/auth/crypto.js.map +1 -0
  14. package/dist/auth/deviceVerification.d.ts +283 -0
  15. package/dist/auth/deviceVerification.d.ts.map +1 -0
  16. package/dist/auth/deviceVerification.js +575 -0
  17. package/dist/auth/deviceVerification.js.map +1 -0
  18. package/dist/auth/displayUtils.d.ts +98 -0
  19. package/dist/auth/displayUtils.d.ts.map +1 -0
  20. package/dist/auth/displayUtils.js +145 -0
  21. package/dist/auth/displayUtils.js.map +1 -0
  22. package/dist/auth/loginGuard.d.ts +134 -0
  23. package/dist/auth/loginGuard.d.ts.map +1 -0
  24. package/dist/auth/loginGuard.js +276 -0
  25. package/dist/auth/loginGuard.js.map +1 -0
  26. package/dist/auth/offlineCredentials.d.ts +105 -0
  27. package/dist/auth/offlineCredentials.d.ts.map +1 -0
  28. package/dist/auth/offlineCredentials.js +176 -0
  29. package/dist/auth/offlineCredentials.js.map +1 -0
  30. package/dist/auth/offlineSession.d.ts +96 -0
  31. package/dist/auth/offlineSession.d.ts.map +1 -0
  32. package/dist/auth/offlineSession.js +145 -0
  33. package/dist/auth/offlineSession.js.map +1 -0
  34. package/dist/auth/resolveAuthState.d.ts +85 -0
  35. package/dist/auth/resolveAuthState.d.ts.map +1 -0
  36. package/dist/auth/resolveAuthState.js +249 -0
  37. package/dist/auth/resolveAuthState.js.map +1 -0
  38. package/dist/auth/singleUser.d.ts +498 -0
  39. package/dist/auth/singleUser.d.ts.map +1 -0
  40. package/dist/auth/singleUser.js +1282 -0
  41. package/dist/auth/singleUser.js.map +1 -0
  42. package/dist/bin/commands.d.ts +14 -0
  43. package/dist/bin/commands.d.ts.map +1 -0
  44. package/dist/bin/commands.js +68 -0
  45. package/dist/bin/commands.js.map +1 -0
  46. package/dist/bin/install-pwa.d.ts +41 -0
  47. package/dist/bin/install-pwa.d.ts.map +1 -0
  48. package/dist/bin/install-pwa.js +4594 -0
  49. package/dist/bin/install-pwa.js.map +1 -0
  50. package/dist/config.d.ts +249 -0
  51. package/dist/config.d.ts.map +1 -0
  52. package/dist/config.js +395 -0
  53. package/dist/config.js.map +1 -0
  54. package/dist/conflicts.d.ts +306 -0
  55. package/dist/conflicts.d.ts.map +1 -0
  56. package/dist/conflicts.js +807 -0
  57. package/dist/conflicts.js.map +1 -0
  58. package/dist/crdt/awareness.d.ts +128 -0
  59. package/dist/crdt/awareness.d.ts.map +1 -0
  60. package/dist/crdt/awareness.js +284 -0
  61. package/dist/crdt/awareness.js.map +1 -0
  62. package/dist/crdt/channel.d.ts +165 -0
  63. package/dist/crdt/channel.d.ts.map +1 -0
  64. package/dist/crdt/channel.js +522 -0
  65. package/dist/crdt/channel.js.map +1 -0
  66. package/dist/crdt/config.d.ts +58 -0
  67. package/dist/crdt/config.d.ts.map +1 -0
  68. package/dist/crdt/config.js +123 -0
  69. package/dist/crdt/config.js.map +1 -0
  70. package/dist/crdt/helpers.d.ts +104 -0
  71. package/dist/crdt/helpers.d.ts.map +1 -0
  72. package/dist/crdt/helpers.js +116 -0
  73. package/dist/crdt/helpers.js.map +1 -0
  74. package/dist/crdt/offline.d.ts +58 -0
  75. package/dist/crdt/offline.d.ts.map +1 -0
  76. package/dist/crdt/offline.js +130 -0
  77. package/dist/crdt/offline.js.map +1 -0
  78. package/dist/crdt/persistence.d.ts +65 -0
  79. package/dist/crdt/persistence.d.ts.map +1 -0
  80. package/dist/crdt/persistence.js +171 -0
  81. package/dist/crdt/persistence.js.map +1 -0
  82. package/dist/crdt/provider.d.ts +109 -0
  83. package/dist/crdt/provider.d.ts.map +1 -0
  84. package/dist/crdt/provider.js +543 -0
  85. package/dist/crdt/provider.js.map +1 -0
  86. package/dist/crdt/store.d.ts +111 -0
  87. package/dist/crdt/store.d.ts.map +1 -0
  88. package/dist/crdt/store.js +158 -0
  89. package/dist/crdt/store.js.map +1 -0
  90. package/dist/crdt/types.d.ts +281 -0
  91. package/dist/crdt/types.d.ts.map +1 -0
  92. package/dist/crdt/types.js +26 -0
  93. package/dist/crdt/types.js.map +1 -0
  94. package/dist/data.d.ts +502 -0
  95. package/dist/data.d.ts.map +1 -0
  96. package/dist/data.js +862 -0
  97. package/dist/data.js.map +1 -0
  98. package/dist/database.d.ts +153 -0
  99. package/dist/database.d.ts.map +1 -0
  100. package/dist/database.js +325 -0
  101. package/dist/database.js.map +1 -0
  102. package/dist/debug.d.ts +87 -0
  103. package/dist/debug.d.ts.map +1 -0
  104. package/dist/debug.js +135 -0
  105. package/dist/debug.js.map +1 -0
  106. package/dist/demo.d.ts +131 -0
  107. package/dist/demo.d.ts.map +1 -0
  108. package/dist/demo.js +168 -0
  109. package/dist/demo.js.map +1 -0
  110. package/dist/deviceId.d.ts +47 -0
  111. package/dist/deviceId.d.ts.map +1 -0
  112. package/dist/deviceId.js +106 -0
  113. package/dist/deviceId.js.map +1 -0
  114. package/dist/diagnostics.d.ts +292 -0
  115. package/dist/diagnostics.d.ts.map +1 -0
  116. package/dist/diagnostics.js +378 -0
  117. package/dist/diagnostics.js.map +1 -0
  118. package/dist/engine.d.ts +230 -0
  119. package/dist/engine.d.ts.map +1 -0
  120. package/dist/engine.js +2636 -0
  121. package/dist/engine.js.map +1 -0
  122. package/dist/entries/actions.d.ts +16 -0
  123. package/dist/entries/actions.d.ts.map +1 -0
  124. package/dist/entries/actions.js +29 -0
  125. package/dist/entries/actions.js.map +1 -0
  126. package/dist/entries/auth.d.ts +19 -0
  127. package/dist/entries/auth.d.ts.map +1 -0
  128. package/dist/entries/auth.js +50 -0
  129. package/dist/entries/auth.js.map +1 -0
  130. package/dist/entries/config.d.ts +15 -0
  131. package/dist/entries/config.d.ts.map +1 -0
  132. package/dist/entries/config.js +20 -0
  133. package/dist/entries/config.js.map +1 -0
  134. package/dist/entries/crdt.d.ts +32 -0
  135. package/dist/entries/crdt.d.ts.map +1 -0
  136. package/dist/entries/crdt.js +52 -0
  137. package/dist/entries/crdt.js.map +1 -0
  138. package/dist/entries/kit.d.ts +22 -0
  139. package/dist/entries/kit.d.ts.map +1 -0
  140. package/dist/entries/kit.js +58 -0
  141. package/dist/entries/kit.js.map +1 -0
  142. package/dist/entries/stores.d.ts +22 -0
  143. package/dist/entries/stores.d.ts.map +1 -0
  144. package/dist/entries/stores.js +57 -0
  145. package/dist/entries/stores.js.map +1 -0
  146. package/dist/entries/types.d.ts +23 -0
  147. package/dist/entries/types.d.ts.map +1 -0
  148. package/dist/entries/types.js +12 -0
  149. package/dist/entries/types.js.map +1 -0
  150. package/dist/entries/utils.d.ts +12 -0
  151. package/dist/entries/utils.d.ts.map +1 -0
  152. package/dist/entries/utils.js +42 -0
  153. package/dist/entries/utils.js.map +1 -0
  154. package/dist/entries/vite.d.ts +20 -0
  155. package/dist/entries/vite.d.ts.map +1 -0
  156. package/dist/entries/vite.js +26 -0
  157. package/dist/entries/vite.js.map +1 -0
  158. package/dist/index.d.ts +77 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +234 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/kit/auth.d.ts +80 -0
  163. package/dist/kit/auth.d.ts.map +1 -0
  164. package/dist/kit/auth.js +75 -0
  165. package/dist/kit/auth.js.map +1 -0
  166. package/dist/kit/confirm.d.ts +111 -0
  167. package/dist/kit/confirm.d.ts.map +1 -0
  168. package/dist/kit/confirm.js +169 -0
  169. package/dist/kit/confirm.js.map +1 -0
  170. package/dist/kit/loads.d.ts +187 -0
  171. package/dist/kit/loads.d.ts.map +1 -0
  172. package/dist/kit/loads.js +208 -0
  173. package/dist/kit/loads.js.map +1 -0
  174. package/dist/kit/server.d.ts +175 -0
  175. package/dist/kit/server.d.ts.map +1 -0
  176. package/dist/kit/server.js +297 -0
  177. package/dist/kit/server.js.map +1 -0
  178. package/dist/kit/sw.d.ts +176 -0
  179. package/dist/kit/sw.d.ts.map +1 -0
  180. package/dist/kit/sw.js +320 -0
  181. package/dist/kit/sw.js.map +1 -0
  182. package/dist/queue.d.ts +306 -0
  183. package/dist/queue.d.ts.map +1 -0
  184. package/dist/queue.js +925 -0
  185. package/dist/queue.js.map +1 -0
  186. package/dist/realtime.d.ts +280 -0
  187. package/dist/realtime.d.ts.map +1 -0
  188. package/dist/realtime.js +1031 -0
  189. package/dist/realtime.js.map +1 -0
  190. package/dist/runtime/runtimeConfig.d.ts +110 -0
  191. package/dist/runtime/runtimeConfig.d.ts.map +1 -0
  192. package/dist/runtime/runtimeConfig.js +260 -0
  193. package/dist/runtime/runtimeConfig.js.map +1 -0
  194. package/dist/schema.d.ts +150 -0
  195. package/dist/schema.d.ts.map +1 -0
  196. package/dist/schema.js +891 -0
  197. package/dist/schema.js.map +1 -0
  198. package/dist/stores/authState.d.ts +204 -0
  199. package/dist/stores/authState.d.ts.map +1 -0
  200. package/dist/stores/authState.js +336 -0
  201. package/dist/stores/authState.js.map +1 -0
  202. package/dist/stores/factories.d.ts +140 -0
  203. package/dist/stores/factories.d.ts.map +1 -0
  204. package/dist/stores/factories.js +157 -0
  205. package/dist/stores/factories.js.map +1 -0
  206. package/dist/stores/network.d.ts +48 -0
  207. package/dist/stores/network.d.ts.map +1 -0
  208. package/dist/stores/network.js +261 -0
  209. package/dist/stores/network.js.map +1 -0
  210. package/dist/stores/remoteChanges.d.ts +417 -0
  211. package/dist/stores/remoteChanges.d.ts.map +1 -0
  212. package/dist/stores/remoteChanges.js +626 -0
  213. package/dist/stores/remoteChanges.js.map +1 -0
  214. package/dist/stores/sync.d.ts +165 -0
  215. package/dist/stores/sync.d.ts.map +1 -0
  216. package/dist/stores/sync.js +275 -0
  217. package/dist/stores/sync.js.map +1 -0
  218. package/dist/supabase/auth.d.ts +219 -0
  219. package/dist/supabase/auth.d.ts.map +1 -0
  220. package/dist/supabase/auth.js +459 -0
  221. package/dist/supabase/auth.js.map +1 -0
  222. package/dist/supabase/client.d.ts +88 -0
  223. package/dist/supabase/client.d.ts.map +1 -0
  224. package/dist/supabase/client.js +313 -0
  225. package/dist/supabase/client.js.map +1 -0
  226. package/dist/supabase/validate.d.ts +118 -0
  227. package/dist/supabase/validate.d.ts.map +1 -0
  228. package/dist/supabase/validate.js +208 -0
  229. package/dist/supabase/validate.js.map +1 -0
  230. package/dist/sw/build/vite-plugin.d.ts +149 -0
  231. package/dist/sw/build/vite-plugin.d.ts.map +1 -0
  232. package/dist/sw/build/vite-plugin.js +517 -0
  233. package/dist/sw/build/vite-plugin.js.map +1 -0
  234. package/dist/sw/sw.js +664 -0
  235. package/dist/types.d.ts +363 -0
  236. package/dist/types.d.ts.map +1 -0
  237. package/dist/types.js +18 -0
  238. package/dist/types.js.map +1 -0
  239. package/dist/utils.d.ts +85 -0
  240. package/dist/utils.d.ts.map +1 -0
  241. package/dist/utils.js +156 -0
  242. package/dist/utils.js.map +1 -0
  243. package/package.json +117 -0
  244. package/src/components/DeferredChangesBanner.svelte +477 -0
  245. package/src/components/DemoBanner.svelte +110 -0
  246. package/src/components/SyncStatus.svelte +1732 -0
@@ -0,0 +1,292 @@
1
+ /**
2
+ * @fileoverview Unified Diagnostics Module
3
+ *
4
+ * Provides a single entry point for inspecting the internal state of the
5
+ * stellar-drive sync system. The main `getDiagnostics()` function returns a
6
+ * comprehensive JSON snapshot covering sync cycles, egress, queue, realtime,
7
+ * network, conflict history, errors, and configuration.
8
+ *
9
+ * Each call returns a **point-in-time snapshot** — the data is not reactive.
10
+ * For a live dashboard, the consumer app should poll `getDiagnostics()` at the
11
+ * desired frequency (e.g., via `setInterval` or `onSyncComplete` callback).
12
+ *
13
+ * Sub-category functions (`getSyncDiagnostics`, `getRealtimeDiagnostics`, etc.)
14
+ * are also exported for lightweight access to specific sections without
15
+ * incurring the cost of async operations (queue/conflict reads).
16
+ *
17
+ * **Dependency direction:** This module imports from engine, realtime, queue,
18
+ * conflicts, config, deviceId, stores, and utils. No module imports from
19
+ * diagnostics — this guarantees zero circular dependency risk.
20
+ *
21
+ * @module diagnostics
22
+ * @see {@link ./engine.ts} for the core sync engine
23
+ * @see {@link ./realtime.ts} for WebSocket connection management
24
+ * @see {@link ./conflicts.ts} for conflict resolution history
25
+ */
26
+ import type { ConflictHistoryEntry, SyncStatus } from './types';
27
+ import type { RealtimeConnectionState } from './realtime';
28
+ import type { SyncError } from './stores/sync';
29
+ import type { CRDTConnectionState } from './crdt/types';
30
+ /**
31
+ * Comprehensive diagnostics snapshot returned by {@link getDiagnostics}.
32
+ *
33
+ * Contains every observable aspect of the sync engine's runtime state,
34
+ * structured into logical sections for easy consumption by dashboards,
35
+ * logging pipelines, or browser console inspection.
36
+ */
37
+ export interface DiagnosticsSnapshot {
38
+ /** ISO 8601 timestamp of when this snapshot was captured */
39
+ timestamp: string;
40
+ /** Engine prefix (e.g., `"engine"` or `"stellar"`) */
41
+ prefix: string;
42
+ /** Stable device identifier for this browser/device */
43
+ deviceId: string;
44
+ /** Sync cycle statistics and cursor state */
45
+ sync: {
46
+ status: SyncStatus;
47
+ totalCycles: number;
48
+ lastSyncTime: string | null;
49
+ lastSuccessfulSyncTimestamp: string | null;
50
+ syncMessage: string | null;
51
+ recentCycles: Array<{
52
+ timestamp: string;
53
+ trigger: string;
54
+ pushedItems: number;
55
+ pulledTables: number;
56
+ pulledRecords: number;
57
+ egressBytes: number;
58
+ durationMs: number;
59
+ }>;
60
+ cyclesLastMinute: number;
61
+ hasHydrated: boolean;
62
+ schemaValidated: boolean;
63
+ pendingCount: number;
64
+ };
65
+ /** Cumulative egress (bandwidth) statistics for the current session */
66
+ egress: {
67
+ sessionStart: string;
68
+ totalBytes: number;
69
+ totalFormatted: string;
70
+ totalRecords: number;
71
+ byTable: Record<string, {
72
+ bytes: number;
73
+ formatted: string;
74
+ records: number;
75
+ percentage: string;
76
+ }>;
77
+ };
78
+ /** Pending sync queue state */
79
+ queue: {
80
+ pendingOperations: number;
81
+ pendingEntityIds: string[];
82
+ byTable: Record<string, number>;
83
+ byOperationType: Record<string, number>;
84
+ oldestPendingTimestamp: string | null;
85
+ itemsInBackoff: number;
86
+ };
87
+ /** Realtime WebSocket connection state */
88
+ realtime: {
89
+ connectionState: RealtimeConnectionState;
90
+ healthy: boolean;
91
+ reconnectAttempts: number;
92
+ lastError: string | null;
93
+ userId: string | null;
94
+ deviceId: string;
95
+ recentlyProcessedCount: number;
96
+ operationInProgress: boolean;
97
+ reconnectScheduled: boolean;
98
+ };
99
+ /** Browser network connectivity */
100
+ network: {
101
+ online: boolean;
102
+ };
103
+ /** Engine-internal state (locks, visibility, auth) */
104
+ engine: {
105
+ isTabVisible: boolean;
106
+ tabHiddenAt: string | null;
107
+ lockHeld: boolean;
108
+ lockHeldForMs: number | null;
109
+ recentlyModifiedCount: number;
110
+ wasOffline: boolean;
111
+ authValidatedAfterReconnect: boolean;
112
+ };
113
+ /** Recent conflict resolution history */
114
+ conflicts: {
115
+ recentHistory: ConflictHistoryEntry[];
116
+ totalCount: number;
117
+ };
118
+ /** Error state from the sync status store */
119
+ errors: {
120
+ lastError: string | null;
121
+ lastErrorDetails: string | null;
122
+ recentErrors: SyncError[];
123
+ };
124
+ /** CRDT collaborative editing subsystem diagnostics */
125
+ crdt: {
126
+ /** Whether the CRDT subsystem is enabled (crdt config provided to initEngine). */
127
+ enabled: boolean;
128
+ /** Resolved CRDT configuration (null if not enabled). */
129
+ config: {
130
+ supabaseTable: string;
131
+ persistIntervalMs: number;
132
+ broadcastDebounceMs: number;
133
+ localSaveDebounceMs: number;
134
+ cursorDebounceMs: number;
135
+ maxOfflineDocuments: number;
136
+ maxBroadcastPayloadBytes: number;
137
+ syncPeerTimeoutMs: number;
138
+ maxReconnectAttempts: number;
139
+ reconnectBaseDelayMs: number;
140
+ } | null;
141
+ /** Currently active (open) CRDT documents. */
142
+ activeDocuments: Array<{
143
+ documentId: string;
144
+ pageId: string;
145
+ connectionState: CRDTConnectionState;
146
+ isDirty: boolean;
147
+ /** Current Y.Doc state size in bytes. */
148
+ stateSizeBytes: number;
149
+ stateSizeFormatted: string;
150
+ /** Number of remote collaborators currently connected. */
151
+ collaboratorCount: number;
152
+ /** Names of connected collaborators. */
153
+ collaboratorNames: string[];
154
+ }>;
155
+ /** Total number of active documents. */
156
+ activeDocumentCount: number;
157
+ /** Offline storage statistics. */
158
+ offline: {
159
+ /** Number of documents stored for offline access. */
160
+ documentCount: number;
161
+ /** Max offline documents allowed. */
162
+ maxDocuments: number;
163
+ /** Total bytes stored across all offline documents. */
164
+ totalSizeBytes: number;
165
+ totalSizeFormatted: string;
166
+ /** Per-document offline storage details. */
167
+ documents: Array<{
168
+ documentId: string;
169
+ pageId: string;
170
+ stateSizeBytes: number;
171
+ stateSizeFormatted: string;
172
+ localUpdatedAt: string;
173
+ lastPersistedAt: string | null;
174
+ /** Whether this document has been persisted to Supabase since last local edit. */
175
+ syncedWithRemote: boolean;
176
+ }>;
177
+ };
178
+ /** Pending crash-recovery updates per active document. */
179
+ pendingUpdates: Array<{
180
+ documentId: string;
181
+ updateCount: number;
182
+ }>;
183
+ /** Total pending updates across all documents. */
184
+ totalPendingUpdates: number;
185
+ };
186
+ /** Engine configuration summary */
187
+ config: {
188
+ tableCount: number;
189
+ tableNames: string[];
190
+ syncDebounceMs: number;
191
+ syncIntervalMs: number;
192
+ tombstoneMaxAgeDays: number;
193
+ };
194
+ }
195
+ /**
196
+ * Capture a comprehensive diagnostics snapshot of the sync engine.
197
+ *
198
+ * This async function reads from all engine subsystems and returns a single
199
+ * JSON-serializable object. The async operations (queue reads, conflict
200
+ * history) are run in parallel for minimal latency.
201
+ *
202
+ * @returns A complete {@link DiagnosticsSnapshot}
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * const snapshot = await getDiagnostics();
207
+ * console.log(JSON.stringify(snapshot, null, 2));
208
+ * ```
209
+ */
210
+ export declare function getDiagnostics(): Promise<DiagnosticsSnapshot>;
211
+ /**
212
+ * Get CRDT subsystem diagnostics (async — reads IndexedDB for offline and pending data).
213
+ *
214
+ * Returns a comprehensive snapshot of the CRDT subsystem including active documents,
215
+ * their state sizes, connection states, collaborator counts, offline storage usage,
216
+ * and pending crash-recovery updates.
217
+ *
218
+ * If CRDT is not enabled, returns a minimal object with `enabled: false`.
219
+ *
220
+ * @returns CRDT diagnostics section of the {@link DiagnosticsSnapshot}.
221
+ */
222
+ export declare function getCRDTDiagnostics(): Promise<DiagnosticsSnapshot['crdt']>;
223
+ /**
224
+ * Get sync cycle and egress diagnostics (synchronous).
225
+ *
226
+ * @returns Sync stats, egress totals, and per-table breakdown
227
+ */
228
+ export declare function getSyncDiagnostics(): Pick<DiagnosticsSnapshot, 'sync' | 'egress'>;
229
+ /**
230
+ * Get realtime WebSocket connection diagnostics (synchronous).
231
+ *
232
+ * @returns Current realtime connection state and metadata
233
+ */
234
+ export declare function getRealtimeDiagnostics(): {
235
+ connectionState: RealtimeConnectionState;
236
+ healthy: boolean;
237
+ reconnectAttempts: number;
238
+ lastError: string | null;
239
+ userId: string | null;
240
+ deviceId: string;
241
+ recentlyProcessedCount: number;
242
+ operationInProgress: boolean;
243
+ reconnectScheduled: boolean;
244
+ };
245
+ /**
246
+ * Get pending sync queue diagnostics (async — reads IndexedDB).
247
+ *
248
+ * @returns Pending operation count, entity IDs, and breakdowns by table/operation type
249
+ */
250
+ export declare function getQueueDiagnostics(): Promise<DiagnosticsSnapshot['queue']>;
251
+ /**
252
+ * Get conflict resolution history diagnostics (async — reads IndexedDB).
253
+ *
254
+ * @returns Recent conflict entries and total count
255
+ */
256
+ export declare function getConflictDiagnostics(): Promise<{
257
+ recentHistory: ConflictHistoryEntry[];
258
+ totalCount: number;
259
+ }>;
260
+ /**
261
+ * Get engine-internal state diagnostics (synchronous).
262
+ *
263
+ * @returns Lock state, visibility, auth validation status
264
+ */
265
+ export declare function getEngineDiagnostics(): {
266
+ isTabVisible: boolean;
267
+ tabHiddenAt: string | null;
268
+ lockHeld: boolean;
269
+ lockHeldForMs: number | null;
270
+ recentlyModifiedCount: number;
271
+ wasOffline: boolean;
272
+ authValidatedAfterReconnect: boolean;
273
+ };
274
+ /**
275
+ * Get network connectivity diagnostics (synchronous).
276
+ *
277
+ * @returns Current online/offline status
278
+ */
279
+ export declare function getNetworkDiagnostics(): {
280
+ online: boolean;
281
+ };
282
+ /**
283
+ * Get error state diagnostics (synchronous).
284
+ *
285
+ * @returns Latest error info and recent error history
286
+ */
287
+ export declare function getErrorDiagnostics(): {
288
+ lastError: string | null;
289
+ lastErrorDetails: string | null;
290
+ recentErrors: SyncError[];
291
+ };
292
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,378 @@
1
+ /**
2
+ * @fileoverview Unified Diagnostics Module
3
+ *
4
+ * Provides a single entry point for inspecting the internal state of the
5
+ * stellar-drive sync system. The main `getDiagnostics()` function returns a
6
+ * comprehensive JSON snapshot covering sync cycles, egress, queue, realtime,
7
+ * network, conflict history, errors, and configuration.
8
+ *
9
+ * Each call returns a **point-in-time snapshot** — the data is not reactive.
10
+ * For a live dashboard, the consumer app should poll `getDiagnostics()` at the
11
+ * desired frequency (e.g., via `setInterval` or `onSyncComplete` callback).
12
+ *
13
+ * Sub-category functions (`getSyncDiagnostics`, `getRealtimeDiagnostics`, etc.)
14
+ * are also exported for lightweight access to specific sections without
15
+ * incurring the cost of async operations (queue/conflict reads).
16
+ *
17
+ * **Dependency direction:** This module imports from engine, realtime, queue,
18
+ * conflicts, config, deviceId, stores, and utils. No module imports from
19
+ * diagnostics — this guarantees zero circular dependency risk.
20
+ *
21
+ * @module diagnostics
22
+ * @see {@link ./engine.ts} for the core sync engine
23
+ * @see {@link ./realtime.ts} for WebSocket connection management
24
+ * @see {@link ./conflicts.ts} for conflict resolution history
25
+ */
26
+ import { _getEngineDiagnostics } from './engine';
27
+ import { _getRealtimeDiagnostics } from './realtime';
28
+ import { _getRecentConflictHistory } from './conflicts';
29
+ import { getPendingSync, getPendingEntityIds } from './queue';
30
+ import { getEngineConfig } from './config';
31
+ import { getDeviceId } from './deviceId';
32
+ import { syncStatusStore } from './stores/sync';
33
+ import { isOnline } from './stores/network';
34
+ import { formatBytes } from './utils';
35
+ import { get } from 'svelte/store';
36
+ import { isCRDTEnabled, getCRDTConfig } from './crdt/config';
37
+ import { getActiveProviderEntries } from './crdt/provider';
38
+ import { getOfflineDocuments, loadPendingUpdates } from './crdt/store';
39
+ import { getCollaborators } from './crdt/awareness';
40
+ import { encodeStateAsUpdate } from 'yjs';
41
+ // =============================================================================
42
+ // Main Diagnostics Function
43
+ // =============================================================================
44
+ /**
45
+ * Capture a comprehensive diagnostics snapshot of the sync engine.
46
+ *
47
+ * This async function reads from all engine subsystems and returns a single
48
+ * JSON-serializable object. The async operations (queue reads, conflict
49
+ * history) are run in parallel for minimal latency.
50
+ *
51
+ * @returns A complete {@link DiagnosticsSnapshot}
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const snapshot = await getDiagnostics();
56
+ * console.log(JSON.stringify(snapshot, null, 2));
57
+ * ```
58
+ */
59
+ export async function getDiagnostics() {
60
+ const config = getEngineConfig();
61
+ const engineState = _getEngineDiagnostics();
62
+ const realtimeState = _getRealtimeDiagnostics();
63
+ const syncState = get(syncStatusStore);
64
+ // Run async operations in parallel
65
+ const [queueData, conflictData, crdtData] = await Promise.all([
66
+ getQueueDiagnostics(),
67
+ getConflictDiagnostics(),
68
+ getCRDTDiagnostics()
69
+ ]);
70
+ // Build egress section with formatted values
71
+ const egressByTable = {};
72
+ for (const [table, stats] of Object.entries(engineState.egressStats.byTable)) {
73
+ const pct = engineState.egressStats.totalBytes > 0
74
+ ? ((stats.bytes / engineState.egressStats.totalBytes) * 100).toFixed(1)
75
+ : '0.0';
76
+ egressByTable[table] = {
77
+ bytes: stats.bytes,
78
+ formatted: formatBytes(stats.bytes),
79
+ records: stats.records,
80
+ percentage: `${pct}%`
81
+ };
82
+ }
83
+ // Count cycles in the last minute
84
+ const oneMinuteAgo = Date.now() - 60000;
85
+ const cyclesLastMinute = engineState.syncStats.filter((s) => new Date(s.timestamp).getTime() > oneMinuteAgo).length;
86
+ return {
87
+ timestamp: new Date().toISOString(),
88
+ prefix: config.prefix || 'engine',
89
+ deviceId: getDeviceId(),
90
+ sync: {
91
+ status: syncState.status,
92
+ totalCycles: engineState.totalSyncCycles,
93
+ lastSyncTime: syncState.lastSyncTime,
94
+ lastSuccessfulSyncTimestamp: engineState.lastSuccessfulSyncTimestamp
95
+ ? new Date(engineState.lastSuccessfulSyncTimestamp).toISOString()
96
+ : null,
97
+ syncMessage: syncState.syncMessage,
98
+ recentCycles: engineState.syncStats,
99
+ cyclesLastMinute,
100
+ hasHydrated: engineState.hasHydrated,
101
+ schemaValidated: engineState.schemaValidated,
102
+ pendingCount: syncState.pendingCount
103
+ },
104
+ egress: {
105
+ sessionStart: engineState.egressStats.sessionStart,
106
+ totalBytes: engineState.egressStats.totalBytes,
107
+ totalFormatted: formatBytes(engineState.egressStats.totalBytes),
108
+ totalRecords: engineState.egressStats.totalRecords,
109
+ byTable: egressByTable
110
+ },
111
+ queue: queueData,
112
+ realtime: realtimeState,
113
+ network: getNetworkDiagnostics(),
114
+ engine: {
115
+ isTabVisible: engineState.isTabVisible,
116
+ tabHiddenAt: engineState.tabHiddenAt ? new Date(engineState.tabHiddenAt).toISOString() : null,
117
+ lockHeld: engineState.lockHeld,
118
+ lockHeldForMs: engineState.lockHeldForMs,
119
+ recentlyModifiedCount: engineState.recentlyModifiedCount,
120
+ wasOffline: engineState.wasOffline,
121
+ authValidatedAfterReconnect: engineState.authValidatedAfterReconnect
122
+ },
123
+ conflicts: conflictData,
124
+ crdt: crdtData,
125
+ errors: getErrorDiagnostics(),
126
+ config: {
127
+ tableCount: config.tables.length,
128
+ tableNames: config.tables.map((t) => t.supabaseName),
129
+ syncDebounceMs: config.syncDebounceMs ?? 2000,
130
+ syncIntervalMs: config.syncIntervalMs ?? 900000,
131
+ tombstoneMaxAgeDays: config.tombstoneMaxAgeDays ?? 7
132
+ }
133
+ };
134
+ }
135
+ // =============================================================================
136
+ // CRDT Diagnostics
137
+ // =============================================================================
138
+ /**
139
+ * Get CRDT subsystem diagnostics (async — reads IndexedDB for offline and pending data).
140
+ *
141
+ * Returns a comprehensive snapshot of the CRDT subsystem including active documents,
142
+ * their state sizes, connection states, collaborator counts, offline storage usage,
143
+ * and pending crash-recovery updates.
144
+ *
145
+ * If CRDT is not enabled, returns a minimal object with `enabled: false`.
146
+ *
147
+ * @returns CRDT diagnostics section of the {@link DiagnosticsSnapshot}.
148
+ */
149
+ export async function getCRDTDiagnostics() {
150
+ if (!isCRDTEnabled()) {
151
+ return {
152
+ enabled: false,
153
+ config: null,
154
+ activeDocuments: [],
155
+ activeDocumentCount: 0,
156
+ offline: {
157
+ documentCount: 0,
158
+ maxDocuments: 0,
159
+ totalSizeBytes: 0,
160
+ totalSizeFormatted: '0 B',
161
+ documents: []
162
+ },
163
+ pendingUpdates: [],
164
+ totalPendingUpdates: 0
165
+ };
166
+ }
167
+ const config = getCRDTConfig();
168
+ /* Gather active document diagnostics. */
169
+ const activeDocuments = [];
170
+ const entries = getActiveProviderEntries();
171
+ /* Collect documentIds for pending update queries. */
172
+ const activeDocumentIds = [];
173
+ for (const [documentId, provider] of entries) {
174
+ activeDocumentIds.push(documentId);
175
+ /* Get Y.Doc state size (encode to measure byte length). */
176
+ const state = encodeStateAsUpdate(provider.doc);
177
+ const stateSizeBytes = state.byteLength;
178
+ /* Get collaborators for this document. */
179
+ const collaborators = getCollaborators(documentId);
180
+ activeDocuments.push({
181
+ documentId: provider.documentId,
182
+ pageId: provider.pageId,
183
+ connectionState: provider.connectionState,
184
+ isDirty: provider.isDirty,
185
+ stateSizeBytes,
186
+ stateSizeFormatted: formatBytes(stateSizeBytes),
187
+ collaboratorCount: collaborators.length,
188
+ collaboratorNames: collaborators.map((c) => c.name)
189
+ });
190
+ }
191
+ /* Gather pending updates for each active document. */
192
+ const pendingUpdatesResults = await Promise.all(activeDocumentIds.map(async (documentId) => {
193
+ const updates = await loadPendingUpdates(documentId);
194
+ return { documentId, updateCount: updates.length };
195
+ }));
196
+ const pendingUpdates = pendingUpdatesResults.filter((p) => p.updateCount > 0);
197
+ const totalPendingUpdates = pendingUpdates.reduce((sum, p) => sum + p.updateCount, 0);
198
+ /* Gather offline document diagnostics. */
199
+ const offlineDocs = await getOfflineDocuments();
200
+ let offlineTotalSize = 0;
201
+ const offlineDocDetails = offlineDocs.map((doc) => {
202
+ offlineTotalSize += doc.stateSize;
203
+ return {
204
+ documentId: doc.documentId,
205
+ pageId: doc.pageId,
206
+ stateSizeBytes: doc.stateSize,
207
+ stateSizeFormatted: formatBytes(doc.stateSize),
208
+ localUpdatedAt: doc.localUpdatedAt,
209
+ lastPersistedAt: doc.lastPersistedAt,
210
+ syncedWithRemote: doc.lastPersistedAt !== null && doc.lastPersistedAt >= doc.localUpdatedAt
211
+ };
212
+ });
213
+ return {
214
+ enabled: true,
215
+ config: {
216
+ supabaseTable: config.supabaseTable,
217
+ persistIntervalMs: config.persistIntervalMs,
218
+ broadcastDebounceMs: config.broadcastDebounceMs,
219
+ localSaveDebounceMs: config.localSaveDebounceMs,
220
+ cursorDebounceMs: config.cursorDebounceMs,
221
+ maxOfflineDocuments: config.maxOfflineDocuments,
222
+ maxBroadcastPayloadBytes: config.maxBroadcastPayloadBytes,
223
+ syncPeerTimeoutMs: config.syncPeerTimeoutMs,
224
+ maxReconnectAttempts: config.maxReconnectAttempts,
225
+ reconnectBaseDelayMs: config.reconnectBaseDelayMs
226
+ },
227
+ activeDocuments,
228
+ activeDocumentCount: activeDocuments.length,
229
+ offline: {
230
+ documentCount: offlineDocs.length,
231
+ maxDocuments: config.maxOfflineDocuments,
232
+ totalSizeBytes: offlineTotalSize,
233
+ totalSizeFormatted: formatBytes(offlineTotalSize),
234
+ documents: offlineDocDetails
235
+ },
236
+ pendingUpdates,
237
+ totalPendingUpdates
238
+ };
239
+ }
240
+ // =============================================================================
241
+ // Sub-Category Diagnostics Functions
242
+ // =============================================================================
243
+ /**
244
+ * Get sync cycle and egress diagnostics (synchronous).
245
+ *
246
+ * @returns Sync stats, egress totals, and per-table breakdown
247
+ */
248
+ export function getSyncDiagnostics() {
249
+ const engineState = _getEngineDiagnostics();
250
+ const syncState = get(syncStatusStore);
251
+ const egressByTable = {};
252
+ for (const [table, stats] of Object.entries(engineState.egressStats.byTable)) {
253
+ const pct = engineState.egressStats.totalBytes > 0
254
+ ? ((stats.bytes / engineState.egressStats.totalBytes) * 100).toFixed(1)
255
+ : '0.0';
256
+ egressByTable[table] = {
257
+ bytes: stats.bytes,
258
+ formatted: formatBytes(stats.bytes),
259
+ records: stats.records,
260
+ percentage: `${pct}%`
261
+ };
262
+ }
263
+ const oneMinuteAgo = Date.now() - 60000;
264
+ const cyclesLastMinute = engineState.syncStats.filter((s) => new Date(s.timestamp).getTime() > oneMinuteAgo).length;
265
+ return {
266
+ sync: {
267
+ status: syncState.status,
268
+ totalCycles: engineState.totalSyncCycles,
269
+ lastSyncTime: syncState.lastSyncTime,
270
+ lastSuccessfulSyncTimestamp: engineState.lastSuccessfulSyncTimestamp
271
+ ? new Date(engineState.lastSuccessfulSyncTimestamp).toISOString()
272
+ : null,
273
+ syncMessage: syncState.syncMessage,
274
+ recentCycles: engineState.syncStats,
275
+ cyclesLastMinute,
276
+ hasHydrated: engineState.hasHydrated,
277
+ schemaValidated: engineState.schemaValidated,
278
+ pendingCount: syncState.pendingCount
279
+ },
280
+ egress: {
281
+ sessionStart: engineState.egressStats.sessionStart,
282
+ totalBytes: engineState.egressStats.totalBytes,
283
+ totalFormatted: formatBytes(engineState.egressStats.totalBytes),
284
+ totalRecords: engineState.egressStats.totalRecords,
285
+ byTable: egressByTable
286
+ }
287
+ };
288
+ }
289
+ /**
290
+ * Get realtime WebSocket connection diagnostics (synchronous).
291
+ *
292
+ * @returns Current realtime connection state and metadata
293
+ */
294
+ export function getRealtimeDiagnostics() {
295
+ return _getRealtimeDiagnostics();
296
+ }
297
+ /**
298
+ * Get pending sync queue diagnostics (async — reads IndexedDB).
299
+ *
300
+ * @returns Pending operation count, entity IDs, and breakdowns by table/operation type
301
+ */
302
+ export async function getQueueDiagnostics() {
303
+ const pending = await getPendingSync();
304
+ const entityIds = await getPendingEntityIds();
305
+ // Breakdown by table
306
+ const byTable = {};
307
+ const byOperationType = {};
308
+ let oldestTimestamp = null;
309
+ let itemsInBackoff = 0;
310
+ for (const item of pending) {
311
+ byTable[item.table] = (byTable[item.table] || 0) + 1;
312
+ byOperationType[item.operationType] = (byOperationType[item.operationType] || 0) + 1;
313
+ if (!oldestTimestamp || item.timestamp < oldestTimestamp) {
314
+ oldestTimestamp = item.timestamp;
315
+ }
316
+ if (item.retries > 0) {
317
+ itemsInBackoff++;
318
+ }
319
+ }
320
+ return {
321
+ pendingOperations: pending.length,
322
+ pendingEntityIds: Array.from(entityIds),
323
+ byTable,
324
+ byOperationType,
325
+ oldestPendingTimestamp: oldestTimestamp,
326
+ itemsInBackoff
327
+ };
328
+ }
329
+ /**
330
+ * Get conflict resolution history diagnostics (async — reads IndexedDB).
331
+ *
332
+ * @returns Recent conflict entries and total count
333
+ */
334
+ export async function getConflictDiagnostics() {
335
+ const { entries, totalCount } = await _getRecentConflictHistory();
336
+ return { recentHistory: entries, totalCount };
337
+ }
338
+ /**
339
+ * Get engine-internal state diagnostics (synchronous).
340
+ *
341
+ * @returns Lock state, visibility, auth validation status
342
+ */
343
+ export function getEngineDiagnostics() {
344
+ const engineState = _getEngineDiagnostics();
345
+ return {
346
+ isTabVisible: engineState.isTabVisible,
347
+ tabHiddenAt: engineState.tabHiddenAt ? new Date(engineState.tabHiddenAt).toISOString() : null,
348
+ lockHeld: engineState.lockHeld,
349
+ lockHeldForMs: engineState.lockHeldForMs,
350
+ recentlyModifiedCount: engineState.recentlyModifiedCount,
351
+ wasOffline: engineState.wasOffline,
352
+ authValidatedAfterReconnect: engineState.authValidatedAfterReconnect
353
+ };
354
+ }
355
+ /**
356
+ * Get network connectivity diagnostics (synchronous).
357
+ *
358
+ * @returns Current online/offline status
359
+ */
360
+ export function getNetworkDiagnostics() {
361
+ return {
362
+ online: get(isOnline)
363
+ };
364
+ }
365
+ /**
366
+ * Get error state diagnostics (synchronous).
367
+ *
368
+ * @returns Latest error info and recent error history
369
+ */
370
+ export function getErrorDiagnostics() {
371
+ const syncState = get(syncStatusStore);
372
+ return {
373
+ lastError: syncState.lastError,
374
+ lastErrorDetails: syncState.lastErrorDetails,
375
+ recentErrors: syncState.syncErrors
376
+ };
377
+ }
378
+ //# sourceMappingURL=diagnostics.js.map