feat(streaming): add global activity watchers, client-side buffering, and improved real-time streaming UX

This commit is contained in:
2026-01-28 14:02:48 +00:00
parent ad8529cb0f
commit 8cc9a1850a
14 changed files with 630 additions and 146 deletions

View File

@@ -1,13 +1,15 @@
import * as plugins from '../plugins.js';
import type * as interfaces from '../interfaces/index.js';
import type { TsView } from '../tsview.classes.tsview.js';
import type { ChangeStreamManager } from '../streaming/index.js';
/**
* Register MongoDB API handlers
*/
export async function registerMongoHandlers(
typedrouter: plugins.typedrequest.TypedRouter,
tsview: TsView
tsview: TsView,
changeStreamManager?: ChangeStreamManager
): Promise<void> {
// Helper to get the native MongoDB client
const getMongoClient = async () => {
@@ -122,6 +124,14 @@ export async function registerMongoHandlers(
const db = client.db(reqData.databaseName);
// Create a placeholder collection to materialize the database
await db.createCollection('_tsview_init');
if (changeStreamManager) {
changeStreamManager.emitMongoActivityEvent({
type: 'insert',
database: reqData.databaseName,
collection: '_tsview_init',
timestamp: new Date().toISOString(),
});
}
return { success: true };
} catch (err) {
console.error('Error creating database:', err);
@@ -140,6 +150,14 @@ export async function registerMongoHandlers(
const client = await getMongoClient();
const db = client.db(reqData.databaseName);
await db.dropDatabase();
if (changeStreamManager) {
changeStreamManager.emitMongoActivityEvent({
type: 'drop',
database: reqData.databaseName,
collection: '*',
timestamp: new Date().toISOString(),
});
}
return { success: true };
} catch (err) {
console.error('Error dropping database:', err);
@@ -158,6 +176,14 @@ export async function registerMongoHandlers(
const client = await getMongoClient();
const db = client.db(reqData.databaseName);
await db.createCollection(reqData.collectionName);
if (changeStreamManager) {
changeStreamManager.emitMongoActivityEvent({
type: 'insert',
database: reqData.databaseName,
collection: reqData.collectionName,
timestamp: new Date().toISOString(),
});
}
return { success: true };
} catch (err) {
console.error('Error creating collection:', err);
@@ -176,6 +202,14 @@ export async function registerMongoHandlers(
const client = await getMongoClient();
const db = client.db(reqData.databaseName);
await db.dropCollection(reqData.collectionName);
if (changeStreamManager) {
changeStreamManager.emitMongoActivityEvent({
type: 'drop',
database: reqData.databaseName,
collection: reqData.collectionName,
timestamp: new Date().toISOString(),
});
}
return { success: true };
} catch (err) {
console.error('Error dropping collection:', err);
@@ -267,6 +301,16 @@ export async function registerMongoHandlers(
const result = await collection.insertOne(reqData.document);
if (changeStreamManager) {
changeStreamManager.emitMongoActivityEvent({
type: 'insert',
database: reqData.databaseName,
collection: reqData.collectionName,
documentId: result.insertedId.toString(),
timestamp: new Date().toISOString(),
});
}
return { insertedId: result.insertedId.toString() };
} catch (err) {
console.error('Error inserting document:', err);
@@ -294,6 +338,16 @@ export async function registerMongoHandlers(
const result = await collection.updateOne(filter, updateDoc);
if (changeStreamManager && (result.modifiedCount > 0 || result.matchedCount > 0)) {
changeStreamManager.emitMongoActivityEvent({
type: 'update',
database: reqData.databaseName,
collection: reqData.collectionName,
documentId: reqData.documentId,
timestamp: new Date().toISOString(),
});
}
return {
success: result.modifiedCount > 0 || result.matchedCount > 0,
modifiedCount: result.modifiedCount,
@@ -319,6 +373,16 @@ export async function registerMongoHandlers(
const filter = createIdFilter(reqData.documentId);
const result = await collection.deleteOne(filter);
if (changeStreamManager && result.deletedCount > 0) {
changeStreamManager.emitMongoActivityEvent({
type: 'delete',
database: reqData.databaseName,
collection: reqData.collectionName,
documentId: reqData.documentId,
timestamp: new Date().toISOString(),
});
}
return {
success: result.deletedCount > 0,
deletedCount: result.deletedCount,