@hotmeshio/hotmesh
Version:
Permanent-Memory Workflows & AI Agents
114 lines (113 loc) • 3.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getProviderSpecificFeatures = exports.isNotificationsEnabled = exports.trimStream = exports.getStreamDepths = exports.getStreamDepth = exports.getStreamStats = void 0;
/**
* Get statistics for a specific stream.
* Returns message count and other metrics.
*/
async function getStreamStats(client, tableName, streamName, logger) {
try {
const res = await client.query(`SELECT COUNT(*) AS available_count
FROM ${tableName}
WHERE stream_name = $1 AND expired_at IS NULL`, [streamName]);
return {
messageCount: parseInt(res.rows[0].available_count, 10),
};
}
catch (error) {
logger.error(`postgres-stream-stats-error-${streamName}`, { error });
throw error;
}
}
exports.getStreamStats = getStreamStats;
/**
* Get the depth (message count) for a specific stream.
*/
async function getStreamDepth(client, tableName, streamName, logger) {
const stats = await getStreamStats(client, tableName, streamName, logger);
return stats.messageCount;
}
exports.getStreamDepth = getStreamDepth;
/**
* Get depths for multiple streams in a single query.
* More efficient than calling getStreamDepth multiple times.
*/
async function getStreamDepths(client, tableName, streamNames, logger) {
try {
const streams = streamNames.map((s) => s.stream);
const res = await client.query(`SELECT stream_name, COUNT(*) AS count
FROM ${tableName}
WHERE stream_name = ANY($1::text[])
GROUP BY stream_name`, [streams]);
const result = res.rows.map((row) => ({
stream: row.stream_name,
depth: parseInt(row.count, 10),
}));
return result;
}
catch (error) {
logger.error('postgres-stream-depth-error', { error });
throw error;
}
}
exports.getStreamDepths = getStreamDepths;
/**
* Trim (soft delete) messages from a stream based on age or count.
* Returns the number of messages expired.
*/
async function trimStream(client, tableName, streamName, options, logger) {
try {
let expiredCount = 0;
if (options.maxLen !== undefined) {
const res = await client.query(`WITH to_expire AS (
SELECT id FROM ${tableName}
WHERE stream_name = $1
ORDER BY id ASC
OFFSET $2
)
UPDATE ${tableName}
SET expired_at = NOW()
WHERE id IN (SELECT id FROM to_expire)`, [streamName, options.maxLen]);
expiredCount += res.rowCount;
}
if (options.maxAge !== undefined) {
const res = await client.query(`UPDATE ${tableName}
SET expired_at = NOW()
WHERE stream_name = $1 AND created_at < NOW() - INTERVAL '${options.maxAge} milliseconds'`, [streamName]);
expiredCount += res.rowCount;
}
return expiredCount;
}
catch (error) {
logger.error(`postgres-stream-trim-error-${streamName}`, { error });
throw error;
}
}
exports.trimStream = trimStream;
/**
* Check if notifications are enabled for the provider.
*/
function isNotificationsEnabled(config = {}) {
// Allow override via environment variable
if (process.env.HOTMESH_POSTGRES_DISABLE_NOTIFICATIONS === 'true') {
return false;
}
return config?.postgres?.enableNotifications !== false; // Default: true
}
exports.isNotificationsEnabled = isNotificationsEnabled;
/**
* Get provider-specific feature flags and capabilities.
*/
function getProviderSpecificFeatures(config = {}) {
return {
supportsBatching: true,
supportsDeadLetterQueue: false,
supportsOrdering: true,
supportsTrimming: true,
supportsRetry: false,
supportsNotifications: isNotificationsEnabled(config),
maxMessageSize: 1024 * 1024,
maxBatchSize: 256,
};
}
exports.getProviderSpecificFeatures = getProviderSpecificFeatures;