@framers/sql-storage-adapter 0.7.2 → 0.7.3
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 +20 -0
- package/dist/adapters/electron/preload.cjs +289 -0
- package/dist/adapters/electron/renderer.d.ts +9 -0
- package/dist/adapters/electron/renderer.d.ts.map +1 -0
- package/dist/adapters/electron/renderer.js +8 -0
- package/dist/adapters/electron/renderer.js.map +1 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.d.ts +2 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.js +133 -0
- package/dist/cjs/adapters/baseStorageAdapter.d.ts +368 -0
- package/dist/cjs/adapters/baseStorageAdapter.js +744 -0
- package/dist/cjs/adapters/betterSqliteAdapter.d.ts +124 -0
- package/dist/cjs/adapters/betterSqliteAdapter.js +334 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.d.ts +83 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.js +250 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.d.ts +251 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.js +392 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.d.ts +177 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.js +283 -0
- package/dist/cjs/adapters/electron/index.d.ts +74 -0
- package/dist/cjs/adapters/electron/index.js +86 -0
- package/dist/cjs/adapters/electron/ipc/channels.d.ts +196 -0
- package/dist/cjs/adapters/electron/ipc/channels.js +127 -0
- package/dist/cjs/adapters/electron/ipc/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/ipc/index.js +38 -0
- package/dist/cjs/adapters/electron/ipc/protocol.d.ts +78 -0
- package/dist/cjs/adapters/electron/ipc/protocol.js +341 -0
- package/dist/cjs/adapters/electron/ipc/types.d.ts +248 -0
- package/dist/cjs/adapters/electron/ipc/types.js +15 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.d.ts +184 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.js +441 -0
- package/dist/cjs/adapters/electron/migration/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/migration/index.js +29 -0
- package/dist/cjs/adapters/electron/preload.d.ts +126 -0
- package/dist/cjs/adapters/electron/preload.js +233 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.d.ts +214 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.js +359 -0
- package/dist/cjs/adapters/electron/recovery/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/recovery/index.js +34 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.d.ts +186 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.js +263 -0
- package/dist/cjs/adapters/electron/renderer.d.ts +9 -0
- package/dist/cjs/adapters/electron/renderer.js +33 -0
- package/dist/cjs/adapters/electron/window/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/window/index.js +29 -0
- package/dist/cjs/adapters/electron/window/windowManager.d.ts +190 -0
- package/dist/cjs/adapters/electron/window/windowManager.js +321 -0
- package/dist/cjs/adapters/indexedDbAdapter.d.ts +490 -0
- package/dist/cjs/adapters/indexedDbAdapter.js +993 -0
- package/dist/cjs/adapters/postgresAdapter.d.ts +424 -0
- package/dist/cjs/adapters/postgresAdapter.js +889 -0
- package/dist/cjs/adapters/sqlJsAdapter.d.ts +64 -0
- package/dist/cjs/adapters/sqlJsAdapter.js +268 -0
- package/dist/cjs/adapters/supabase.d.ts +58 -0
- package/dist/cjs/adapters/supabase.js +401 -0
- package/dist/cjs/codecs/BrowserBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/BrowserBlobCodec.js +57 -0
- package/dist/cjs/codecs/NodeBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/NodeBlobCodec.js +59 -0
- package/dist/cjs/core/contracts/blobCodec.d.ts +15 -0
- package/dist/cjs/core/contracts/blobCodec.js +15 -0
- package/dist/cjs/core/contracts/context.d.ts +221 -0
- package/dist/cjs/core/contracts/context.js +15 -0
- package/dist/cjs/core/contracts/dialect.d.ts +154 -0
- package/dist/cjs/core/contracts/dialect.js +15 -0
- package/dist/cjs/core/contracts/events.d.ts +225 -0
- package/dist/cjs/core/contracts/events.js +15 -0
- package/dist/cjs/core/contracts/exporter.d.ts +13 -0
- package/dist/cjs/core/contracts/exporter.js +15 -0
- package/dist/cjs/core/contracts/extensions.d.ts +73 -0
- package/dist/cjs/core/contracts/extensions.js +15 -0
- package/dist/cjs/core/contracts/features.d.ts +25 -0
- package/dist/cjs/core/contracts/features.js +61 -0
- package/dist/cjs/core/contracts/fts.d.ts +66 -0
- package/dist/cjs/core/contracts/fts.js +15 -0
- package/dist/cjs/core/contracts/hooks.d.ts +481 -0
- package/dist/cjs/core/contracts/hooks.js +140 -0
- package/dist/cjs/core/contracts/index.d.ts +260 -0
- package/dist/cjs/core/contracts/index.js +41 -0
- package/dist/cjs/core/contracts/limitations.d.ts +46 -0
- package/dist/cjs/core/contracts/limitations.js +218 -0
- package/dist/cjs/core/contracts/performance.d.ts +308 -0
- package/dist/cjs/core/contracts/performance.js +144 -0
- package/dist/cjs/core/database.d.ts +220 -0
- package/dist/cjs/core/database.js +136 -0
- package/dist/cjs/core/portable.d.ts +23 -0
- package/dist/cjs/core/portable.js +66 -0
- package/dist/cjs/core/resolver.d.ts +77 -0
- package/dist/cjs/core/resolver.js +235 -0
- package/dist/cjs/dialects/PostgresDialect.d.ts +35 -0
- package/dist/cjs/dialects/PostgresDialect.js +143 -0
- package/dist/cjs/dialects/SqliteDialect.d.ts +35 -0
- package/dist/cjs/dialects/SqliteDialect.js +142 -0
- package/dist/cjs/dialects/shared.d.ts +17 -0
- package/dist/cjs/dialects/shared.js +90 -0
- package/dist/cjs/exporters/PostgresExporter.d.ts +14 -0
- package/dist/cjs/exporters/PostgresExporter.js +61 -0
- package/dist/cjs/exporters/SqliteFileExporter.d.ts +16 -0
- package/dist/cjs/exporters/SqliteFileExporter.js +43 -0
- package/dist/cjs/features/backup/cloudBackup.d.ts +219 -0
- package/dist/cjs/features/backup/cloudBackup.js +209 -0
- package/dist/cjs/features/kv/idb.d.ts +79 -0
- package/dist/cjs/features/kv/idb.js +32 -0
- package/dist/cjs/features/kv/index.d.ts +14 -0
- package/dist/cjs/features/kv/index.js +40 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.d.ts +77 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.js +116 -0
- package/dist/cjs/features/kv/keyValueStore.d.ts +48 -0
- package/dist/cjs/features/kv/keyValueStore.js +67 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.d.ts +34 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.js +81 -0
- package/dist/cjs/features/kv/valueCodec.d.ts +35 -0
- package/dist/cjs/features/kv/valueCodec.js +426 -0
- package/dist/cjs/features/migrations/dataExport.d.ts +77 -0
- package/dist/cjs/features/migrations/dataExport.js +194 -0
- package/dist/cjs/features/migrations/dataImport.d.ts +54 -0
- package/dist/cjs/features/migrations/dataImport.js +300 -0
- package/dist/cjs/features/migrations/migration.d.ts +89 -0
- package/dist/cjs/features/migrations/migration.js +187 -0
- package/dist/cjs/features/migrations/schemaMigrations.d.ts +93 -0
- package/dist/cjs/features/migrations/schemaMigrations.js +220 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.d.ts +222 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.js +366 -0
- package/dist/cjs/features/sync/conflicts/index.d.ts +9 -0
- package/dist/cjs/features/sync/conflicts/index.js +21 -0
- package/dist/cjs/features/sync/crossPlatformSync.d.ts +281 -0
- package/dist/cjs/features/sync/crossPlatformSync.js +551 -0
- package/dist/cjs/features/sync/devices/deviceManager.d.ts +243 -0
- package/dist/cjs/features/sync/devices/deviceManager.js +450 -0
- package/dist/cjs/features/sync/devices/index.d.ts +10 -0
- package/dist/cjs/features/sync/devices/index.js +21 -0
- package/dist/cjs/features/sync/index.d.ts +37 -0
- package/dist/cjs/features/sync/index.js +111 -0
- package/dist/cjs/features/sync/protocol/index.d.ts +11 -0
- package/dist/cjs/features/sync/protocol/index.js +23 -0
- package/dist/cjs/features/sync/protocol/messages.d.ts +348 -0
- package/dist/cjs/features/sync/protocol/messages.js +215 -0
- package/dist/cjs/features/sync/protocol/vectorClock.d.ts +164 -0
- package/dist/cjs/features/sync/protocol/vectorClock.js +256 -0
- package/dist/cjs/features/sync/syncManager.d.ts +348 -0
- package/dist/cjs/features/sync/syncManager.js +455 -0
- package/dist/cjs/features/sync/tables/index.d.ts +10 -0
- package/dist/cjs/features/sync/tables/index.js +21 -0
- package/dist/cjs/features/sync/tables/syncLogManager.d.ts +216 -0
- package/dist/cjs/features/sync/tables/syncLogManager.js +423 -0
- package/dist/cjs/features/sync/transport/httpTransport.d.ts +123 -0
- package/dist/cjs/features/sync/transport/httpTransport.js +346 -0
- package/dist/cjs/features/sync/transport/index.d.ts +12 -0
- package/dist/cjs/features/sync/transport/index.js +25 -0
- package/dist/cjs/features/sync/transport/transport.d.ts +259 -0
- package/dist/cjs/features/sync/transport/transport.js +163 -0
- package/dist/cjs/features/sync/transport/websocketTransport.d.ts +126 -0
- package/dist/cjs/features/sync/transport/websocketTransport.js +342 -0
- package/dist/cjs/fts/PostgresFts.d.ts +29 -0
- package/dist/cjs/fts/PostgresFts.js +77 -0
- package/dist/cjs/fts/SqliteFts5.d.ts +22 -0
- package/dist/cjs/fts/SqliteFts5.js +58 -0
- package/dist/cjs/index.d.ts +30 -0
- package/dist/cjs/index.js +101 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/shared/connectionGate.d.ts +94 -0
- package/dist/cjs/shared/connectionGate.js +77 -0
- package/dist/cjs/shared/errors.d.ts +23 -0
- package/dist/cjs/shared/errors.js +45 -0
- package/dist/cjs/shared/logger.d.ts +26 -0
- package/dist/cjs/shared/logger.js +40 -0
- package/dist/cjs/shared/parameterUtils.d.ts +16 -0
- package/dist/cjs/shared/parameterUtils.js +59 -0
- package/dist/cjs/shared/postgresTarget.d.ts +11 -0
- package/dist/cjs/shared/postgresTarget.js +30 -0
- package/dist/cjs/shared/segment.d.ts +43 -0
- package/dist/cjs/shared/segment.js +63 -0
- package/dist/cjs/shared/serializedTransaction.d.ts +14 -0
- package/dist/cjs/shared/serializedTransaction.js +74 -0
- package/dist/cjs/shared/splitSqlStatements.d.ts +27 -0
- package/dist/cjs/shared/splitSqlStatements.js +163 -0
- package/dist/cjs/shared/sqlPlaceholders.d.ts +48 -0
- package/dist/cjs/shared/sqlPlaceholders.js +263 -0
- package/dist/cjs/types/index.d.ts +29 -0
- package/dist/cjs/types/index.js +33 -0
- package/dist/core/portable.d.ts +23 -0
- package/dist/core/portable.d.ts.map +1 -0
- package/dist/core/portable.js +23 -0
- package/dist/core/portable.js.map +1 -0
- package/package.json +139 -24
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Registry and Presence Management.
|
|
3
|
+
*
|
|
4
|
+
* Manages device registration, presence tracking, and peer discovery
|
|
5
|
+
* for cross-platform sync.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { StorageAdapter } from '../../../core/contracts/index.js';
|
|
10
|
+
import type { VectorClock } from '../protocol/vectorClock.js';
|
|
11
|
+
/**
|
|
12
|
+
* Device types supported for sync.
|
|
13
|
+
*/
|
|
14
|
+
export type DeviceType = 'electron' | 'capacitor' | 'browser' | 'server' | 'unknown';
|
|
15
|
+
/**
|
|
16
|
+
* Device presence status.
|
|
17
|
+
*/
|
|
18
|
+
export type PresenceStatus = 'online' | 'away' | 'offline' | 'syncing';
|
|
19
|
+
/**
|
|
20
|
+
* Information about a registered device.
|
|
21
|
+
*/
|
|
22
|
+
export interface DeviceInfo {
|
|
23
|
+
/** Unique device identifier */
|
|
24
|
+
deviceId: string;
|
|
25
|
+
/** Device type/platform */
|
|
26
|
+
deviceType: DeviceType;
|
|
27
|
+
/** Human-readable device name */
|
|
28
|
+
deviceName: string;
|
|
29
|
+
/** Device's current vector clock */
|
|
30
|
+
vectorClock: VectorClock;
|
|
31
|
+
/** When the device was first registered */
|
|
32
|
+
createdAt: number;
|
|
33
|
+
/** When the device was last seen */
|
|
34
|
+
lastSeenAt: number;
|
|
35
|
+
/** Current presence status */
|
|
36
|
+
presence: PresenceStatus;
|
|
37
|
+
/** Device capabilities */
|
|
38
|
+
capabilities: DeviceCapabilities;
|
|
39
|
+
/** Additional metadata */
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Capabilities a device supports.
|
|
44
|
+
*/
|
|
45
|
+
export interface DeviceCapabilities {
|
|
46
|
+
/** Supports real-time sync */
|
|
47
|
+
realTimeSync: boolean;
|
|
48
|
+
/** Supports background sync */
|
|
49
|
+
backgroundSync: boolean;
|
|
50
|
+
/** Supports push notifications */
|
|
51
|
+
pushNotifications: boolean;
|
|
52
|
+
/** Supports offline mode */
|
|
53
|
+
offlineMode: boolean;
|
|
54
|
+
/** Maximum batch size */
|
|
55
|
+
maxBatchSize: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Device registration options.
|
|
59
|
+
*/
|
|
60
|
+
export interface DeviceRegistrationOptions {
|
|
61
|
+
/** Device name (auto-generated if not provided) */
|
|
62
|
+
name?: string;
|
|
63
|
+
/** Device type (auto-detected if not provided) */
|
|
64
|
+
type?: DeviceType;
|
|
65
|
+
/** Device capabilities */
|
|
66
|
+
capabilities?: Partial<DeviceCapabilities>;
|
|
67
|
+
/** Additional metadata */
|
|
68
|
+
metadata?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Device presence event.
|
|
72
|
+
*/
|
|
73
|
+
export interface PresenceEvent {
|
|
74
|
+
deviceId: string;
|
|
75
|
+
previousStatus: PresenceStatus;
|
|
76
|
+
currentStatus: PresenceStatus;
|
|
77
|
+
timestamp: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Device manager event types.
|
|
81
|
+
*/
|
|
82
|
+
export type DeviceManagerEventType = 'deviceRegistered' | 'deviceUpdated' | 'deviceRemoved' | 'presenceChanged' | 'peerDiscovered' | 'peerLost';
|
|
83
|
+
/**
|
|
84
|
+
* Device manager event payloads.
|
|
85
|
+
*/
|
|
86
|
+
export interface DeviceManagerEvents {
|
|
87
|
+
deviceRegistered: DeviceInfo;
|
|
88
|
+
deviceUpdated: {
|
|
89
|
+
device: DeviceInfo;
|
|
90
|
+
changes: Partial<DeviceInfo>;
|
|
91
|
+
};
|
|
92
|
+
deviceRemoved: {
|
|
93
|
+
deviceId: string;
|
|
94
|
+
reason: string;
|
|
95
|
+
};
|
|
96
|
+
presenceChanged: PresenceEvent;
|
|
97
|
+
peerDiscovered: DeviceInfo;
|
|
98
|
+
peerLost: {
|
|
99
|
+
deviceId: string;
|
|
100
|
+
lastSeenAt: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Device manager configuration.
|
|
105
|
+
*/
|
|
106
|
+
export interface DeviceManagerOptions {
|
|
107
|
+
/** Storage adapter for persisting device registry */
|
|
108
|
+
adapter: StorageAdapter;
|
|
109
|
+
/** Presence timeout in milliseconds (default: 60000) */
|
|
110
|
+
presenceTimeout?: number;
|
|
111
|
+
/** Heartbeat interval in milliseconds (default: 30000) */
|
|
112
|
+
heartbeatInterval?: number;
|
|
113
|
+
/** Auto-detect device info */
|
|
114
|
+
autoDetect?: boolean;
|
|
115
|
+
/** Initial device name */
|
|
116
|
+
deviceName?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Device registry and presence manager.
|
|
120
|
+
*
|
|
121
|
+
* Manages device registration, presence tracking, and peer discovery
|
|
122
|
+
* across all connected devices in a sync cluster.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const manager = new DeviceManager({
|
|
127
|
+
* adapter: db,
|
|
128
|
+
* deviceName: 'MacBook Pro',
|
|
129
|
+
* });
|
|
130
|
+
*
|
|
131
|
+
* await manager.initialize();
|
|
132
|
+
*
|
|
133
|
+
* // Get current device
|
|
134
|
+
* const thisDevice = manager.getCurrentDevice();
|
|
135
|
+
*
|
|
136
|
+
* // Get all known peers
|
|
137
|
+
* const peers = await manager.getPeers();
|
|
138
|
+
*
|
|
139
|
+
* // Listen for presence changes
|
|
140
|
+
* manager.on('presenceChanged', (event) => {
|
|
141
|
+
* console.log(`${event.deviceId} is now ${event.currentStatus}`);
|
|
142
|
+
* });
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare class DeviceManager {
|
|
146
|
+
private _adapter;
|
|
147
|
+
private _options;
|
|
148
|
+
private _currentDevice;
|
|
149
|
+
private _peers;
|
|
150
|
+
private _heartbeatTimer;
|
|
151
|
+
private _listeners;
|
|
152
|
+
private _initialized;
|
|
153
|
+
constructor(options: DeviceManagerOptions);
|
|
154
|
+
/**
|
|
155
|
+
* Initialize the device manager.
|
|
156
|
+
*/
|
|
157
|
+
initialize(): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Get the current device.
|
|
160
|
+
*/
|
|
161
|
+
getCurrentDevice(): DeviceInfo | null;
|
|
162
|
+
/**
|
|
163
|
+
* Get all known peer devices.
|
|
164
|
+
*/
|
|
165
|
+
getPeers(): Promise<DeviceInfo[]>;
|
|
166
|
+
/**
|
|
167
|
+
* Get online peers only.
|
|
168
|
+
*/
|
|
169
|
+
getOnlinePeers(): Promise<DeviceInfo[]>;
|
|
170
|
+
/**
|
|
171
|
+
* Get a specific device by ID.
|
|
172
|
+
*/
|
|
173
|
+
getDevice(deviceId: string): Promise<DeviceInfo | null>;
|
|
174
|
+
/**
|
|
175
|
+
* Update the current device's presence.
|
|
176
|
+
*/
|
|
177
|
+
updatePresence(status: PresenceStatus): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Update the current device's vector clock.
|
|
180
|
+
*/
|
|
181
|
+
updateVectorClock(clock: VectorClock): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Register or update a peer device.
|
|
184
|
+
*/
|
|
185
|
+
registerPeer(peerInfo: DeviceInfo): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Remove a device from the registry.
|
|
188
|
+
*/
|
|
189
|
+
removeDevice(deviceId: string, reason?: string): Promise<void>;
|
|
190
|
+
/**
|
|
191
|
+
* Check for stale peers and update their presence.
|
|
192
|
+
*/
|
|
193
|
+
checkStaleDevices(): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* Listen for device manager events.
|
|
196
|
+
*/
|
|
197
|
+
on<T extends DeviceManagerEventType>(event: T, listener: (data: DeviceManagerEvents[T]) => void): () => void;
|
|
198
|
+
/**
|
|
199
|
+
* Dispose of the device manager.
|
|
200
|
+
*/
|
|
201
|
+
dispose(): Promise<void>;
|
|
202
|
+
/**
|
|
203
|
+
* Emit an event to listeners.
|
|
204
|
+
*/
|
|
205
|
+
private _emit;
|
|
206
|
+
/**
|
|
207
|
+
* Ensure device tables exist.
|
|
208
|
+
*/
|
|
209
|
+
private _ensureTables;
|
|
210
|
+
/**
|
|
211
|
+
* Initialize the current device.
|
|
212
|
+
*/
|
|
213
|
+
private _initializeCurrentDevice;
|
|
214
|
+
/**
|
|
215
|
+
* Load stored device ID.
|
|
216
|
+
*/
|
|
217
|
+
private _loadStoredDeviceId;
|
|
218
|
+
/**
|
|
219
|
+
* Store device ID for persistence.
|
|
220
|
+
*/
|
|
221
|
+
private _storeDeviceId;
|
|
222
|
+
/**
|
|
223
|
+
* Load a device from the database.
|
|
224
|
+
*/
|
|
225
|
+
private _loadDevice;
|
|
226
|
+
/**
|
|
227
|
+
* Save a device to the database.
|
|
228
|
+
*/
|
|
229
|
+
private _saveDevice;
|
|
230
|
+
/**
|
|
231
|
+
* Load all peer devices.
|
|
232
|
+
*/
|
|
233
|
+
private _loadPeers;
|
|
234
|
+
/**
|
|
235
|
+
* Start heartbeat timer.
|
|
236
|
+
*/
|
|
237
|
+
private _startHeartbeat;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Create a device manager instance.
|
|
241
|
+
*/
|
|
242
|
+
export declare const createDeviceManager: (options: DeviceManagerOptions) => DeviceManager;
|
|
243
|
+
//# sourceMappingURL=deviceManager.d.ts.map
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var deviceManager_exports = {};
|
|
19
|
+
__export(deviceManager_exports, {
|
|
20
|
+
DeviceManager: () => DeviceManager,
|
|
21
|
+
createDeviceManager: () => createDeviceManager
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(deviceManager_exports);
|
|
24
|
+
var import_vectorClock = require("../protocol/vectorClock.js");
|
|
25
|
+
const detectDeviceType = () => {
|
|
26
|
+
if (typeof process !== "undefined" && process.versions?.electron) {
|
|
27
|
+
return "electron";
|
|
28
|
+
}
|
|
29
|
+
if (typeof window !== "undefined") {
|
|
30
|
+
const win = window;
|
|
31
|
+
if (win.Capacitor?.isNativePlatform?.()) {
|
|
32
|
+
return "capacitor";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (typeof process !== "undefined" && process.versions?.node && typeof window === "undefined") {
|
|
36
|
+
return "server";
|
|
37
|
+
}
|
|
38
|
+
if (typeof window !== "undefined") {
|
|
39
|
+
return "browser";
|
|
40
|
+
}
|
|
41
|
+
return "unknown";
|
|
42
|
+
};
|
|
43
|
+
const generateDeviceName = (type) => {
|
|
44
|
+
const timestamp = Date.now().toString(36).slice(-4);
|
|
45
|
+
switch (type) {
|
|
46
|
+
case "electron":
|
|
47
|
+
return `Desktop-${timestamp}`;
|
|
48
|
+
case "capacitor":
|
|
49
|
+
return `Mobile-${timestamp}`;
|
|
50
|
+
case "browser":
|
|
51
|
+
return `Browser-${timestamp}`;
|
|
52
|
+
case "server":
|
|
53
|
+
return `Server-${timestamp}`;
|
|
54
|
+
default:
|
|
55
|
+
return `Device-${timestamp}`;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const getDefaultCapabilities = (type) => {
|
|
59
|
+
switch (type) {
|
|
60
|
+
case "electron":
|
|
61
|
+
return {
|
|
62
|
+
realTimeSync: true,
|
|
63
|
+
backgroundSync: true,
|
|
64
|
+
pushNotifications: true,
|
|
65
|
+
offlineMode: true,
|
|
66
|
+
maxBatchSize: 1e3
|
|
67
|
+
};
|
|
68
|
+
case "capacitor":
|
|
69
|
+
return {
|
|
70
|
+
realTimeSync: true,
|
|
71
|
+
backgroundSync: true,
|
|
72
|
+
pushNotifications: true,
|
|
73
|
+
offlineMode: true,
|
|
74
|
+
maxBatchSize: 500
|
|
75
|
+
};
|
|
76
|
+
case "browser":
|
|
77
|
+
return {
|
|
78
|
+
realTimeSync: true,
|
|
79
|
+
backgroundSync: false,
|
|
80
|
+
pushNotifications: false,
|
|
81
|
+
offlineMode: true,
|
|
82
|
+
maxBatchSize: 500
|
|
83
|
+
};
|
|
84
|
+
case "server":
|
|
85
|
+
return {
|
|
86
|
+
realTimeSync: true,
|
|
87
|
+
backgroundSync: true,
|
|
88
|
+
pushNotifications: false,
|
|
89
|
+
offlineMode: false,
|
|
90
|
+
maxBatchSize: 1e4
|
|
91
|
+
};
|
|
92
|
+
default:
|
|
93
|
+
return {
|
|
94
|
+
realTimeSync: false,
|
|
95
|
+
backgroundSync: false,
|
|
96
|
+
pushNotifications: false,
|
|
97
|
+
offlineMode: false,
|
|
98
|
+
maxBatchSize: 100
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
class DeviceManager {
|
|
103
|
+
constructor(options) {
|
|
104
|
+
this._currentDevice = null;
|
|
105
|
+
this._peers = /* @__PURE__ */ new Map();
|
|
106
|
+
this._heartbeatTimer = null;
|
|
107
|
+
this._listeners = /* @__PURE__ */ new Map();
|
|
108
|
+
this._initialized = false;
|
|
109
|
+
this._adapter = options.adapter;
|
|
110
|
+
this._options = {
|
|
111
|
+
adapter: options.adapter,
|
|
112
|
+
presenceTimeout: options.presenceTimeout ?? 6e4,
|
|
113
|
+
heartbeatInterval: options.heartbeatInterval ?? 3e4,
|
|
114
|
+
autoDetect: options.autoDetect ?? true,
|
|
115
|
+
deviceName: options.deviceName ?? ""
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Initialize the device manager.
|
|
120
|
+
*/
|
|
121
|
+
async initialize() {
|
|
122
|
+
if (this._initialized)
|
|
123
|
+
return;
|
|
124
|
+
await this._ensureTables();
|
|
125
|
+
await this._initializeCurrentDevice();
|
|
126
|
+
await this._loadPeers();
|
|
127
|
+
this._startHeartbeat();
|
|
128
|
+
this._initialized = true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the current device.
|
|
132
|
+
*/
|
|
133
|
+
getCurrentDevice() {
|
|
134
|
+
return this._currentDevice;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get all known peer devices.
|
|
138
|
+
*/
|
|
139
|
+
async getPeers() {
|
|
140
|
+
return Array.from(this._peers.values());
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get online peers only.
|
|
144
|
+
*/
|
|
145
|
+
async getOnlinePeers() {
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
const timeout = this._options.presenceTimeout;
|
|
148
|
+
return Array.from(this._peers.values()).filter((peer) => {
|
|
149
|
+
return now - peer.lastSeenAt < timeout;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get a specific device by ID.
|
|
154
|
+
*/
|
|
155
|
+
async getDevice(deviceId) {
|
|
156
|
+
if (deviceId === this._currentDevice?.deviceId) {
|
|
157
|
+
return this._currentDevice;
|
|
158
|
+
}
|
|
159
|
+
return this._peers.get(deviceId) ?? null;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Update the current device's presence.
|
|
163
|
+
*/
|
|
164
|
+
async updatePresence(status) {
|
|
165
|
+
if (!this._currentDevice)
|
|
166
|
+
return;
|
|
167
|
+
const previous = this._currentDevice.presence;
|
|
168
|
+
this._currentDevice.presence = status;
|
|
169
|
+
this._currentDevice.lastSeenAt = Date.now();
|
|
170
|
+
await this._saveDevice(this._currentDevice);
|
|
171
|
+
this._emit("presenceChanged", {
|
|
172
|
+
deviceId: this._currentDevice.deviceId,
|
|
173
|
+
previousStatus: previous,
|
|
174
|
+
currentStatus: status,
|
|
175
|
+
timestamp: Date.now()
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Update the current device's vector clock.
|
|
180
|
+
*/
|
|
181
|
+
async updateVectorClock(clock) {
|
|
182
|
+
if (!this._currentDevice)
|
|
183
|
+
return;
|
|
184
|
+
this._currentDevice.vectorClock = clock;
|
|
185
|
+
this._currentDevice.lastSeenAt = Date.now();
|
|
186
|
+
await this._saveDevice(this._currentDevice);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Register or update a peer device.
|
|
190
|
+
*/
|
|
191
|
+
async registerPeer(peerInfo) {
|
|
192
|
+
const existing = this._peers.get(peerInfo.deviceId);
|
|
193
|
+
if (existing) {
|
|
194
|
+
const changes = {};
|
|
195
|
+
if (peerInfo.lastSeenAt > existing.lastSeenAt) {
|
|
196
|
+
Object.assign(existing, peerInfo);
|
|
197
|
+
this._emit("deviceUpdated", { device: existing, changes: peerInfo });
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
this._peers.set(peerInfo.deviceId, peerInfo);
|
|
201
|
+
await this._saveDevice(peerInfo);
|
|
202
|
+
this._emit("peerDiscovered", peerInfo);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Remove a device from the registry.
|
|
207
|
+
*/
|
|
208
|
+
async removeDevice(deviceId, reason = "removed") {
|
|
209
|
+
if (deviceId === this._currentDevice?.deviceId) {
|
|
210
|
+
throw new Error("Cannot remove current device");
|
|
211
|
+
}
|
|
212
|
+
const device = this._peers.get(deviceId);
|
|
213
|
+
if (!device)
|
|
214
|
+
return;
|
|
215
|
+
this._peers.delete(deviceId);
|
|
216
|
+
await this._adapter.run("DELETE FROM _sync_devices WHERE device_id = ?", [deviceId]);
|
|
217
|
+
this._emit("deviceRemoved", { deviceId, reason });
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check for stale peers and update their presence.
|
|
221
|
+
*/
|
|
222
|
+
async checkStaleDevices() {
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
const timeout = this._options.presenceTimeout;
|
|
225
|
+
for (const peer of this._peers.values()) {
|
|
226
|
+
if (peer.presence !== "offline" && now - peer.lastSeenAt > timeout) {
|
|
227
|
+
const previousStatus = peer.presence;
|
|
228
|
+
peer.presence = "offline";
|
|
229
|
+
this._emit("presenceChanged", {
|
|
230
|
+
deviceId: peer.deviceId,
|
|
231
|
+
previousStatus,
|
|
232
|
+
currentStatus: "offline",
|
|
233
|
+
timestamp: now
|
|
234
|
+
});
|
|
235
|
+
this._emit("peerLost", {
|
|
236
|
+
deviceId: peer.deviceId,
|
|
237
|
+
lastSeenAt: peer.lastSeenAt
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Listen for device manager events.
|
|
244
|
+
*/
|
|
245
|
+
on(event, listener) {
|
|
246
|
+
if (!this._listeners.has(event)) {
|
|
247
|
+
this._listeners.set(event, /* @__PURE__ */ new Set());
|
|
248
|
+
}
|
|
249
|
+
const listeners = this._listeners.get(event);
|
|
250
|
+
listeners.add(listener);
|
|
251
|
+
return () => {
|
|
252
|
+
listeners.delete(listener);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Dispose of the device manager.
|
|
257
|
+
*/
|
|
258
|
+
async dispose() {
|
|
259
|
+
if (this._heartbeatTimer) {
|
|
260
|
+
clearInterval(this._heartbeatTimer);
|
|
261
|
+
this._heartbeatTimer = null;
|
|
262
|
+
}
|
|
263
|
+
if (this._currentDevice) {
|
|
264
|
+
await this.updatePresence("offline");
|
|
265
|
+
}
|
|
266
|
+
this._listeners.clear();
|
|
267
|
+
this._peers.clear();
|
|
268
|
+
this._currentDevice = null;
|
|
269
|
+
this._initialized = false;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Emit an event to listeners.
|
|
273
|
+
*/
|
|
274
|
+
_emit(event, data) {
|
|
275
|
+
const listeners = this._listeners.get(event);
|
|
276
|
+
if (!listeners)
|
|
277
|
+
return;
|
|
278
|
+
for (const listener of listeners) {
|
|
279
|
+
try {
|
|
280
|
+
listener(data);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error(`[DeviceManager] Error in ${event} listener:`, error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Ensure device tables exist.
|
|
288
|
+
*/
|
|
289
|
+
async _ensureTables() {
|
|
290
|
+
await this._adapter.exec(`
|
|
291
|
+
CREATE TABLE IF NOT EXISTS _sync_devices (
|
|
292
|
+
device_id TEXT PRIMARY KEY,
|
|
293
|
+
device_type TEXT NOT NULL,
|
|
294
|
+
device_name TEXT NOT NULL,
|
|
295
|
+
vector_clock TEXT NOT NULL,
|
|
296
|
+
capabilities TEXT NOT NULL,
|
|
297
|
+
metadata TEXT,
|
|
298
|
+
presence TEXT DEFAULT 'offline',
|
|
299
|
+
created_at INTEGER NOT NULL,
|
|
300
|
+
last_seen_at INTEGER NOT NULL
|
|
301
|
+
)
|
|
302
|
+
`);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Initialize the current device.
|
|
306
|
+
*/
|
|
307
|
+
async _initializeCurrentDevice() {
|
|
308
|
+
const storedId = await this._loadStoredDeviceId();
|
|
309
|
+
if (storedId) {
|
|
310
|
+
const device2 = await this._loadDevice(storedId);
|
|
311
|
+
if (device2) {
|
|
312
|
+
device2.presence = "online";
|
|
313
|
+
device2.lastSeenAt = Date.now();
|
|
314
|
+
await this._saveDevice(device2);
|
|
315
|
+
this._currentDevice = device2;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const deviceType = this._options.autoDetect ? detectDeviceType() : "unknown";
|
|
320
|
+
const deviceName = this._options.deviceName || generateDeviceName(deviceType);
|
|
321
|
+
const deviceId = (0, import_vectorClock.generateDeviceId)();
|
|
322
|
+
const device = {
|
|
323
|
+
deviceId,
|
|
324
|
+
deviceType,
|
|
325
|
+
deviceName,
|
|
326
|
+
vectorClock: (0, import_vectorClock.createVectorClock)(deviceId),
|
|
327
|
+
createdAt: Date.now(),
|
|
328
|
+
lastSeenAt: Date.now(),
|
|
329
|
+
presence: "online",
|
|
330
|
+
capabilities: getDefaultCapabilities(deviceType)
|
|
331
|
+
};
|
|
332
|
+
await this._saveDevice(device);
|
|
333
|
+
await this._storeDeviceId(deviceId);
|
|
334
|
+
this._currentDevice = device;
|
|
335
|
+
this._emit("deviceRegistered", device);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Load stored device ID.
|
|
339
|
+
*/
|
|
340
|
+
async _loadStoredDeviceId() {
|
|
341
|
+
try {
|
|
342
|
+
if (typeof localStorage !== "undefined") {
|
|
343
|
+
return localStorage.getItem("_sync_device_id");
|
|
344
|
+
}
|
|
345
|
+
const result = await this._adapter.get("SELECT value FROM _sync_settings WHERE key = ?", ["device_id"]);
|
|
346
|
+
return result?.value ?? null;
|
|
347
|
+
} catch {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Store device ID for persistence.
|
|
353
|
+
*/
|
|
354
|
+
async _storeDeviceId(deviceId) {
|
|
355
|
+
try {
|
|
356
|
+
if (typeof localStorage !== "undefined") {
|
|
357
|
+
localStorage.setItem("_sync_device_id", deviceId);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
await this._adapter.exec(`
|
|
361
|
+
CREATE TABLE IF NOT EXISTS _sync_settings (
|
|
362
|
+
key TEXT PRIMARY KEY,
|
|
363
|
+
value TEXT NOT NULL
|
|
364
|
+
)
|
|
365
|
+
`);
|
|
366
|
+
await this._adapter.run("INSERT OR REPLACE INTO _sync_settings (key, value) VALUES (?, ?)", ["device_id", deviceId]);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.warn("[DeviceManager] Failed to persist device ID:", error);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Load a device from the database.
|
|
373
|
+
*/
|
|
374
|
+
async _loadDevice(deviceId) {
|
|
375
|
+
const row = await this._adapter.get("SELECT * FROM _sync_devices WHERE device_id = ?", [deviceId]);
|
|
376
|
+
if (!row)
|
|
377
|
+
return null;
|
|
378
|
+
return {
|
|
379
|
+
deviceId: row.device_id,
|
|
380
|
+
deviceType: row.device_type,
|
|
381
|
+
deviceName: row.device_name,
|
|
382
|
+
vectorClock: JSON.parse(row.vector_clock),
|
|
383
|
+
capabilities: JSON.parse(row.capabilities),
|
|
384
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
385
|
+
presence: row.presence,
|
|
386
|
+
createdAt: row.created_at,
|
|
387
|
+
lastSeenAt: row.last_seen_at
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Save a device to the database.
|
|
392
|
+
*/
|
|
393
|
+
async _saveDevice(device) {
|
|
394
|
+
await this._adapter.run(`INSERT OR REPLACE INTO _sync_devices
|
|
395
|
+
(device_id, device_type, device_name, vector_clock, capabilities, metadata, presence, created_at, last_seen_at)
|
|
396
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
397
|
+
device.deviceId,
|
|
398
|
+
device.deviceType,
|
|
399
|
+
device.deviceName,
|
|
400
|
+
JSON.stringify(device.vectorClock),
|
|
401
|
+
JSON.stringify(device.capabilities),
|
|
402
|
+
device.metadata ? JSON.stringify(device.metadata) : null,
|
|
403
|
+
device.presence,
|
|
404
|
+
device.createdAt,
|
|
405
|
+
device.lastSeenAt
|
|
406
|
+
]);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Load all peer devices.
|
|
410
|
+
*/
|
|
411
|
+
async _loadPeers() {
|
|
412
|
+
const rows = await this._adapter.all("SELECT * FROM _sync_devices WHERE device_id != ?", [this._currentDevice?.deviceId ?? ""]);
|
|
413
|
+
for (const row of rows) {
|
|
414
|
+
const device = {
|
|
415
|
+
deviceId: row.device_id,
|
|
416
|
+
deviceType: row.device_type,
|
|
417
|
+
deviceName: row.device_name,
|
|
418
|
+
vectorClock: JSON.parse(row.vector_clock),
|
|
419
|
+
capabilities: JSON.parse(row.capabilities),
|
|
420
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
421
|
+
presence: row.presence,
|
|
422
|
+
createdAt: row.created_at,
|
|
423
|
+
lastSeenAt: row.last_seen_at
|
|
424
|
+
};
|
|
425
|
+
this._peers.set(device.deviceId, device);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Start heartbeat timer.
|
|
430
|
+
*/
|
|
431
|
+
_startHeartbeat() {
|
|
432
|
+
if (this._heartbeatTimer)
|
|
433
|
+
return;
|
|
434
|
+
this._heartbeatTimer = setInterval(async () => {
|
|
435
|
+
if (this._currentDevice) {
|
|
436
|
+
this._currentDevice.lastSeenAt = Date.now();
|
|
437
|
+
await this._saveDevice(this._currentDevice);
|
|
438
|
+
}
|
|
439
|
+
await this.checkStaleDevices();
|
|
440
|
+
}, this._options.heartbeatInterval);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const createDeviceManager = (options) => {
|
|
444
|
+
return new DeviceManager(options);
|
|
445
|
+
};
|
|
446
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
447
|
+
0 && (module.exports = {
|
|
448
|
+
DeviceManager,
|
|
449
|
+
createDeviceManager
|
|
450
|
+
});
|