@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.
- package/README.md +193 -0
- package/dist/src/api/firebase/auth/index.d.ts +137 -0
- package/dist/src/api/firebase/auth/index.js +352 -0
- package/dist/src/api/firebase/auth/providers.d.ts +254 -0
- package/dist/src/api/firebase/auth/providers.js +518 -0
- package/dist/src/api/firebase/database/index.d.ts +108 -0
- package/dist/src/api/firebase/database/index.js +368 -0
- package/dist/src/api/firebase/errors.d.ts +15 -0
- package/dist/src/api/firebase/errors.js +215 -0
- package/dist/src/api/firebase/firestore/data-types.d.ts +116 -0
- package/dist/src/api/firebase/firestore/data-types.js +280 -0
- package/dist/src/api/firebase/firestore/index.d.ts +7 -0
- package/dist/src/api/firebase/firestore/index.js +13 -0
- package/dist/src/api/firebase/firestore/listeners.d.ts +20 -0
- package/dist/src/api/firebase/firestore/listeners.js +50 -0
- package/dist/src/api/firebase/firestore/operations.d.ts +123 -0
- package/dist/src/api/firebase/firestore/operations.js +490 -0
- package/dist/src/api/firebase/firestore/query.d.ts +118 -0
- package/dist/src/api/firebase/firestore/query.js +418 -0
- package/dist/src/api/firebase/index.d.ts +11 -0
- package/dist/src/api/firebase/index.js +17 -0
- package/dist/src/api/firebase/storage/index.d.ts +100 -0
- package/dist/src/api/firebase/storage/index.js +286 -0
- package/dist/src/api/firebase/types.d.ts +341 -0
- package/dist/src/api/firebase/types.js +4 -0
- package/dist/src/auth/manager.d.ts +182 -0
- package/dist/src/auth/manager.js +598 -0
- package/dist/src/engine/ai.d.ts +10 -0
- package/dist/src/engine/ai.js +76 -0
- package/dist/src/engine/sqlite.d.ts +353 -0
- package/dist/src/engine/sqlite.js +1328 -0
- package/dist/src/engine/vec_extension.d.ts +5 -0
- package/dist/src/engine/vec_extension.js +10 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.js +26 -0
- package/dist/src/mcp/server.d.ts +8 -0
- package/dist/src/mcp/server.js +87 -0
- package/dist/src/reactivity/signal.d.ts +3 -0
- package/dist/src/reactivity/signal.js +31 -0
- package/dist/src/schema/lens.d.ts +29 -0
- package/dist/src/schema/lens.js +122 -0
- package/dist/src/sync/AeonDurableSync.d.ts +27 -0
- package/dist/src/sync/AeonDurableSync.js +133 -0
- package/dist/src/sync/AutomergeProvider.d.ts +45 -0
- package/dist/src/sync/AutomergeProvider.js +153 -0
- package/dist/src/sync/aeon/config.d.ts +21 -0
- package/dist/src/sync/aeon/config.js +14 -0
- package/dist/src/sync/aeon/delta-adapter.d.ts +62 -0
- package/dist/src/sync/aeon/delta-adapter.js +98 -0
- package/dist/src/sync/aeon/index.d.ts +18 -0
- package/dist/src/sync/aeon/index.js +19 -0
- package/dist/src/sync/aeon/offline-adapter.d.ts +110 -0
- package/dist/src/sync/aeon/offline-adapter.js +227 -0
- package/dist/src/sync/aeon/presence-adapter.d.ts +114 -0
- package/dist/src/sync/aeon/presence-adapter.js +157 -0
- package/dist/src/sync/aeon/schema-adapter.d.ts +95 -0
- package/dist/src/sync/aeon/schema-adapter.js +163 -0
- package/dist/src/sync/backup.d.ts +12 -0
- package/dist/src/sync/backup.js +44 -0
- package/dist/src/sync/connection.d.ts +20 -0
- package/dist/src/sync/connection.js +50 -0
- package/dist/src/sync/d1-provider.d.ts +103 -0
- package/dist/src/sync/d1-provider.js +418 -0
- package/dist/src/sync/hybrid-provider.d.ts +307 -0
- package/dist/src/sync/hybrid-provider.js +1353 -0
- package/dist/src/sync/provider.d.ts +11 -0
- package/dist/src/sync/provider.js +67 -0
- package/dist/src/sync/types.d.ts +32 -0
- package/dist/src/sync/types.js +4 -0
- package/dist/src/sync/verify.d.ts +1 -0
- package/dist/src/sync/verify.js +23 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +77 -43
- package/dist/index.d.ts +0 -62
- package/dist/index.js +0 -31
- package/dist/sync/index.d.ts +0 -6
- 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
|
+
}
|