@affectively/dash 5.4.1 → 5.4.5

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 (92) hide show
  1. package/README.md +8 -189
  2. package/dist/automerge_wasm_bg-4hg5vg2g.wasm +0 -0
  3. package/dist/engine/sqlite.d.ts +30 -0
  4. package/dist/engine/vec_extension.d.ts +2 -0
  5. package/dist/index.d.ts +73 -0
  6. package/dist/index.js +53895 -0
  7. package/dist/middleware/errorHandler.d.ts +60 -0
  8. package/dist/{src/sync → sync}/AeonDurableSync.d.ts +7 -9
  9. package/dist/sync/AeonDurableSync.js +1984 -0
  10. package/dist/{src/sync → sync}/AutomergeProvider.d.ts +8 -8
  11. package/dist/sync/AutomergeProvider.js +4421 -0
  12. package/dist/sync/HybridProvider.d.ts +124 -0
  13. package/dist/sync/HybridProvider.js +8328 -0
  14. package/dist/sync/connection/WebRTCConnection.d.ts +23 -0
  15. package/dist/sync/connection/WebRTCConnection.js +59 -0
  16. package/dist/sync/index.d.ts +13 -0
  17. package/dist/sync/index.js +12773 -0
  18. package/dist/sync/provider/YjsSqliteProvider.d.ts +17 -0
  19. package/dist/sync/provider/YjsSqliteProvider.js +54 -0
  20. package/dist/sync/types.d.ts +74 -0
  21. package/dist/sync/webtransport/WebTransportProvider.d.ts +16 -0
  22. package/dist/sync/webtransport/WebTransportProvider.js +55 -0
  23. package/package.json +62 -70
  24. package/dist/src/api/firebase/auth/index.d.ts +0 -137
  25. package/dist/src/api/firebase/auth/index.js +0 -352
  26. package/dist/src/api/firebase/auth/providers.d.ts +0 -254
  27. package/dist/src/api/firebase/auth/providers.js +0 -518
  28. package/dist/src/api/firebase/database/index.d.ts +0 -108
  29. package/dist/src/api/firebase/database/index.js +0 -368
  30. package/dist/src/api/firebase/errors.d.ts +0 -15
  31. package/dist/src/api/firebase/errors.js +0 -215
  32. package/dist/src/api/firebase/firestore/data-types.d.ts +0 -116
  33. package/dist/src/api/firebase/firestore/data-types.js +0 -280
  34. package/dist/src/api/firebase/firestore/index.d.ts +0 -7
  35. package/dist/src/api/firebase/firestore/index.js +0 -13
  36. package/dist/src/api/firebase/firestore/listeners.d.ts +0 -20
  37. package/dist/src/api/firebase/firestore/listeners.js +0 -50
  38. package/dist/src/api/firebase/firestore/operations.d.ts +0 -123
  39. package/dist/src/api/firebase/firestore/operations.js +0 -490
  40. package/dist/src/api/firebase/firestore/query.d.ts +0 -118
  41. package/dist/src/api/firebase/firestore/query.js +0 -418
  42. package/dist/src/api/firebase/index.d.ts +0 -11
  43. package/dist/src/api/firebase/index.js +0 -17
  44. package/dist/src/api/firebase/storage/index.d.ts +0 -100
  45. package/dist/src/api/firebase/storage/index.js +0 -286
  46. package/dist/src/api/firebase/types.d.ts +0 -341
  47. package/dist/src/api/firebase/types.js +0 -4
  48. package/dist/src/auth/manager.d.ts +0 -182
  49. package/dist/src/auth/manager.js +0 -598
  50. package/dist/src/engine/ai.js +0 -76
  51. package/dist/src/engine/sqlite.d.ts +0 -353
  52. package/dist/src/engine/sqlite.js +0 -1328
  53. package/dist/src/engine/vec_extension.d.ts +0 -5
  54. package/dist/src/engine/vec_extension.js +0 -10
  55. package/dist/src/index.d.ts +0 -21
  56. package/dist/src/index.js +0 -26
  57. package/dist/src/mcp/server.js +0 -87
  58. package/dist/src/reactivity/signal.js +0 -31
  59. package/dist/src/schema/lens.d.ts +0 -29
  60. package/dist/src/schema/lens.js +0 -122
  61. package/dist/src/sync/AeonDurableSync.js +0 -133
  62. package/dist/src/sync/AutomergeProvider.js +0 -153
  63. package/dist/src/sync/aeon/config.d.ts +0 -21
  64. package/dist/src/sync/aeon/config.js +0 -14
  65. package/dist/src/sync/aeon/delta-adapter.d.ts +0 -62
  66. package/dist/src/sync/aeon/delta-adapter.js +0 -98
  67. package/dist/src/sync/aeon/index.d.ts +0 -18
  68. package/dist/src/sync/aeon/index.js +0 -19
  69. package/dist/src/sync/aeon/offline-adapter.d.ts +0 -110
  70. package/dist/src/sync/aeon/offline-adapter.js +0 -227
  71. package/dist/src/sync/aeon/presence-adapter.d.ts +0 -114
  72. package/dist/src/sync/aeon/presence-adapter.js +0 -157
  73. package/dist/src/sync/aeon/schema-adapter.d.ts +0 -95
  74. package/dist/src/sync/aeon/schema-adapter.js +0 -163
  75. package/dist/src/sync/backup.d.ts +0 -12
  76. package/dist/src/sync/backup.js +0 -44
  77. package/dist/src/sync/connection.d.ts +0 -20
  78. package/dist/src/sync/connection.js +0 -50
  79. package/dist/src/sync/d1-provider.d.ts +0 -103
  80. package/dist/src/sync/d1-provider.js +0 -418
  81. package/dist/src/sync/hybrid-provider.d.ts +0 -307
  82. package/dist/src/sync/hybrid-provider.js +0 -1353
  83. package/dist/src/sync/provider.d.ts +0 -11
  84. package/dist/src/sync/provider.js +0 -67
  85. package/dist/src/sync/types.d.ts +0 -32
  86. package/dist/src/sync/types.js +0 -4
  87. package/dist/src/sync/verify.d.ts +0 -1
  88. package/dist/src/sync/verify.js +0 -23
  89. package/dist/tsconfig.tsbuildinfo +0 -1
  90. /package/dist/{src/engine → engine}/ai.d.ts +0 -0
  91. /package/dist/{src/mcp → mcp}/server.d.ts +0 -0
  92. /package/dist/{src/reactivity → reactivity}/signal.d.ts +0 -0
