@affectively/dash 5.2.1 → 5.3.1

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 (71) hide show
  1. package/dist/index.d.ts +62 -0
  2. package/dist/index.js +31 -0
  3. package/dist/sync/index.d.ts +6 -0
  4. package/dist/sync/index.js +4 -0
  5. package/package.json +45 -66
  6. package/README.md +0 -193
  7. package/dist/src/api/firebase/auth/index.d.ts +0 -137
  8. package/dist/src/api/firebase/auth/index.js +0 -352
  9. package/dist/src/api/firebase/auth/providers.d.ts +0 -254
  10. package/dist/src/api/firebase/auth/providers.js +0 -518
  11. package/dist/src/api/firebase/database/index.d.ts +0 -108
  12. package/dist/src/api/firebase/database/index.js +0 -368
  13. package/dist/src/api/firebase/errors.d.ts +0 -15
  14. package/dist/src/api/firebase/errors.js +0 -215
  15. package/dist/src/api/firebase/firestore/data-types.d.ts +0 -116
  16. package/dist/src/api/firebase/firestore/data-types.js +0 -280
  17. package/dist/src/api/firebase/firestore/index.d.ts +0 -7
  18. package/dist/src/api/firebase/firestore/index.js +0 -13
  19. package/dist/src/api/firebase/firestore/listeners.d.ts +0 -20
  20. package/dist/src/api/firebase/firestore/listeners.js +0 -50
  21. package/dist/src/api/firebase/firestore/operations.d.ts +0 -123
  22. package/dist/src/api/firebase/firestore/operations.js +0 -490
  23. package/dist/src/api/firebase/firestore/query.d.ts +0 -118
  24. package/dist/src/api/firebase/firestore/query.js +0 -418
  25. package/dist/src/api/firebase/index.d.ts +0 -11
  26. package/dist/src/api/firebase/index.js +0 -17
  27. package/dist/src/api/firebase/storage/index.d.ts +0 -100
  28. package/dist/src/api/firebase/storage/index.js +0 -286
  29. package/dist/src/api/firebase/types.d.ts +0 -341
  30. package/dist/src/api/firebase/types.js +0 -4
  31. package/dist/src/auth/manager.d.ts +0 -182
  32. package/dist/src/auth/manager.js +0 -598
  33. package/dist/src/engine/ai.d.ts +0 -10
  34. package/dist/src/engine/ai.js +0 -76
  35. package/dist/src/engine/sqlite.d.ts +0 -298
  36. package/dist/src/engine/sqlite.js +0 -1088
  37. package/dist/src/engine/vec_extension.d.ts +0 -5
  38. package/dist/src/engine/vec_extension.js +0 -10
  39. package/dist/src/index.d.ts +0 -15
  40. package/dist/src/index.js +0 -24
  41. package/dist/src/mcp/server.d.ts +0 -8
  42. package/dist/src/mcp/server.js +0 -87
  43. package/dist/src/reactivity/signal.d.ts +0 -3
  44. package/dist/src/reactivity/signal.js +0 -31
  45. package/dist/src/schema/lens.d.ts +0 -29
  46. package/dist/src/schema/lens.js +0 -122
  47. package/dist/src/sync/aeon/config.d.ts +0 -21
  48. package/dist/src/sync/aeon/config.js +0 -14
  49. package/dist/src/sync/aeon/delta-adapter.d.ts +0 -62
  50. package/dist/src/sync/aeon/delta-adapter.js +0 -98
  51. package/dist/src/sync/aeon/index.d.ts +0 -18
  52. package/dist/src/sync/aeon/index.js +0 -19
  53. package/dist/src/sync/aeon/offline-adapter.d.ts +0 -110
  54. package/dist/src/sync/aeon/offline-adapter.js +0 -227
  55. package/dist/src/sync/aeon/presence-adapter.d.ts +0 -114
  56. package/dist/src/sync/aeon/presence-adapter.js +0 -157
  57. package/dist/src/sync/aeon/schema-adapter.d.ts +0 -95
  58. package/dist/src/sync/aeon/schema-adapter.js +0 -163
  59. package/dist/src/sync/backup.d.ts +0 -12
  60. package/dist/src/sync/backup.js +0 -44
  61. package/dist/src/sync/connection.d.ts +0 -20
  62. package/dist/src/sync/connection.js +0 -50
  63. package/dist/src/sync/d1-provider.d.ts +0 -97
  64. package/dist/src/sync/d1-provider.js +0 -345
  65. package/dist/src/sync/hybrid-provider.d.ts +0 -172
  66. package/dist/src/sync/hybrid-provider.js +0 -477
  67. package/dist/src/sync/provider.d.ts +0 -11
  68. package/dist/src/sync/provider.js +0 -67
  69. package/dist/src/sync/verify.d.ts +0 -1
  70. package/dist/src/sync/verify.js +0 -23
  71. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,345 +0,0 @@
