@affectively/dash 5.3.1 → 5.4.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 (77) hide show
  1. package/README.md +193 -0
  2. package/dist/src/api/firebase/auth/index.d.ts +137 -0
  3. package/dist/src/api/firebase/auth/index.js +352 -0
  4. package/dist/src/api/firebase/auth/providers.d.ts +254 -0
  5. package/dist/src/api/firebase/auth/providers.js +518 -0
  6. package/dist/src/api/firebase/database/index.d.ts +108 -0
  7. package/dist/src/api/firebase/database/index.js +368 -0
  8. package/dist/src/api/firebase/errors.d.ts +15 -0
  9. package/dist/src/api/firebase/errors.js +215 -0
  10. package/dist/src/api/firebase/firestore/data-types.d.ts +116 -0
  11. package/dist/src/api/firebase/firestore/data-types.js +280 -0
  12. package/dist/src/api/firebase/firestore/index.d.ts +7 -0
  13. package/dist/src/api/firebase/firestore/index.js +13 -0
  14. package/dist/src/api/firebase/firestore/listeners.d.ts +20 -0
  15. package/dist/src/api/firebase/firestore/listeners.js +50 -0
  16. package/dist/src/api/firebase/firestore/operations.d.ts +123 -0
  17. package/dist/src/api/firebase/firestore/operations.js +490 -0
  18. package/dist/src/api/firebase/firestore/query.d.ts +118 -0
  19. package/dist/src/api/firebase/firestore/query.js +418 -0
  20. package/dist/src/api/firebase/index.d.ts +11 -0
  21. package/dist/src/api/firebase/index.js +17 -0
  22. package/dist/src/api/firebase/storage/index.d.ts +100 -0
  23. package/dist/src/api/firebase/storage/index.js +286 -0
  24. package/dist/src/api/firebase/types.d.ts +341 -0
  25. package/dist/src/api/firebase/types.js +4 -0
  26. package/dist/src/auth/manager.d.ts +182 -0
  27. package/dist/src/auth/manager.js +598 -0
  28. package/dist/src/engine/ai.d.ts +10 -0
  29. package/dist/src/engine/ai.js +76 -0
  30. package/dist/src/engine/sqlite.d.ts +353 -0
  31. package/dist/src/engine/sqlite.js +1328 -0
  32. package/dist/src/engine/vec_extension.d.ts +5 -0
  33. package/dist/src/engine/vec_extension.js +10 -0
  34. package/dist/src/index.d.ts +21 -0
  35. package/dist/src/index.js +26 -0
  36. package/dist/src/mcp/server.d.ts +8 -0
  37. package/dist/src/mcp/server.js +87 -0
  38. package/dist/src/reactivity/signal.d.ts +3 -0
  39. package/dist/src/reactivity/signal.js +31 -0
  40. package/dist/src/schema/lens.d.ts +29 -0
  41. package/dist/src/schema/lens.js +122 -0
  42. package/dist/src/sync/AeonDurableSync.d.ts +27 -0
  43. package/dist/src/sync/AeonDurableSync.js +133 -0
  44. package/dist/src/sync/AutomergeProvider.d.ts +45 -0
  45. package/dist/src/sync/AutomergeProvider.js +153 -0
  46. package/dist/src/sync/aeon/config.d.ts +21 -0
  47. package/dist/src/sync/aeon/config.js +14 -0
  48. package/dist/src/sync/aeon/delta-adapter.d.ts +62 -0
  49. package/dist/src/sync/aeon/delta-adapter.js +98 -0
  50. package/dist/src/sync/aeon/index.d.ts +18 -0
  51. package/dist/src/sync/aeon/index.js +19 -0
  52. package/dist/src/sync/aeon/offline-adapter.d.ts +110 -0
  53. package/dist/src/sync/aeon/offline-adapter.js +227 -0
  54. package/dist/src/sync/aeon/presence-adapter.d.ts +114 -0
  55. package/dist/src/sync/aeon/presence-adapter.js +157 -0
  56. package/dist/src/sync/aeon/schema-adapter.d.ts +95 -0
  57. package/dist/src/sync/aeon/schema-adapter.js +163 -0
  58. package/dist/src/sync/backup.d.ts +12 -0
  59. package/dist/src/sync/backup.js +44 -0
  60. package/dist/src/sync/connection.d.ts +20 -0
  61. package/dist/src/sync/connection.js +50 -0
  62. package/dist/src/sync/d1-provider.d.ts +103 -0
  63. package/dist/src/sync/d1-provider.js +418 -0
  64. package/dist/src/sync/hybrid-provider.d.ts +307 -0
  65. package/dist/src/sync/hybrid-provider.js +1353 -0
  66. package/dist/src/sync/provider.d.ts +11 -0
  67. package/dist/src/sync/provider.js +67 -0
  68. package/dist/src/sync/types.d.ts +32 -0
  69. package/dist/src/sync/types.js +4 -0
  70. package/dist/src/sync/verify.d.ts +1 -0
  71. package/dist/src/sync/verify.js +23 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -0
  73. package/package.json +77 -43
  74. package/dist/index.d.ts +0 -62
  75. package/dist/index.js +0 -31
  76. package/dist/sync/index.d.ts +0 -6
  77. package/dist/sync/index.js +0 -4
