@affectively/dash 5.0.0 → 5.1.0

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.
@@ -30,5 +30,140 @@ export declare class DashEngine {
30
30
  maxZ: number;
31
31
  }): Promise<any[]>;
32
32
  close(): void;
33
+ /**
34
+ * Get information about all tables in the database
35
+ */
36
+ getAllTables(): TableInfo[];
37
+ /**
38
+ * Get detailed schema information for a specific table
39
+ */
40
+ getTableSchema(tableName: string): TableSchema | null;
41
+ /**
42
+ * Get the row count for a specific table
43
+ */
44
+ getTableRowCount(tableName: string): number;
45
+ /**
46
+ * Get statistics about the vector index
47
+ */
48
+ getVectorIndexStats(): VectorIndexStats;
49
+ /**
50
+ * Get all embeddings from the vector index
51
+ */
52
+ getAllEmbeddings(limit?: number, offset?: number): EmbeddingEntry[];
53
+ /**
54
+ * Search for similar vectors given a raw vector
55
+ */
56
+ searchSimilarByVector(vector: number[], k?: number): SimilarityResult[];
57
+ /**
58
+ * Get statistics about the spatial R-Tree index
59
+ */
60
+ getSpatialIndexStats(): SpatialIndexStats;
61
+ /**
62
+ * Get all spatial bounds from the R-Tree index
63
+ */
64
+ getAllSpatialBounds(limit?: number, offset?: number): SpatialBound[];
65
+ /**
66
+ * Get information about active table subscriptions
67
+ */
68
+ getActiveSubscriptions(): SubscriptionInfo[];
69
+ /**
70
+ * Get table listener counts as a map
71
+ */
72
+ getTableListenerCounts(): Record<string, number>;
73
+ /**
74
+ * Check if the database is ready
75
+ */
76
+ isReady(): boolean;
77
+ /**
78
+ * Get database metadata
79
+ */
80
+ getDatabaseInfo(): DatabaseInfo;
81
+ }
82
+ export interface TableInfo {
83
+ name: string;
84
+ type: 'table' | 'virtual';
85
+ rowCount: number;
86
+ isVirtual: boolean;
87
+ }
88
+ export interface TableSchema {
89
+ name: string;
90
+ columns: ColumnInfo[];
91
+ indexes: IndexInfo[];
92
+ foreignKeys: ForeignKeyInfo[];
93
+ rowCount: number;
94
+ }
95
+ export interface ColumnInfo {
96
+ cid: number;
97
+ name: string;
98
+ type: string;
99
+ notNull: boolean;
100
+ defaultValue: string | null;
101
+ isPrimaryKey: boolean;
102
+ }
103
+ export interface IndexInfo {
104
+ name: string;
105
+ unique: boolean;
106
+ origin: string;
107
+ }
108
+ export interface ForeignKeyInfo {
109
+ id: number;
110
+ table: string;
111
+ from: string;
112
+ to: string;
113
+ }
114
+ export interface VectorIndexStats {
115
+ totalEmbeddings: number;
116
+ dimensions: number;
117
+ indexExists: boolean;
118
+ tableName?: string;
119
+ error?: string;
120
+ }
121
+ export interface EmbeddingEntry {
122
+ id: string;
123
+ content: string | null;
124
+ embedding: number[] | null;
125
+ }
126
+ export interface SimilarityResult {
127
+ id: string;
128
+ content: string | null;
129
+ distance: number;
130
+ score: number;
131
+ }
132
+ export interface SpatialIndexStats {
133
+ totalEntries: number;
134
+ indexExists: boolean;
135
+ tableName?: string;
136
+ globalBounds?: {
137
+ minX: number;
138
+ maxX: number;
139
+ minY: number;
140
+ maxY: number;
141
+ minZ: number;
142
+ maxZ: number;
143
+ };
144
+ error?: string;
145
+ }
146
+ export interface SpatialBound {
147
+ id: string;
148
+ rowid: number;
149
+ bounds: {
150
+ minX: number;
151
+ maxX: number;
152
+ minY: number;
153
+ maxY: number;
154
+ minZ: number;
155
+ maxZ: number;
156
+ };
157
+ content: string | null;
158
+ }
159
+ export interface SubscriptionInfo {
160
+ table: string;
161
+ listenerCount: number;
162
+ }
163
+ export interface DatabaseInfo {
164
+ ready: boolean;
165
+ schemaVersion: number;
166
+ tableCount: number;
167
+ tables?: string[];
33
168
  }
34
169
  export declare const dash: DashEngine;
