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.
- package/README.md +607 -0
- package/dist/actions/remoteChange.d.ts +204 -0
- package/dist/actions/remoteChange.d.ts.map +1 -0
- package/dist/actions/remoteChange.js +424 -0
- package/dist/actions/remoteChange.js.map +1 -0
- package/dist/actions/truncateTooltip.d.ts +56 -0
- package/dist/actions/truncateTooltip.d.ts.map +1 -0
- package/dist/actions/truncateTooltip.js +312 -0
- package/dist/actions/truncateTooltip.js.map +1 -0
- package/dist/auth/crypto.d.ts +41 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +50 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/deviceVerification.d.ts +283 -0
- package/dist/auth/deviceVerification.d.ts.map +1 -0
- package/dist/auth/deviceVerification.js +575 -0
- package/dist/auth/deviceVerification.js.map +1 -0
- package/dist/auth/displayUtils.d.ts +98 -0
- package/dist/auth/displayUtils.d.ts.map +1 -0
- package/dist/auth/displayUtils.js +145 -0
- package/dist/auth/displayUtils.js.map +1 -0
- package/dist/auth/loginGuard.d.ts +134 -0
- package/dist/auth/loginGuard.d.ts.map +1 -0
- package/dist/auth/loginGuard.js +276 -0
- package/dist/auth/loginGuard.js.map +1 -0
- package/dist/auth/offlineCredentials.d.ts +105 -0
- package/dist/auth/offlineCredentials.d.ts.map +1 -0
- package/dist/auth/offlineCredentials.js +176 -0
- package/dist/auth/offlineCredentials.js.map +1 -0
- package/dist/auth/offlineSession.d.ts +96 -0
- package/dist/auth/offlineSession.d.ts.map +1 -0
- package/dist/auth/offlineSession.js +145 -0
- package/dist/auth/offlineSession.js.map +1 -0
- package/dist/auth/resolveAuthState.d.ts +85 -0
- package/dist/auth/resolveAuthState.d.ts.map +1 -0
- package/dist/auth/resolveAuthState.js +249 -0
- package/dist/auth/resolveAuthState.js.map +1 -0
- package/dist/auth/singleUser.d.ts +498 -0
- package/dist/auth/singleUser.d.ts.map +1 -0
- package/dist/auth/singleUser.js +1282 -0
- package/dist/auth/singleUser.js.map +1 -0
- package/dist/bin/commands.d.ts +14 -0
- package/dist/bin/commands.d.ts.map +1 -0
- package/dist/bin/commands.js +68 -0
- package/dist/bin/commands.js.map +1 -0
- package/dist/bin/install-pwa.d.ts +41 -0
- package/dist/bin/install-pwa.d.ts.map +1 -0
- package/dist/bin/install-pwa.js +4594 -0
- package/dist/bin/install-pwa.js.map +1 -0
- package/dist/config.d.ts +249 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +395 -0
- package/dist/config.js.map +1 -0
- package/dist/conflicts.d.ts +306 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +807 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/crdt/awareness.d.ts +128 -0
- package/dist/crdt/awareness.d.ts.map +1 -0
- package/dist/crdt/awareness.js +284 -0
- package/dist/crdt/awareness.js.map +1 -0
- package/dist/crdt/channel.d.ts +165 -0
- package/dist/crdt/channel.d.ts.map +1 -0
- package/dist/crdt/channel.js +522 -0
- package/dist/crdt/channel.js.map +1 -0
- package/dist/crdt/config.d.ts +58 -0
- package/dist/crdt/config.d.ts.map +1 -0
- package/dist/crdt/config.js +123 -0
- package/dist/crdt/config.js.map +1 -0
- package/dist/crdt/helpers.d.ts +104 -0
- package/dist/crdt/helpers.d.ts.map +1 -0
- package/dist/crdt/helpers.js +116 -0
- package/dist/crdt/helpers.js.map +1 -0
- package/dist/crdt/offline.d.ts +58 -0
- package/dist/crdt/offline.d.ts.map +1 -0
- package/dist/crdt/offline.js +130 -0
- package/dist/crdt/offline.js.map +1 -0
- package/dist/crdt/persistence.d.ts +65 -0
- package/dist/crdt/persistence.d.ts.map +1 -0
- package/dist/crdt/persistence.js +171 -0
- package/dist/crdt/persistence.js.map +1 -0
- package/dist/crdt/provider.d.ts +109 -0
- package/dist/crdt/provider.d.ts.map +1 -0
- package/dist/crdt/provider.js +543 -0
- package/dist/crdt/provider.js.map +1 -0
- package/dist/crdt/store.d.ts +111 -0
- package/dist/crdt/store.d.ts.map +1 -0
- package/dist/crdt/store.js +158 -0
- package/dist/crdt/store.js.map +1 -0
- package/dist/crdt/types.d.ts +281 -0
- package/dist/crdt/types.d.ts.map +1 -0
- package/dist/crdt/types.js +26 -0
- package/dist/crdt/types.js.map +1 -0
- package/dist/data.d.ts +502 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +862 -0
- package/dist/data.js.map +1 -0
- package/dist/database.d.ts +153 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +325 -0
- package/dist/database.js.map +1 -0
- package/dist/debug.d.ts +87 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +135 -0
- package/dist/debug.js.map +1 -0
- package/dist/demo.d.ts +131 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +168 -0
- package/dist/demo.js.map +1 -0
- package/dist/deviceId.d.ts +47 -0
- package/dist/deviceId.d.ts.map +1 -0
- package/dist/deviceId.js +106 -0
- package/dist/deviceId.js.map +1 -0
- package/dist/diagnostics.d.ts +292 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +378 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/engine.d.ts +230 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +2636 -0
- package/dist/engine.js.map +1 -0
- package/dist/entries/actions.d.ts +16 -0
- package/dist/entries/actions.d.ts.map +1 -0
- package/dist/entries/actions.js +29 -0
- package/dist/entries/actions.js.map +1 -0
- package/dist/entries/auth.d.ts +19 -0
- package/dist/entries/auth.d.ts.map +1 -0
- package/dist/entries/auth.js +50 -0
- package/dist/entries/auth.js.map +1 -0
- package/dist/entries/config.d.ts +15 -0
- package/dist/entries/config.d.ts.map +1 -0
- package/dist/entries/config.js +20 -0
- package/dist/entries/config.js.map +1 -0
- package/dist/entries/crdt.d.ts +32 -0
- package/dist/entries/crdt.d.ts.map +1 -0
- package/dist/entries/crdt.js +52 -0
- package/dist/entries/crdt.js.map +1 -0
- package/dist/entries/kit.d.ts +22 -0
- package/dist/entries/kit.d.ts.map +1 -0
- package/dist/entries/kit.js +58 -0
- package/dist/entries/kit.js.map +1 -0
- package/dist/entries/stores.d.ts +22 -0
- package/dist/entries/stores.d.ts.map +1 -0
- package/dist/entries/stores.js +57 -0
- package/dist/entries/stores.js.map +1 -0
- package/dist/entries/types.d.ts +23 -0
- package/dist/entries/types.d.ts.map +1 -0
- package/dist/entries/types.js +12 -0
- package/dist/entries/types.js.map +1 -0
- package/dist/entries/utils.d.ts +12 -0
- package/dist/entries/utils.d.ts.map +1 -0
- package/dist/entries/utils.js +42 -0
- package/dist/entries/utils.js.map +1 -0
- package/dist/entries/vite.d.ts +20 -0
- package/dist/entries/vite.d.ts.map +1 -0
- package/dist/entries/vite.js +26 -0
- package/dist/entries/vite.js.map +1 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +234 -0
- package/dist/index.js.map +1 -0
- package/dist/kit/auth.d.ts +80 -0
- package/dist/kit/auth.d.ts.map +1 -0
- package/dist/kit/auth.js +75 -0
- package/dist/kit/auth.js.map +1 -0
- package/dist/kit/confirm.d.ts +111 -0
- package/dist/kit/confirm.d.ts.map +1 -0
- package/dist/kit/confirm.js +169 -0
- package/dist/kit/confirm.js.map +1 -0
- package/dist/kit/loads.d.ts +187 -0
- package/dist/kit/loads.d.ts.map +1 -0
- package/dist/kit/loads.js +208 -0
- package/dist/kit/loads.js.map +1 -0
- package/dist/kit/server.d.ts +175 -0
- package/dist/kit/server.d.ts.map +1 -0
- package/dist/kit/server.js +297 -0
- package/dist/kit/server.js.map +1 -0
- package/dist/kit/sw.d.ts +176 -0
- package/dist/kit/sw.d.ts.map +1 -0
- package/dist/kit/sw.js +320 -0
- package/dist/kit/sw.js.map +1 -0
- package/dist/queue.d.ts +306 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +925 -0
- package/dist/queue.js.map +1 -0
- package/dist/realtime.d.ts +280 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +1031 -0
- package/dist/realtime.js.map +1 -0
- package/dist/runtime/runtimeConfig.d.ts +110 -0
- package/dist/runtime/runtimeConfig.d.ts.map +1 -0
- package/dist/runtime/runtimeConfig.js +260 -0
- package/dist/runtime/runtimeConfig.js.map +1 -0
- package/dist/schema.d.ts +150 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +891 -0
- package/dist/schema.js.map +1 -0
- package/dist/stores/authState.d.ts +204 -0
- package/dist/stores/authState.d.ts.map +1 -0
- package/dist/stores/authState.js +336 -0
- package/dist/stores/authState.js.map +1 -0
- package/dist/stores/factories.d.ts +140 -0
- package/dist/stores/factories.d.ts.map +1 -0
- package/dist/stores/factories.js +157 -0
- package/dist/stores/factories.js.map +1 -0
- package/dist/stores/network.d.ts +48 -0
- package/dist/stores/network.d.ts.map +1 -0
- package/dist/stores/network.js +261 -0
- package/dist/stores/network.js.map +1 -0
- package/dist/stores/remoteChanges.d.ts +417 -0
- package/dist/stores/remoteChanges.d.ts.map +1 -0
- package/dist/stores/remoteChanges.js +626 -0
- package/dist/stores/remoteChanges.js.map +1 -0
- package/dist/stores/sync.d.ts +165 -0
- package/dist/stores/sync.d.ts.map +1 -0
- package/dist/stores/sync.js +275 -0
- package/dist/stores/sync.js.map +1 -0
- package/dist/supabase/auth.d.ts +219 -0
- package/dist/supabase/auth.d.ts.map +1 -0
- package/dist/supabase/auth.js +459 -0
- package/dist/supabase/auth.js.map +1 -0
- package/dist/supabase/client.d.ts +88 -0
- package/dist/supabase/client.d.ts.map +1 -0
- package/dist/supabase/client.js +313 -0
- package/dist/supabase/client.js.map +1 -0
- package/dist/supabase/validate.d.ts +118 -0
- package/dist/supabase/validate.d.ts.map +1 -0
- package/dist/supabase/validate.js +208 -0
- package/dist/supabase/validate.js.map +1 -0
- package/dist/sw/build/vite-plugin.d.ts +149 -0
- package/dist/sw/build/vite-plugin.d.ts.map +1 -0
- package/dist/sw/build/vite-plugin.js +517 -0
- package/dist/sw/build/vite-plugin.js.map +1 -0
- package/dist/sw/sw.js +664 -0
- package/dist/types.d.ts +363 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +85 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +156 -0
- package/dist/utils.js.map +1 -0
- package/package.json +117 -0
- package/src/components/DeferredChangesBanner.svelte +477 -0
- package/src/components/DemoBanner.svelte +110 -0
- 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
|