1
- /**
2
- * D1 Sync Provider
3
- *
4
- * Provides HTTP-based sync between local Dash SQLite (WASM) and Cloudflare D1.
5
- *
6
- * Unlike the HybridProvider which syncs Yjs CRDT documents over WebSocket,
7
- * this provider syncs regular SQL tables via HTTP REST API.
8
- *
9
- * Architecture:
10
- * - Local changes are tracked in a sync_queue table
11
- * - Sync runs periodically or on-demand
12
- * - Conflicts are resolved using last-write-wins with timestamps
13
- */
14
- import { dash } from '../engine/sqlite.js';
15
- export class D1SyncProvider {
16
- config;
17
- syncTimer = null;
18
- isSyncing = false;
19
- lastSyncTime = 0;
20
- initialized = false;
21
- constructor(config) {
22
- this.config = {
23
- syncInterval: 30000,
24
- ...config,
25
- };
26
- }
27
- /**
28
- * Initialize the sync provider - must be called before syncing
29
- */
30
- async initialize() {
31
- if (this.initialized)
32
- return;
33
- await dash.ready();
34
- // Create sync metadata and queue tables
35
- dash.execute(`
36
- CREATE TABLE IF NOT EXISTS dash_sync_meta (
37
- key TEXT PRIMARY KEY,
38
- value TEXT
39
- )
40
- `);
41
- dash.execute(`
42
- CREATE TABLE IF NOT EXISTS dash_sync_queue (
43
- id INTEGER PRIMARY KEY AUTOINCREMENT,
44
- table_name TEXT NOT NULL,
45
- row_id TEXT NOT NULL,
46
- operation TEXT NOT NULL CHECK(operation IN ('create', 'update', 'delete')),
47
- data TEXT,
48
- created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
49
- synced INTEGER DEFAULT 0
50
- )
51
- `);
52
- // Create index for efficient querying
53
- dash.execute(`
54
- CREATE INDEX IF NOT EXISTS idx_sync_queue_synced
55
- ON dash_sync_queue(synced, created_at)
56
- `);
57
- // Load last sync time
58
- const meta = dash.execute("SELECT value FROM dash_sync_meta WHERE key = 'lastSyncTime'");
59
- if (meta.length > 0) {
60
- this.lastSyncTime = parseInt(meta[0].value, 10) || 0;
61
- }
62
- this.initialized = true;
63
- // Set up triggers for each table
64
- for (const table of this.config.tables) {
65
- this.setupTableTriggers(table);
66
- }
67
- // Start auto-sync if configured
68
- if (this.config.syncInterval && this.config.syncInterval > 0) {
69
- this.startAutoSync();
70
- }
71
- console.log('[D1SyncProvider] Initialized for tables:', this.config.tables);
72
- }
73
- /**
74
- * Set up SQLite triggers to track changes for a table
75
- */
76
- setupTableTriggers(tableName) {
77
- // Clean up existing triggers first
78
- try {
79
- dash.execute(`DROP TRIGGER IF EXISTS dash_sync_${tableName}_insert`);
80
- dash.execute(`DROP TRIGGER IF EXISTS dash_sync_${tableName}_update`);
81
- dash.execute(`DROP TRIGGER IF EXISTS dash_sync_${tableName}_delete`);
82
- }
83
- catch {
84
- // Ignore errors from dropping non-existent triggers
85
- }
86
- // Create INSERT trigger
87
- dash.execute(`
88
- CREATE TRIGGER IF NOT EXISTS dash_sync_${tableName}_insert
89
- AFTER INSERT ON ${tableName}
90
- BEGIN
91
- INSERT INTO dash_sync_queue (table_name, row_id, operation, data)
92
- VALUES ('${tableName}', NEW.id, 'create', json(NEW));
93
- END
94
- `);
95
- // Create UPDATE trigger
96
- dash.execute(`
97
- CREATE TRIGGER IF NOT EXISTS dash_sync_${tableName}_update
98
- AFTER UPDATE ON ${tableName}
99
- BEGIN
100
- INSERT INTO dash_sync_queue (table_name, row_id, operation, data)
101
- VALUES ('${tableName}', NEW.id, 'update', json(NEW));
102
- END
103
- `);
104
- // Create DELETE trigger
105
- dash.execute(`
106
- CREATE TRIGGER IF NOT EXISTS dash_sync_${tableName}_delete
107
- AFTER DELETE ON ${tableName}
108
- BEGIN
109
- INSERT INTO dash_sync_queue (table_name, row_id, operation, data)
110
- VALUES ('${tableName}', OLD.id, 'delete', NULL);
111
- END
112
- `);
113
- }
114
- /**
115
- * Start automatic sync at the configured interval
116
- */
117
- startAutoSync() {
118
- if (this.syncTimer)
119
- return;
120
- this.syncTimer = setInterval(() => {
121
- this.sync().catch((err) => {
122
- console.error('[D1SyncProvider] Auto-sync failed:', err);
123
- this.config.onSyncError?.(err);
124
- });
125
- }, this.config.syncInterval);
126
- console.log('[D1SyncProvider] Auto-sync started, interval:', this.config.syncInterval, 'ms');
127
- }
128
- /**
129
- * Stop automatic sync
130
- */
131
- stopAutoSync() {
132
- if (this.syncTimer) {
133
- clearInterval(this.syncTimer);
134
- this.syncTimer = null;
135
- console.log('[D1SyncProvider] Auto-sync stopped');
136
- }
137
- }
138
- /**
139
- * Perform a sync operation
140
- */
141
- async sync() {
142
- if (!this.initialized) {
143
- await this.initialize();
144
- }
145
- if (this.isSyncing) {
146
- console.log('[D1SyncProvider] Sync already in progress, skipping');
147
- return { pushed: 0, pulled: 0, errors: ['Sync already in progress'], timestamp: Date.now() };
148
- }
149
- this.isSyncing = true;
150
- const result = { pushed: 0, pulled: 0, errors: [], timestamp: Date.now() };
151
- try {
152
- const token = await this.config.getAuthToken();
153
- if (!token) {
154
- throw new Error('No auth token available');
155
- }
156
- // Get pending changes from queue
157
- const pending = dash.execute(`
158
- SELECT * FROM dash_sync_queue
159
- WHERE synced = 0
160
- ORDER BY created_at ASC
161
- LIMIT 100
162
- `);
163
- // Group changes by table
164
- const changes = {};
165
- for (const entry of pending) {
166
- if (!changes[entry.table_name]) {
167
- changes[entry.table_name] = { creates: [], updates: [], deletes: [] };
168
- }
169
- const tableChanges = changes[entry.table_name];
170
- const data = entry.data ? JSON.parse(entry.data) : null;
171
- switch (entry.operation) {
172
- case 'create':
173
- if (data)
174
- tableChanges.creates.push(data);
175
- break;
176
- case 'update':
177
- if (data)
178
- tableChanges.updates.push({ id: entry.row_id, data });
179
- break;
180
- case 'delete':
181
- tableChanges.deletes.push(entry.row_id);
182
- break;
183
- }
184
- }
185
- // Sync each table
186
- for (const tableName of this.config.tables) {
187
- const tableChanges = changes[tableName] || { creates: [], updates: [], deletes: [] };
188
- try {
189
- const response = await fetch(`${this.config.baseUrl}/sync`, {
190
- method: 'POST',
191
- headers: {
192
- 'Content-Type': 'application/json',
193
- 'Authorization': `Bearer ${token}`,
194
- },
195
- body: JSON.stringify({
196
- table: tableName,
197
- creates: tableChanges.creates,
198
- updates: tableChanges.updates,
199
- deletes: tableChanges.deletes,
200
- lastSyncTime: this.lastSyncTime,
201
- }),
202
- });
203
- if (!response.ok) {
204
- const errorText = await response.text();
205
- result.errors.push(`${tableName}: ${response.status} ${errorText}`);
206
- continue;
207
- }
208
- const syncResponse = await response.json();
209
- result.pushed += tableChanges.creates.length + tableChanges.updates.length + tableChanges.deletes.length;
210
- result.pulled += syncResponse.serverChanges?.length || 0;
211
- // Apply server changes locally
212
- if (syncResponse.serverChanges && syncResponse.serverChanges.length > 0) {
213
- await this.applyServerChanges(tableName, syncResponse.serverChanges);
214
- }
215
- // Update sync time
216
- if (syncResponse.syncTime > this.lastSyncTime) {
217
- this.lastSyncTime = syncResponse.syncTime;
218
- dash.execute("INSERT OR REPLACE INTO dash_sync_meta (key, value) VALUES ('lastSyncTime', ?)", [String(this.lastSyncTime)]);
219
- }
220
- }
221
- catch (err) {
222
- result.errors.push(`${tableName}: ${err instanceof Error ? err.message : String(err)}`);
223
- }
224
- }
225
- // Mark synced entries
226
- if (pending.length > 0) {
227
- const ids = pending.map(e => e.id).join(',');
228
- dash.execute(`UPDATE dash_sync_queue SET synced = 1 WHERE id IN (${ids})`);
229
- }
230
- // Clean up old synced entries (keep last 1000)
231
- dash.execute(`
232
- DELETE FROM dash_sync_queue
233
- WHERE synced = 1
234
- AND id NOT IN (
235
- SELECT id FROM dash_sync_queue
236
- WHERE synced = 1
237
- ORDER BY id DESC
238
- LIMIT 1000
239
- )
240
- `);
241
- console.log('[D1SyncProvider] Sync complete:', result);
242
- this.config.onSyncComplete?.(result);
243
- }
244
- catch (err) {
245
- const error = err instanceof Error ? err : new Error(String(err));
246
- result.errors.push(error.message);
247
- console.error('[D1SyncProvider] Sync failed:', error);
248
- this.config.onSyncError?.(error);
249
- }
250
- finally {
251
- this.isSyncing = false;
252
- }
253
- return result;
254
- }
255
- /**
256
- * Apply changes received from the server
257
- */
258
- async applyServerChanges(tableName, changes) {
259
- for (const change of changes) {
260
- try {
261
- // Check if this is a delete (marked by a flag or null data)
262
- if (change.deleted || change._deleted) {
263
- dash.execute(`DELETE FROM ${tableName} WHERE id = ?`, [change.id]);
264
- continue;
265
- }
266
- // Get column names from the change object
267
- const columns = Object.keys(change).filter(k => !k.startsWith('_'));
268
- const placeholders = columns.map(() => '?').join(', ');
269
- const values = columns.map(k => {
270
- const v = change[k];
271
- // Serialize objects/arrays to JSON
272
- if (v !== null && typeof v === 'object') {
273
- return JSON.stringify(v);
274
- }
275
- return v;
276
- });
277
- // Use INSERT OR REPLACE to handle both new and updated records
278
- const sql = `INSERT OR REPLACE INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
279
- dash.execute(sql, values);
280
- }
281
- catch (err) {
282
- console.error(`[D1SyncProvider] Failed to apply change to ${tableName}:`, err, change);
283
- }
284
- }
285
- }
286
- /**
287
- * Queue a change manually (for cases where triggers can't be used)
288
- */
289
- queueChange(tableName, rowId, operation, data) {
290
- if (!this.initialized) {
291
- console.warn('[D1SyncProvider] Not initialized, change will not be queued');
292
- return;
293
- }
294
- dash.execute(`INSERT INTO dash_sync_queue (table_name, row_id, operation, data) VALUES (?, ?, ?, ?)`, [tableName, rowId, operation, data ? JSON.stringify(data) : null]);
295
- }
296
- /**
297
- * Get the current sync status
298
- */
299
- getStatus() {
300
- let pendingChanges = 0;
301
- if (this.initialized) {
302
- const result = dash.execute('SELECT COUNT(*) as count FROM dash_sync_queue WHERE synced = 0');
303
- pendingChanges = result[0]?.count || 0;
304
- }
305
- return {
306
- initialized: this.initialized,
307
- syncing: this.isSyncing,
308
- lastSyncTime: this.lastSyncTime,
309
- pendingChanges,
310
- };
311
- }
312
- /**
313
- * Force a full sync by resetting last sync time
314
- */
315
- async forceFullSync() {
316
- this.lastSyncTime = 0;
317
- dash.execute("INSERT OR REPLACE INTO dash_sync_meta (key, value) VALUES ('lastSyncTime', '0')");
318
- return this.sync();
319
- }
320
- /**
321
- * Destroy the provider and clean up
322
- */
323
- destroy() {
324
- this.stopAutoSync();
325
- this.initialized = false;
326
- console.log('[D1SyncProvider] Destroyed');
327
- }
328
- }
329
- // Export singleton factory for convenience
330
- let defaultProvider = null;
331
- export function getD1SyncProvider(config) {
332
- if (!defaultProvider && config) {
333
- defaultProvider = new D1SyncProvider(config);
334
- }
335
- if (!defaultProvider) {
336
- throw new Error('D1SyncProvider not configured. Call getD1SyncProvider(config) first.');
337
- }
338
- return defaultProvider;
339
- }
340
- export function resetD1SyncProvider() {
341
- if (defaultProvider) {
342
- defaultProvider.destroy();
343
- defaultProvider = null;
344
- }
345
- }
@@ -1,172 +0,0 @@
1
- import * as Y from 'yjs';
2
- import { Observable } from 'lib0/observable';
3
- import * as awarenessProtocol from 'y-protocols/awareness';
4
- import { type AeonConfig } from './aeon/config.js';
5
- import { DashDeltaAdapter } from './aeon/delta-adapter.js';
6
- import { DashPresenceAdapter } from './aeon/presence-adapter.js';
7
- import { DashOfflineAdapter } from './aeon/offline-adapter.js';
8
- export declare class HybridProvider extends Observable<string> {
9
- private doc;
10
- private ws;
11
- private wt;
12
- private connected;
13
- private url;
14
- private roomName;
15
- awareness: awarenessProtocol.Awareness;
16
- private writer;
17
- private highFrequencyMode;
18
- private aeonConfig;
19
- private deltaAdapter;
20
- private presenceAdapter;
21
- private offlineAdapter;
22
- constructor(url: string, roomName: string, doc: Y.Doc, { awareness, aeonConfig, }?: {
23
- awareness?: awarenessProtocol.Awareness;
24
- aeonConfig?: AeonConfig;
25
- });
26
- private connect;
27
- private connectWebSocket;
28
- private getAuthToken;
29
- enterHighFrequencyMode(): Promise<void>;
30
- private sendSyncStep1;
31
- private readLoop;
32
- private handleMessage;
33
- private send;
34
- private onDocUpdate;
35
- private onAwarenessUpdate;
36
- destroy(): void;
37
- /**
38
- * Get the delta adapter for compression stats
39
- */
40
- getDeltaAdapter(): DashDeltaAdapter | null;
41
- /**
42
- * Get the presence adapter for rich presence features
43
- */
44
- getPresenceAdapter(): DashPresenceAdapter | null;
45
- /**
46
- * Get the offline adapter for queue management
47
- */
48
- getOfflineAdapter(): DashOfflineAdapter | null;
49
- /**
50
- * Get Aeon configuration
51
- */
52
- getAeonConfig(): AeonConfig;
53
- /**
54
- * Process offline queue when back online
55
- */
56
- processOfflineQueue(): Promise<{
57
- synced: number;
58
- failed: number;
59
- }>;
60
- /**
61
- * Get comprehensive connection status
62
- */
63
- getConnectionStatus(): ConnectionStatus;
64
- /**
65
- * Get all awareness states from connected peers
66
- */
67
- getAwarenessStates(): AwarenessState[];
68
- /**
69
- * Get the local client ID
70
- */
71
- getLocalClientId(): number;
72
- /**
73
- * Get connected peer count (excluding local)
74
- */
75
- getPeerCount(): number;
76
- /**
77
- * Get document state information
78
- */
79
- getDocumentState(): DocumentState;
80
- /**
81
- * Get a snapshot of the Yjs document for inspection
82
- */
83
- getDocumentSnapshot(): DocumentSnapshot;
84
- /**
85
- * Set local awareness state for introspection (admin view)
86
- */
87
- setLocalAwareness(state: Record<string, unknown>): void;
88
- /**
89
- * Get full provider status for debugging
90
- */
91
- getProviderStatus(): ProviderStatus;
92
- /**
93
- * Get Aeon-specific status
94
- */
95
- getAeonStatus(): AeonStatus;
96
- }
97
- export interface ConnectionStatus {
98
- connected: boolean;
99
- roomName: string;
100
- url: string;
101
- websocket: {
102
- state: 'connecting' | 'open' | 'closing' | 'closed';
103
- connected: boolean;
104
- };
105
- webTransport: {
106
- connected: boolean;
107
- highFrequencyMode: boolean;
108
- };
109
- transport: 'WebSocket' | 'WebTransport';
110
- }
111
- export interface AwarenessState {
112
- clientId: number;
113
- state: Record<string, unknown>;
114
- isLocal: boolean;
115
- }
116
- export interface SharedTypeInfo {
117
- name: string;
118
- type: 'YMap' | 'YArray' | 'YText' | 'YXmlFragment' | 'unknown';
119
- size: number;
120
- }
121
- export interface DocumentState {
122
- clientId: number;
123
- guid: string;
124
- stateVectorSize: number;
125
- updateSize: number;
126
- sharedTypes: SharedTypeInfo[];
127
- transactionCount: number;
128
- gcEnabled: boolean;
129
- }
130
- export interface DocumentSnapshot {
131
- timestamp: string;
132
- roomName: string;
133
- content: Record<string, unknown>;
134
- }
135
- export interface ProviderStatus {
136
- connection: ConnectionStatus;
137
- awareness: {
138
- localClientId: number;
139
- peerCount: number;
140
- states: AwarenessState[];
141
- };
142
- document: DocumentState;
143
- aeon: AeonStatus;
144
- }
145
- export interface AeonStatus {
146
- enabled: {
147
- deltaSync: boolean;
148
- richPresence: boolean;
149
- offlineQueue: boolean;
150
- };
151
- delta: {
152
- totalOperations: number;
153
- totalFull: number;
154
- totalDelta: number;
155
- totalOriginalSize: number;
156
- totalDeltaSize: number;
157
- averageReductionPercent: number;
158
- } | null;
159
- offline: {
160
- pending: number;
161
- syncing: number;
162
- failed: number;
163
- synced: number;
164
- totalOperations: number;
165
- } | null;
166
- presence: {
167
- total: number;
168
- online: number;
169
- away: number;
170
- offline: number;
171
- } | null;
172
- }