@@ -1,5 +0,0 @@
1
- import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
2
- type Sqlite3Module = Awaited<ReturnType<typeof sqlite3InitModule>>;
3
- type Database = Sqlite3Module['oo1']['OpfsDb'] | Sqlite3Module['oo1']['DB'];
4
- export declare function loadVectorExtension(db: Database): Promise<void>;
5
- export {};
@@ -1,10 +0,0 @@
1
- export async function loadVectorExtension(db) {
2
- // Hypothetical API for loading WASM extension if sqlite-wasm supports it.
3
- // Currently sqlite-wasm static builds don't easily support dynamic extension loading
4
- // without a custom build.
5
- // For this 'cutting edge' roadmap implementation, we will simulate the
6
- // extension's presence or use a polyfill pattern if the raw WASM isn't present.
7
- console.log("Loading sqlite-vec extension...");
8
- // register native functions if possible
9
- // db.createFunction(...)
10
- }
@@ -1,21 +0,0 @@
1
- export { dash } from './engine/sqlite.js';
2
- export type { CloudConfig, CloudSyncResult } from './engine/sqlite.js';
3
- export { liveQuery, signal, effect, computed } from './reactivity/signal.js';
4
- export { mcpServer } from './mcp/server.js';
5
- export { YjsSqliteProvider } from './sync/provider.js';
6
- export { backup, restore, generateKey, exportKey, importKey } from './sync/backup.js';
7
- export type { CloudStorageAdapter } from './sync/backup.js';
8
- export { HybridProvider } from './sync/hybrid-provider.js';
9
- export type { RelayDiscoveryConfig, RelayPerformanceConfig, RelayPrivacyConfig, ConnectionStatus, ProviderStatus, } from './sync/hybrid-provider.js';
10
- export { AutomergeProvider } from './sync/AutomergeProvider.js';
11
- export type { AutomergeProviderOptions } from './sync/AutomergeProvider.js';
12
- export type { AutomergeProviderConfig } from './sync/types.js';
13
- export { AeonDurableSyncRuntime } from './sync/AeonDurableSync.js';
14
- export type { AeonDurableSyncConfig } from './sync/AeonDurableSync.js';
15
- export { D1SyncProvider, getD1SyncProvider, resetD1SyncProvider } from './sync/d1-provider.js';
16
- export type { D1SyncConfig, SyncResult, SyncQueueEntry } from './sync/d1-provider.js';
17
- export * as firebase from './api/firebase/index.js';
18
- export { collection, doc, query, where, orderBy, limit, limitToLast, startAt, startAfter, endAt, endBefore, offset, Timestamp, GeoPoint, serverTimestamp, deleteField, arrayUnion, arrayRemove, increment, getDoc, getDocs, setDoc, updateDoc, deleteDoc, writeBatch, runTransaction, onSnapshot, } from './api/firebase/index.js';
19
- export { ref, child, set, update, remove, push, get, onDisconnect, onValue, off, } from './api/firebase/index.js';
20
- export { createUserWithEmailAndPassword, signInWithEmailAndPassword, signInAnonymously, signOut, getAuth, onAuthStateChanged, updateUserProfile, updateUserEmail, updateUserPassword, deleteUser, sendPasswordResetEmail, sendEmailVerification, GoogleAuthProvider, GithubAuthProvider, FacebookAuthProvider, TwitterAuthProvider, DiscordAuthProvider, TotpMultiFactorGenerator, PhoneMultiFactorGenerator, setPersistence, browserLocalPersistence, browserSessionPersistence, inMemoryPersistence, } from './api/firebase/index.js';
21
- export { ref as storageRef, refFromURL, child as storageChild, uploadBytes, uploadBytesResumable, uploadString, getBytes, getDownloadURL, getMetadata, updateMetadata, deleteObject, list as listFiles, listAll, } from './api/firebase/storage/index.js';
package/dist/src/index.js DELETED
@@ -1,26 +0,0 @@
1
- export { dash } from './engine/sqlite.js';
2
- export { liveQuery, signal, effect, computed } from './reactivity/signal.js';
3
- export { mcpServer } from './mcp/server.js';
4
- // Sync exports
5
- export { YjsSqliteProvider } from './sync/provider.js';
6
- export { backup, restore, generateKey, exportKey, importKey } from './sync/backup.js';
7
- export { HybridProvider } from './sync/hybrid-provider.js';
8
- export { AutomergeProvider } from './sync/AutomergeProvider.js';
9
- export { AeonDurableSyncRuntime } from './sync/AeonDurableSync.js';
10
- // D1 HTTP Sync (legacy - prefer dash.enableCloudSync())
11
- export { D1SyncProvider, getD1SyncProvider, resetD1SyncProvider } from './sync/d1-provider.js';
12
- // Firebase Compatibility API exports
13
- export * as firebase from './api/firebase/index.js';
14
- // Firestore
15
- export { collection, doc, query, where, orderBy, limit, limitToLast, startAt, startAfter, endAt, endBefore, offset, serverTimestamp, deleteField, arrayUnion, arrayRemove, increment, getDoc, getDocs, setDoc, updateDoc, deleteDoc, writeBatch, runTransaction, onSnapshot, } from './api/firebase/index.js';
16
- // Realtime Database
17
- export { ref, child, set, update, remove, push, get, onDisconnect, onValue,
18
- // onChildAdded,
19
- // onChildChanged,
20
- // onChildRemoved,
21
- // onChildMoved,
22
- off, } from './api/firebase/index.js';
23
- // Authentication
24
- export { createUserWithEmailAndPassword, signInWithEmailAndPassword, signInAnonymously, signOut, getAuth, onAuthStateChanged, updateUserProfile, updateUserEmail, updateUserPassword, deleteUser, sendPasswordResetEmail, sendEmailVerification, GoogleAuthProvider, GithubAuthProvider, FacebookAuthProvider, TwitterAuthProvider, DiscordAuthProvider, TotpMultiFactorGenerator, PhoneMultiFactorGenerator, setPersistence, browserLocalPersistence, browserSessionPersistence, inMemoryPersistence, } from './api/firebase/index.js';
25
- // Storage
26
- export { ref as storageRef, refFromURL, child as storageChild, uploadBytes, uploadBytesResumable, uploadString, getBytes, getDownloadURL, getMetadata, updateMetadata, deleteObject, list as listFiles, listAll, } from './api/firebase/storage/index.js';
@@ -1,87 +0,0 @@
1
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
- import { dash } from '../engine/sqlite.js';
4
- export class DashMCPServer {
5
- server;
6
- constructor() {
7
- this.server = new Server({
8
- name: 'dash-local-db',
9
- version: '2.0.0',
10
- }, {
11
- capabilities: {
12
- resources: {},
13
- tools: {},
14
- },
15
- });
16
- this.setupHandlers();
17
- }
18
- setupHandlers() {
19
- // List Resources: Expose all tables as resources
20
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
21
- await dash.ready();
22
- // Get all table names
23
- const tables = dash.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
24
- return {
25
- resources: tables.map((t) => ({
26
- uri: `mcp://local/dash/${t.name}`,
27
- name: t.name,
28
- mimeType: 'application/json',
29
- description: `Table: ${t.name}`,
30
- })),
31
- };
32
- });
33
- // Read Resource: Return table content
34
- this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
35
- const uri = request.params.uri;
36
- const tableName = uri.split('/').pop();
37
- if (!tableName)
38
- throw new Error('Invalid URI');
39
- const rows = dash.execute(`SELECT * FROM ${tableName} LIMIT 100`);
40
- return {
41
- contents: [{
42
- uri: uri,
43
- mimeType: 'application/json',
44
- text: JSON.stringify(rows, null, 2),
45
- }],
46
- };
47
- });
48
- // List Tools: Expose Semantic Search
49
- this.server.setRequestHandler(ListToolsRequestSchema, async () => {
50
- return {
51
- tools: [{
52
- name: 'semantic_search',
53
- description: 'Search the Dash database using vector similarity (semantic search).',
54
- inputSchema: {
55
- type: 'object',
56
- properties: {
57
- query: { type: 'string', description: 'The search query string.' },
58
- },
59
- required: ['query'],
60
- },
61
- }],
62
- };
63
- });
64
- // Call Tool: Execute Semantic Search
65
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
66
- if (request.params.name === 'semantic_search') {
67
- const query = request.params.arguments?.query;
68
- if (!query)
69
- throw new Error('Missing query argument');
70
- const results = await dash.search(query);
71
- return {
72
- content: [{
73
- type: 'text',
74
- text: JSON.stringify(results, null, 2),
75
- }],
76
- };
77
- }
78
- throw new Error('Tool not found');
79
- });
80
- }
81
- // Method to connect to a transport (e.g., SSE for web)
82
- async connect(transport) {
83
- await this.server.connect(transport);
84
- console.log('Dash MCP Server Connected');
85
- }
86
- }
87
- export const mcpServer = new DashMCPServer();
@@ -1,31 +0,0 @@
1
- import { signal } from '@preact/signals-core';
2
- import { dash } from '../engine/sqlite.js';
3
- export function liveQuery(sql, bind) {
4
- const data = signal([]);
5
- const run = async () => {
6
- await dash.ready();
7
- try {
8
- const result = dash.execute(sql, bind);
9
- data.value = result;
10
- }
11
- catch (e) {
12
- console.error('Dash LiveQuery Error:', e);
13
- }
14
- };
15
- // Run immediately
16
- run();
17
- // Subscribe to database changes
18
- // Parse table name naively
19
- const match = sql.match(/FROM\s+([a-zA-Z0-9_]+)/i);
20
- const table = match ? match[1] : null;
21
- if (table) {
22
- dash.subscribe(table, () => {
23
- run();
24
- });
25
- }
26
- else {
27
- console.warn('Dash: Could not identify table for liveQuery', sql);
28
- }
29
- return data;
30
- }
31
- export { signal, effect, computed } from '@preact/signals-core';
@@ -1,29 +0,0 @@
1
- export type LensOp = {
2
- kind: 'rename';
3
- source: string;
4
- target: string;
5
- } | {
6
- kind: 'hoist';
7
- host: string;
8
- name: string;
9
- } | {
10
- kind: 'wrap';
11
- name: string;
12
- };
13
- export interface SchemaLens {
14
- from: number;
15
- to: number;
16
- ops: LensOp[];
17
- }
18
- export declare class LensEngine {
19
- private lenses;
20
- private currentVersion;
21
- constructor(currentVersion: number);
22
- registerLens(from: number, to: number, ops: LensOp[]): void;
23
- private findPath;
24
- migrate(data: any, from: number, to?: number): any;
25
- private normalizeOps;
26
- private invertOp;
27
- private applyOp;
28
- }
29
- export declare const schema: LensEngine;
@@ -1,122 +0,0 @@
1
- // Native Bidirectional Lens Implementation
2
- // Inspired by Project Cambria
3
- export class LensEngine {
4
- lenses = [];
5
- currentVersion;
6
- constructor(currentVersion) {
7
- this.currentVersion = currentVersion;
8
- }
9
- registerLens(from, to, ops) {
10
- this.lenses.push({ from, to, ops });
11
- }
12
- // Find path from V_start to V_end
13
- findPath(from, to) {
14
- // BFS to find shortest path
15
- const queue = [{ version: from, path: [] }];
16
- const visited = new Set([from]);
17
- while (queue.length > 0) {
18
- const { version, path } = queue.shift();
19
- if (version === to)
20
- return path;
21
- // Find neighbors
22
- // Forward lenses
23
- const forward = this.lenses.filter(l => l.from === version);
24
- for (const lens of forward) {
25
- if (!visited.has(lens.to)) {
26
- visited.add(lens.to);
27
- queue.push({ version: lens.to, path: [...path, lens] });
28
- }
29
- }
30
- // Reverse lenses (Implicit)
31
- const backward = this.lenses.filter(l => l.to === version);
32
- for (const lens of backward) {
33
- if (!visited.has(lens.from)) {
34
- visited.add(lens.from);
35
- // Create synthetic reverse lens
36
- const reverseLens = {
37
- from: lens.to,
38
- to: lens.from,
39
- ops: [...lens.ops].reverse() // Ops must be reversed!
40
- };
41
- queue.push({ version: lens.from, path: [...path, reverseLens] });
42
- }
43
- }
44
- }
45
- throw new Error(`No translation path found from v${from} to v${to}`);
46
- }
47
- migrate(data, from, to = this.currentVersion) {
48
- if (from === to)
49
- return data;
50
- const path = this.findPath(from, to);
51
- let doc = JSON.parse(JSON.stringify(data)); // Deep clone to avoid mutation
52
- for (const step of path) {
53
- const isForward = step.from < step.to; // Heuristic: Assuming localized version jumps
54
- // Actually we should just apply the ops in the step direction.
55
- // But we need to know if the step is a "Forward Lens" or a "Reverse Lens"
56
- // because ops like 'rename' need to swap source/target if reversed.
57
- // The path finding creates "synthetic" lenses for reverse steps where from > to.
58
- // So we just need to apply 'step.ops' carefully.
59
- // But wait! in findPath, I just reversed the ARRAY of ops.
60
- // I ALSO need to invert the OPERATORS themselves.
61
- const opsToApply = this.normalizeOps(step);
62
- for (const op of opsToApply) {
63
- doc = this.applyOp(doc, op);
64
- }
65
- }
66
- return doc;
67
- }
68
- normalizeOps(step) {
69
- // If this lens was registered as from=1, to=2
70
- // And we are using it as from=1, to=2. No change.
71
- // If we are traversing 2->1.
72
- // The findPath constructed a lens { from: 2, to: 1, ops: ops.reverse() }
73
- // But the ops inside are still { kind: 'rename', source: 'old', target: 'new' }
74
- // We need to invert them to { kind: 'rename', source: 'new', target: 'old' }
75
- // We need to know the *registred* direction to know if inversion is needed?
76
- // Actually findPath constructs a fresh object. We can check if it exists in this.lenses.
77
- const registered = this.lenses.find(l => l.from === step.from && l.to === step.to);
78
- if (registered) {
79
- return step.ops; // Direct match, use as is
80
- }
81
- // Must be a reverse step constructed by findPath
82
- return step.ops.map(op => this.invertOp(op));
83
- }
84
- invertOp(op) {
85
- switch (op.kind) {
86
- case 'rename': return { kind: 'rename', source: op.target, target: op.source };
87
- // Invert Hoist: Wrap?
88
- // Hoist: host='meta', name='key' (moves doc.meta.key -> doc.key)
89
- // Inverse: Wrap (moves doc.key -> doc.meta.key)
90
- // Use 'wrap' as simplified inverse of simple hoist
91
- case 'hoist': return { kind: 'wrap', name: op.name }; // Simplified
92
- case 'wrap': return { kind: 'hoist', host: 'container', name: op.name }; // Naive inverse
93
- default: return op;
94
- }
95
- }
96
- applyOp(doc, op) {
97
- switch (op.kind) {
98
- case 'rename': {
99
- if (doc[op.source] !== undefined) {
100
- doc[op.target] = doc[op.source];
101
- delete doc[op.source];
102
- }
103
- return doc;
104
- }
105
- case 'hoist': {
106
- // Extract property from scalar host? Or object?
107
- // Assume simple object flattening: { data: { val: 1 } } -> hoist 'data', 'val' -> { val: 1 }
108
- // doc[op.host][op.name] -> doc[op.name]
109
- if (doc[op.host] && doc[op.host][op.name] !== undefined) {
110
- doc[op.name] = doc[op.host][op.name];
111
- delete doc[op.host][op.name];
112
- if (Object.keys(doc[op.host]).length === 0)
113
- delete doc[op.host];
114
- }
115
- return doc;
116
- }
117
- default: return doc;
118
- }
119
- }
120
- }
121
- export const schema = new LensEngine(1);
122
- // Default to v1 until configured
@@ -1,133 +0,0 @@
1
- /**
2
- * AeonDurableSync - Durable telemetry runtime for sync operations
3
- *
4
- * Records transport selection, discovery events, and sync protocol messages
5
- * using Aeon's OfflineOperationQueue, SyncProtocol, and ReplicationManager
6
- * with real persistence through DashStorageAdapter.
7
- */
8
- import { OfflineOperationQueue, SyncProtocol, ReplicationManager, DashStorageAdapter, InMemoryStorageAdapter, } from '@affectively/aeon';
9
- function createBrowserStorageBackend() {
10
- return {
11
- get(key) {
12
- if (typeof localStorage === 'undefined') {
13
- return null;
14
- }
15
- try {
16
- return localStorage.getItem(key);
17
- }
18
- catch {
19
- return null;
20
- }
21
- },
22
- set(key, value) {
23
- if (typeof localStorage === 'undefined') {
24
- return;
25
- }
26
- try {
27
- localStorage.setItem(key, value);
28
- }
29
- catch {
30
- // best-effort persistence in constrained browser contexts
31
- }
32
- },
33
- delete(key) {
34
- if (typeof localStorage === 'undefined') {
35
- return;
36
- }
37
- try {
38
- localStorage.removeItem(key);
39
- }
40
- catch {
41
- // best-effort persistence in constrained browser contexts
42
- }
43
- },
44
- };
45
- }
46
- export class AeonDurableSyncRuntime {
47
- queue;
48
- protocol;
49
- replication;
50
- constructor(config) {
51
- const prefix = config.persistenceKeyPrefix ?? `dash:aeon:${config.roomName}`;
52
- const adapter = config.enabled === false
53
- ? new InMemoryStorageAdapter()
54
- : new DashStorageAdapter(config.storageBackend ?? createBrowserStorageBackend(), config.storageOptions ?? {});
55
- this.queue = new OfflineOperationQueue({
56
- persistence: {
57
- adapter,
58
- key: `${prefix}:offline`,
59
- autoPersist: true,
60
- autoLoad: true,
61
- },
62
- });
63
- this.protocol = new SyncProtocol({
64
- persistence: {
65
- adapter,
66
- key: `${prefix}:protocol`,
67
- autoPersist: true,
68
- autoLoad: true,
69
- },
70
- });
71
- this.replication = new ReplicationManager({
72
- persistence: {
73
- adapter,
74
- key: `${prefix}:replication`,
75
- autoPersist: true,
76
- autoLoad: true,
77
- },
78
- });
79
- }
80
- async initialize() {
81
- try {
82
- await Promise.all([
83
- this.queue.loadFromPersistence(),
84
- this.protocol.loadFromPersistence(),
85
- this.replication.loadFromPersistence(),
86
- ]);
87
- }
88
- catch {
89
- // Persistence hydration is optional; runtime should stay operational.
90
- }
91
- }
92
- recordTransportSelection(roomName, transport, relay) {
93
- if (!transport) {
94
- return;
95
- }
96
- try {
97
- this.queue.enqueue('update', {
98
- roomName,
99
- event: 'transport-selected',
100
- transport,
101
- relay,
102
- ts: Date.now(),
103
- }, roomName, 'normal');
104
- this.protocol.createSyncRequestMessage(roomName, relay ?? 'unknown-relay', `transport-${Date.now()}`, '0.0.0', '1.1.0', {
105
- transport,
106
- relay,
107
- });
108
- }
109
- catch {
110
- // Telemetry durability must never impact foreground sync operations.
111
- }
112
- }
113
- recordDiscovery(roomName, relays) {
114
- const sanitizedRelays = relays
115
- .filter((relay) => typeof relay === 'string' && relay.length > 0)
116
- .slice(0, 32);
117
- try {
118
- this.queue.enqueue('sync', {
119
- roomName,
120
- event: 'relay-discovery',
121
- count: sanitizedRelays.length,
122
- relays: sanitizedRelays,
123
- ts: Date.now(),
124
- }, roomName, 'low');
125
- }
126
- catch {
127
- // Telemetry durability must never impact foreground sync operations.
128
- }
129
- }
130
- getPendingOperations() {
131
- return this.queue.getPendingCount();
132
- }
133
- }
@@ -1,153 +0,0 @@
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
- }
@@ -1,21 +0,0 @@
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;
@@ -1,14 +0,0 @@
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
- };