@@ -0,0 +1,153 @@
1
+ /**
2
+ * AutomergeProvider - Automerge sync over WebSocket relay
3
+ *
4
+ * Mirrors HybridProvider's transport selection and discovery logic
5
+ * but uses Automerge's built-in sync protocol instead of Yjs.
6
+ * The relay server is CRDT-agnostic — it relays opaque binary frames.
7
+ *
8
+ * @automerge/automerge is dynamically imported so users who only use
9
+ * Yjs never pay the bundle cost.
10
+ */
11
+ export class AutomergeProvider {
12
+ config;
13
+ _doc;
14
+ _onChange;
15
+ _ws = null;
16
+ _syncState = null;
17
+ _automerge = null;
18
+ _ready;
19
+ _destroyed = false;
20
+ _reconnectTimer = null;
21
+ _reconnectDelayMs = 1000;
22
+ _maxReconnectDelayMs = 30_000;
23
+ constructor(doc, config, options) {
24
+ this._doc = doc;
25
+ this.config = config;
26
+ this._onChange = options?.onChange;
27
+ this._ready = this.initialize();
28
+ }
29
+ async initialize() {
30
+ const am = await import('@automerge/automerge');
31
+ this._automerge = am;
32
+ this._syncState = am.initSyncState();
33
+ this.connectWebSocket();
34
+ }
35
+ getRelayUrl() {
36
+ if (this.config.websocket?.url) {
37
+ return toWsUrl(this.config.websocket.url);
38
+ }
39
+ if (this.config.webtransport?.url) {
40
+ return toWsUrl(this.config.webtransport.url);
41
+ }
42
+ return null;
43
+ }
44
+ connectWebSocket() {
45
+ if (this._destroyed)
46
+ return;
47
+ const relayUrl = this.getRelayUrl();
48
+ if (!relayUrl)
49
+ return;
50
+ const separator = relayUrl.includes('?') ? '&' : '?';
51
+ const params = new URLSearchParams({ room: this.config.roomName });
52
+ if (this.config.apiKey) {
53
+ params.set('apiKey', this.config.apiKey);
54
+ }
55
+ const url = `${relayUrl}${separator}${params.toString()}`;
56
+ try {
57
+ this._ws = new WebSocket(url);
58
+ this._ws.binaryType = 'arraybuffer';
59
+ }
60
+ catch {
61
+ this.scheduleReconnect();
62
+ return;
63
+ }
64
+ this._ws.addEventListener('open', () => {
65
+ this._reconnectDelayMs = 1000;
66
+ this.sendSyncMessage();
67
+ });
68
+ this._ws.addEventListener('message', (event) => {
69
+ this.handleMessage(event.data);
70
+ });
71
+ this._ws.addEventListener('close', () => {
72
+ this._ws = null;
73
+ this.scheduleReconnect();
74
+ });
75
+ this._ws.addEventListener('error', () => {
76
+ // close event will follow and trigger reconnect
77
+ });
78
+ }
79
+ scheduleReconnect() {
80
+ if (this._destroyed || this._reconnectTimer)
81
+ return;
82
+ this._reconnectTimer = setTimeout(() => {
83
+ this._reconnectTimer = null;
84
+ this.connectWebSocket();
85
+ }, this._reconnectDelayMs);
86
+ this._reconnectDelayMs = Math.min(this._reconnectDelayMs * 2, this._maxReconnectDelayMs);
87
+ }
88
+ sendSyncMessage() {
89
+ const am = this._automerge;
90
+ if (!am || !this._ws || this._ws.readyState !== WebSocket.OPEN)
91
+ return;
92
+ const [nextSyncState, message] = am.generateSyncMessage(this._doc, this._syncState);
93
+ this._syncState = nextSyncState;
94
+ if (message) {
95
+ this._ws.send(message);
96
+ }
97
+ }
98
+ handleMessage(data) {
99
+ const am = this._automerge;
100
+ if (!am)
101
+ return;
102
+ const message = new Uint8Array(data);
103
+ const [nextDoc, nextSyncState] = am.receiveSyncMessage(this._doc, this._syncState, message);
104
+ this._doc = nextDoc;
105
+ this._syncState = nextSyncState;
106
+ this._onChange?.(this._doc);
107
+ // After receiving, we may need to send our own changes
108
+ this.sendSyncMessage();
109
+ }
110
+ /**
111
+ * Update the local document. Required because Automerge documents are immutable —
112
+ * after calling Automerge.change(), pass the new doc here to sync it.
113
+ */
114
+ updateDoc(newDoc) {
115
+ this._doc = newDoc;
116
+ this.sendSyncMessage();
117
+ }
118
+ get doc() {
119
+ return this._doc;
120
+ }
121
+ get ready() {
122
+ return this._ready;
123
+ }
124
+ get roomName() {
125
+ return this.config.roomName;
126
+ }
127
+ get connected() {
128
+ return this._ws !== null && this._ws.readyState === WebSocket.OPEN;
129
+ }
130
+ disconnect() {
131
+ if (this._reconnectTimer) {
132
+ clearTimeout(this._reconnectTimer);
133
+ this._reconnectTimer = null;
134
+ }
135
+ if (this._ws) {
136
+ this._ws.close();
137
+ this._ws = null;
138
+ }
139
+ }
140
+ async destroy() {
141
+ this._destroyed = true;
142
+ this.disconnect();
143
+ }
144
+ }
145
+ function toWsUrl(url) {
146
+ if (url.startsWith('wss://') || url.startsWith('ws://'))
147
+ return url;
148
+ if (url.startsWith('https://'))
149
+ return `wss://${url.slice('https://'.length)}`;
150
+ if (url.startsWith('http://'))
151
+ return `ws://${url.slice('http://'.length)}`;
152
+ return url;
153
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Aeon Integration Configuration
3
+ *
4
+ * Configuration for Aeon sync features in Dash.
5
+ * All features enabled by default.
6
+ */
7
+ export interface AeonConfig {
8
+ /** Enable delta sync optimization (70-80% bandwidth reduction) */
9
+ enableDeltaSync: boolean;
10
+ /** Enable rich presence tracking (cursors, sections, activity) */
11
+ enableRichPresence: boolean;
12
+ /** Enable offline operation queuing */
13
+ enableOfflineQueue: boolean;
14
+ /** Bytes threshold before falling back to full sync instead of delta */
15
+ deltaThreshold: number;
16
+ /** Maximum operations to hold in offline queue */
17
+ maxOfflineQueueSize: number;
18
+ /** Maximum retries for failed offline operations */
19
+ maxOfflineRetries: number;
20
+ }
21
+ export declare const defaultAeonConfig: AeonConfig;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Aeon Integration Configuration
3
+ *
4
+ * Configuration for Aeon sync features in Dash.
5
+ * All features enabled by default.
6
+ */
7
+ export const defaultAeonConfig = {
8
+ enableDeltaSync: true,
9
+ enableRichPresence: true,
10
+ enableOfflineQueue: true,
11
+ deltaThreshold: 1000,
12
+ maxOfflineQueueSize: 1000,
13
+ maxOfflineRetries: 3,
14
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Delta Adapter for Dash
3
+ *
4
+ * Wraps Aeon's DeltaSyncOptimizer to provide delta compression
5
+ * for Yjs document updates. Achieves 70-80% bandwidth reduction.
6
+ */
7
+ import { type DeltaOperation, type DeltaStats } from '@affectively/aeon';
8
+ /**
9
+ * Adapter that wraps Yjs updates with delta compression
10
+ */
11
+ export declare class DashDeltaAdapter {
12
+ private optimizer;
13
+ private roomId;
14
+ private updateCounter;
15
+ constructor(roomId: string, threshold?: number);
16
+ /**
17
+ * Wrap a Yjs update with delta compression
18
+ * Returns a delta payload ready for transmission
19
+ */
20
+ wrapUpdate(update: Uint8Array, origin?: string): DeltaPayload;
21
+ /**
22
+ * Unwrap a delta payload back to Yjs update
23
+ */
24
+ unwrapDelta(payload: DeltaPayload): Uint8Array;
25
+ /**
26
+ * Encode delta payload for wire transmission
27
+ */
28
+ encodePayload(payload: DeltaPayload): Uint8Array;
29
+ /**
30
+ * Decode delta payload from wire
31
+ */
32
+ decodePayload(data: Uint8Array): DeltaPayload;
33
+ /**
34
+ * Get compression statistics
35
+ */
36
+ getStats(): DeltaStats;
37
+ /**
38
+ * Reset statistics
39
+ */
40
+ resetStats(): void;
41
+ /**
42
+ * Get memory footprint estimate
43
+ */
44
+ getMemoryEstimate(): number;
45
+ /**
46
+ * Clear operation history
47
+ */
48
+ clearHistory(): void;
49
+ }
50
+ /**
51
+ * Delta payload structure for wire transmission
52
+ */
53
+ export interface DeltaPayload {
54
+ type: 'delta';
55
+ delta: DeltaOperation;
56
+ originalSize: number;
57
+ deltaSize: number;
58
+ }
59
+ /**
60
+ * Check if a payload is a delta payload
61
+ */
62
+ export declare function isDeltaPayload(data: unknown): data is DeltaPayload;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Delta Adapter for Dash
3
+ *
4
+ * Wraps Aeon's DeltaSyncOptimizer to provide delta compression
5
+ * for Yjs document updates. Achieves 70-80% bandwidth reduction.
6
+ */
7
+ import { DeltaSyncOptimizer, } from '@affectively/aeon';
8
+ /**
9
+ * Adapter that wraps Yjs updates with delta compression
10
+ */
11
+ export class DashDeltaAdapter {
12
+ optimizer;
13
+ roomId;
14
+ updateCounter = 0;
15
+ constructor(roomId, threshold = 1000) {
16
+ this.roomId = roomId;
17
+ this.optimizer = new DeltaSyncOptimizer(threshold);
18
+ }
19
+ /**
20
+ * Wrap a Yjs update with delta compression
21
+ * Returns a delta payload ready for transmission
22
+ */
23
+ wrapUpdate(update, origin) {
24
+ const operationId = `${this.roomId}-${this.updateCounter++}`;
25
+ // Create an Aeon Operation from the Yjs update
26
+ const operation = {
27
+ id: operationId,
28
+ type: 'update',
29
+ sessionId: this.roomId,
30
+ data: {
31
+ update: Array.from(update),
32
+ origin: origin ?? 'local',
33
+ },
34
+ status: 'pending',
35
+ createdAt: Date.now(),
36
+ };
37
+ const delta = this.optimizer.computeDelta(operation);
38
+ return {
39
+ type: 'delta',
40
+ delta,
41
+ originalSize: update.byteLength,
42
+ deltaSize: new TextEncoder().encode(JSON.stringify(delta)).byteLength,
43
+ };
44
+ }
45
+ /**
46
+ * Unwrap a delta payload back to Yjs update
47
+ */
48
+ unwrapDelta(payload) {
49
+ const operation = this.optimizer.decompressDelta(payload.delta);
50
+ return new Uint8Array(operation.data.update);
51
+ }
52
+ /**
53
+ * Encode delta payload for wire transmission
54
+ */
55
+ encodePayload(payload) {
56
+ return new TextEncoder().encode(JSON.stringify(payload));
57
+ }
58
+ /**
59
+ * Decode delta payload from wire
60
+ */
61
+ decodePayload(data) {
62
+ return JSON.parse(new TextDecoder().decode(data));
63
+ }
64
+ /**
65
+ * Get compression statistics
66
+ */
67
+ getStats() {
68
+ return this.optimizer.getStats();
69
+ }
70
+ /**
71
+ * Reset statistics
72
+ */
73
+ resetStats() {
74
+ this.optimizer.resetStats();
75
+ }
76
+ /**
77
+ * Get memory footprint estimate
78
+ */
79
+ getMemoryEstimate() {
80
+ return this.optimizer.getMemoryEstimate();
81
+ }
82
+ /**
83
+ * Clear operation history
84
+ */
85
+ clearHistory() {
86
+ this.optimizer.resetStats();
87
+ }
88
+ }
89
+ /**
90
+ * Check if a payload is a delta payload
91
+ */
92
+ export function isDeltaPayload(data) {
93
+ return (typeof data === 'object' &&
94
+ data !== null &&
95
+ 'type' in data &&
96
+ data.type === 'delta' &&
97
+ 'delta' in data);
98
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Aeon Integration for Dash
3
+ *
4
+ * Provides enhanced sync capabilities through Aeon:
5
+ * - Delta compression (70-80% bandwidth reduction)
6
+ * - Rich presence tracking (cursors, sections, activity)
7
+ * - Offline operation queuing with persistence
8
+ * - Schema versioning and migrations
9
+ */
10
+ export { defaultAeonConfig, type AeonConfig } from './config.js';
11
+ export { DashDeltaAdapter, isDeltaPayload, type DeltaPayload, } from './delta-adapter.js';
12
+ export { DashPresenceAdapter, type PresenceEvents, } from './presence-adapter.js';
13
+ export { DashOfflineAdapter, type ProcessQueueResult, type OfflineQueueEvents, } from './offline-adapter.js';
14
+ export { DashSchemaAdapter, createSchemaAdapter, } from './schema-adapter.js';
15
+ export type { DeltaOperation, DeltaStats, DeltaBatch, } from '@affectively/aeon';
16
+ export type { AgentPresence, } from '@affectively/aeon';
17
+ export type { OfflineOperation, OfflineQueueStats, OperationPriority, } from '@affectively/aeon';
18
+ export type { SchemaVersion, Migration, MigrationResult, MigrationRecord, } from '@affectively/aeon';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Aeon Integration for Dash
3
+ *
4
+ * Provides enhanced sync capabilities through Aeon:
5
+ * - Delta compression (70-80% bandwidth reduction)
6
+ * - Rich presence tracking (cursors, sections, activity)
7
+ * - Offline operation queuing with persistence
8
+ * - Schema versioning and migrations
9
+ */
10
+ // Configuration
11
+ export { defaultAeonConfig } from './config.js';
12
+ // Delta compression adapter
13
+ export { DashDeltaAdapter, isDeltaPayload, } from './delta-adapter.js';
14
+ // Presence adapter
15
+ export { DashPresenceAdapter, } from './presence-adapter.js';
16
+ // Offline queue adapter
17
+ export { DashOfflineAdapter, } from './offline-adapter.js';
18
+ // Schema versioning adapter
19
+ export { DashSchemaAdapter, createSchemaAdapter, } from './schema-adapter.js';
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Offline Adapter for Dash
3
+ *
4
+ * Wraps Aeon's OfflineOperationQueue to provide offline-first
5
+ * operation queuing for Yjs documents.
6
+ */
7
+ import { type OfflineOperation, type OfflineQueueStats, type OperationPriority } from '@affectively/aeon';
8
+ import * as Y from 'yjs';
9
+ /**
10
+ * Adapter that queues Yjs updates when offline
11
+ */
12
+ export declare class DashOfflineAdapter {
13
+ private queue;
14
+ private doc;
15
+ private roomId;
16
+ private storageKey;
17
+ private isOnline;
18
+ private processingQueue;
19
+ constructor(doc: Y.Doc, roomId: string, maxQueueSize?: number, maxRetries?: number);
20
+ /**
21
+ * Setup network status listeners
22
+ */
23
+ private setupNetworkListeners;
24
+ /**
25
+ * Setup queue event handlers
26
+ */
27
+ private setupQueueEvents;
28
+ /**
29
+ * Queue a Yjs update for later sync
30
+ */
31
+ queueUpdate(update: Uint8Array, priority?: OperationPriority): OfflineOperation;
32
+ /**
33
+ * Check if we should queue (offline) or send directly
34
+ */
35
+ shouldQueue(): boolean;
36
+ /**
37
+ * Process queued operations
38
+ * @param sendFn Function to send updates (typically HybridProvider.send)
39
+ */
40
+ processQueue(sendFn?: (update: Uint8Array) => Promise<void>): Promise<ProcessQueueResult>;
41
+ /**
42
+ * Get queue statistics
43
+ */
44
+ getStats(): OfflineQueueStats;
45
+ /**
46
+ * Get pending operation count
47
+ */
48
+ getPendingCount(): number;
49
+ /**
50
+ * Check if online
51
+ */
52
+ getOnlineStatus(): boolean;
53
+ /**
54
+ * Retry failed operations
55
+ */
56
+ retryFailed(): void;
57
+ /**
58
+ * Clear failed operations
59
+ */
60
+ clearFailed(): void;
61
+ /**
62
+ * Clear all queued operations
63
+ */
64
+ clear(): void;
65
+ /**
66
+ * Subscribe to queue events
67
+ */
68
+ on<E extends keyof OfflineQueueEvents>(event: E, handler: OfflineQueueEvents[E]): void;
69
+ /**
70
+ * Unsubscribe from queue events
71
+ */
72
+ off<E extends keyof OfflineQueueEvents>(event: E, handler: OfflineQueueEvents[E]): void;
73
+ /**
74
+ * Persist queue to localStorage
75
+ */
76
+ private persistQueue;
77
+ /**
78
+ * Load queue from localStorage
79
+ */
80
+ private loadPersistedQueue;
81
+ /**
82
+ * Clear persisted queue
83
+ */
84
+ private clearPersistedQueue;
85
+ /**
86
+ * Cleanup
87
+ */
88
+ destroy(): void;
89
+ }
90
+ /**
91
+ * Result of processing the queue
92
+ */
93
+ export interface ProcessQueueResult {
94
+ synced: number;
95
+ failed: number;
96
+ }
97
+ /**
98
+ * Offline queue event types
99
+ */
100
+ export interface OfflineQueueEvents {
101
+ 'operation-added': (operation: OfflineOperation) => void;
102
+ 'operation-synced': (operation: OfflineOperation) => void;
103
+ 'operation-failed': (operation: OfflineOperation, error: Error) => void;
104
+ 'queue-empty': () => void;
105
+ 'sync-started': () => void;
106
+ 'sync-completed': (stats: {
107
+ synced: number;
108
+ failed: number;
109
+ }) => void;
110
+ }