@@ -252,5 +252,303 @@ export class DashEngine {
252
252
  close() {
253
253
  this.db?.close();
254
254
  }
255
+ // ============================================
256
+ // INTROSPECTION METHODS FOR DASH-STUDIO
257
+ // ============================================
258
+ /**
259
+ * Get information about all tables in the database
260
+ */
261
+ getAllTables() {
262
+ if (!this.db)
263
+ return [];
264
+ const tables = this.execute(`
265
+ SELECT name, type
266
+ FROM sqlite_master
267
+ WHERE type IN ('table', 'virtual table')
268
+ AND name NOT LIKE 'sqlite_%'
269
+ ORDER BY name
270
+ `);
271
+ return tables.map((t) => {
272
+ const rowCount = this.getTableRowCount(t.name);
273
+ return {
274
+ name: t.name,
275
+ type: t.type === 'table' ? 'table' : 'virtual',
276
+ rowCount,
277
+ isVirtual: t.type !== 'table'
278
+ };
279
+ });
280
+ }
281
+ /**
282
+ * Get detailed schema information for a specific table
283
+ */
284
+ getTableSchema(tableName) {
285
+ if (!this.db)
286
+ return null;
287
+ try {
288
+ const columns = this.execute(`PRAGMA table_info('${tableName.replace(/'/g, "''")}')`);
289
+ // Get indexes for this table
290
+ const indexes = this.execute(`PRAGMA index_list('${tableName.replace(/'/g, "''")}')`);
291
+ // Get foreign keys
292
+ const foreignKeys = this.execute(`PRAGMA foreign_key_list('${tableName.replace(/'/g, "''")}')`);
293
+ return {
294
+ name: tableName,
295
+ columns: columns.map((col) => ({
296
+ cid: col.cid,
297
+ name: col.name,
298
+ type: col.type || 'ANY',
299
+ notNull: col.notnull === 1,
300
+ defaultValue: col.dflt_value,
301
+ isPrimaryKey: col.pk === 1
302
+ })),
303
+ indexes: indexes.map((idx) => ({
304
+ name: idx.name,
305
+ unique: idx.unique === 1,
306
+ origin: idx.origin
307
+ })),
308
+ foreignKeys: foreignKeys.map((fk) => ({
309
+ id: fk.id,
310
+ table: fk.table,
311
+ from: fk.from,
312
+ to: fk.to
313
+ })),
314
+ rowCount: this.getTableRowCount(tableName)
315
+ };
316
+ }
317
+ catch (e) {
318
+ console.warn(`Failed to get schema for table ${tableName}:`, e);
319
+ return null;
320
+ }
321
+ }
322
+ /**
323
+ * Get the row count for a specific table
324
+ */
325
+ getTableRowCount(tableName) {
326
+ if (!this.db)
327
+ return 0;
328
+ try {
329
+ const result = this.execute(`SELECT COUNT(*) as count FROM "${tableName.replace(/"/g, '""')}"`);
330
+ return result[0]?.count ?? 0;
331
+ }
332
+ catch (e) {
333
+ // Virtual tables may not support COUNT
334
+ return -1;
335
+ }
336
+ }
337
+ /**
338
+ * Get statistics about the vector index
339
+ */
340
+ getVectorIndexStats() {
341
+ if (!this.db) {
342
+ return { totalEmbeddings: 0, dimensions: 384, indexExists: false };
343
+ }
344
+ try {
345
+ const countResult = this.execute('SELECT COUNT(*) as count FROM dash_vec_idx');
346
+ const totalEmbeddings = countResult[0]?.count ?? 0;
347
+ return {
348
+ totalEmbeddings,
349
+ dimensions: 384, // Fixed dimension from schema
350
+ indexExists: true,
351
+ tableName: 'dash_vec_idx'
352
+ };
353
+ }
354
+ catch (e) {
355
+ return {
356
+ totalEmbeddings: 0,
357
+ dimensions: 384,
358
+ indexExists: false,
359
+ error: e instanceof Error ? e.message : 'Vector index not available'
360
+ };
361
+ }
362
+ }
363
+ /**
364
+ * Get all embeddings from the vector index
365
+ */
366
+ getAllEmbeddings(limit = 100, offset = 0) {
367
+ if (!this.db)
368
+ return [];
369
+ try {
370
+ const rows = this.execute(`
371
+ SELECT v.id, i.content, v.embedding
372
+ FROM dash_vec_idx v
373
+ LEFT JOIN dash_items i ON v.id = i.id
374
+ LIMIT ? OFFSET ?
375
+ `, [limit, offset]);
376
+ return rows.map((row) => ({
377
+ id: row.id,
378
+ content: row.content,
379
+ embedding: row.embedding ? Array.from(row.embedding) : null
380
+ }));
381
+ }
382
+ catch (e) {
383
+ console.warn('Failed to get embeddings:', e);
384
+ return [];
385
+ }
386
+ }
387
+ /**
388
+ * Search for similar vectors given a raw vector
389
+ */
390
+ searchSimilarByVector(vector, k = 5) {
391
+ if (!this.db)
392
+ return [];
393
+ try {
394
+ const rows = this.execute(`
395
+ SELECT
396
+ item.id,
397
+ item.content,
398
+ distance
399
+ FROM dash_vec_idx
400
+ JOIN dash_items AS item ON item.id = dash_vec_idx.id
401
+ WHERE embedding MATCH ?
402
+ ORDER BY distance
403
+ LIMIT ?
404
+ `, [new Float32Array(vector), k]);
405
+ return rows.map((row) => ({
406
+ id: row.id,
407
+ content: row.content,
408
+ distance: row.distance,
409
+ score: row.distance !== undefined ? 1 - row.distance : 0
410
+ }));
411
+ }
412
+ catch (e) {
413
+ console.warn('Vector search failed:', e);
414
+ return [];
415
+ }
416
+ }
417
+ /**
418
+ * Get statistics about the spatial R-Tree index
419
+ */
420
+ getSpatialIndexStats() {
421
+ if (!this.db) {
422
+ return { totalEntries: 0, indexExists: false };
423
+ }
424
+ try {
425
+ const countResult = this.execute('SELECT COUNT(*) as count FROM dash_spatial_idx');
426
+ const totalEntries = countResult[0]?.count ?? 0;
427
+ // Get bounding box of all entries
428
+ const boundsResult = this.execute(`
429
+ SELECT
430
+ MIN(minX) as minX, MAX(maxX) as maxX,
431
+ MIN(minY) as minY, MAX(maxY) as maxY,
432
+ MIN(minZ) as minZ, MAX(maxZ) as maxZ
433
+ FROM dash_spatial_idx
434
+ `);
435
+ const bounds = boundsResult[0] || {};
436
+ return {
437
+ totalEntries,
438
+ indexExists: true,
439
+ tableName: 'dash_spatial_idx',
440
+ globalBounds: totalEntries > 0 ? {
441
+ minX: bounds.minX,
442
+ maxX: bounds.maxX,
443
+ minY: bounds.minY,
444
+ maxY: bounds.maxY,
445
+ minZ: bounds.minZ,
446
+ maxZ: bounds.maxZ
447
+ } : undefined
448
+ };
449
+ }
450
+ catch (e) {
451
+ return {
452
+ totalEntries: 0,
453
+ indexExists: false,
454
+ error: e instanceof Error ? e.message : 'Spatial index not available'
455
+ };
456
+ }
457
+ }
458
+ /**
459
+ * Get all spatial bounds from the R-Tree index
460
+ */
461
+ getAllSpatialBounds(limit = 100, offset = 0) {
462
+ if (!this.db)
463
+ return [];
464
+ try {
465
+ const rows = this.execute(`
466
+ SELECT
467
+ idx.id as rowid,
468
+ map.item_id as id,
469
+ idx.minX, idx.maxX,
470
+ idx.minY, idx.maxY,
471
+ idx.minZ, idx.maxZ,
472
+ item.content
473
+ FROM dash_spatial_idx idx
474
+ JOIN dash_spatial_map map ON map.rowid = idx.id
475
+ LEFT JOIN dash_items item ON item.id = map.item_id
476
+ LIMIT ? OFFSET ?
477
+ `, [limit, offset]);
478
+ return rows.map((row) => ({
479
+ id: row.id,
480
+ rowid: row.rowid,
481
+ bounds: {
482
+ minX: row.minX,
483
+ maxX: row.maxX,
484
+ minY: row.minY,
485
+ maxY: row.maxY,
486
+ minZ: row.minZ,
487
+ maxZ: row.maxZ
488
+ },
489
+ content: row.content
490
+ }));
491
+ }
492
+ catch (e) {
493
+ console.warn('Failed to get spatial bounds:', e);
494
+ return [];
495
+ }
496
+ }
497
+ /**
498
+ * Get information about active table subscriptions
499
+ */
500
+ getActiveSubscriptions() {
501
+ const subscriptions = [];
502
+ this.tableListeners.forEach((listeners, tableName) => {
503
+ subscriptions.push({
504
+ table: tableName,
505
+ listenerCount: listeners.size
506
+ });
507
+ });
508
+ // Add global listeners
509
+ if (this.listeners.size > 0) {
510
+ subscriptions.push({
511
+ table: '*',
512
+ listenerCount: this.listeners.size
513
+ });
514
+ }
515
+ return subscriptions;
516
+ }
517
+ /**
518
+ * Get table listener counts as a map
519
+ */
520
+ getTableListenerCounts() {
521
+ const counts = {};
522
+ this.tableListeners.forEach((listeners, tableName) => {
523
+ counts[tableName] = listeners.size;
524
+ });
525
+ counts['*'] = this.listeners.size;
526
+ return counts;
527
+ }
528
+ /**
529
+ * Check if the database is ready
530
+ */
531
+ isReady() {
532
+ return this.db !== null;
533
+ }
534
+ /**
535
+ * Get database metadata
536
+ */
537
+ getDatabaseInfo() {
538
+ if (!this.db) {
539
+ return {
540
+ ready: false,
541
+ schemaVersion: this.currentSchemaVersion,
542
+ tableCount: 0
543
+ };
544
+ }
545
+ const tables = this.getAllTables();
546
+ return {
547
+ ready: true,
548
+ schemaVersion: this.currentSchemaVersion,
549
+ tableCount: tables.length,
550
+ tables: tables.map(t => t.name)
551
+ };
552
+ }
255
553
  }
256
554
  export const dash = new DashEngine();
@@ -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';