UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

226 lines (225 loc) 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NatsStreamService = void 0; const index_1 = require("../../index"); const key_1 = require("../../../../modules/key"); const enums_1 = require("../../../../modules/enums"); const utils_1 = require("../../../../modules/utils"); class NatsStreamService extends index_1.StreamService { constructor(streamClient, storeClient, config = {}) { super(streamClient, storeClient, config); this.jetstream = streamClient.jetstream(); } async init(namespace, appId, logger) { this.namespace = namespace; this.logger = logger; this.appId = appId; this.jsm = await this.jetstream.jetstreamManager(); } mintKey(type, params) { if (!this.namespace) throw new Error('namespace not set'); return key_1.KeyService.mintKey(this.namespace, type, { ...params, appId: this.appId, }); } transact() { return {}; } async createStream(streamName) { try { const config = { name: streamName, subjects: [`${streamName}.*`], retention: 'workqueue', storage: 'memory', num_replicas: 1, }; await this.jsm.streams.add(config); return true; } catch (error) { this.logger.error(`Error creating stream ${streamName}`, { error }); throw error; } } async deleteStream(streamName) { try { await this.jsm.streams.delete(streamName); return true; } catch (error) { this.logger.error(`Error deleting stream ${streamName}`, { error }); throw error; } } async createConsumerGroup(streamName, groupName) { try { const consumerConfig = { durable_name: groupName, deliver_group: groupName, ack_policy: 'explicit', ack_wait: 30 * 1000, max_deliver: 10, }; await this.jsm.consumers.add(streamName, consumerConfig); return true; } catch (error) { this.logger.error(`Error creating consumer group ${groupName} for stream ${streamName}`, { error }); throw error; } } async deleteConsumerGroup(streamName, groupName) { try { await this.jsm.consumers.delete(streamName, groupName); return true; } catch (error) { this.logger.error(`Error deleting consumer group ${groupName} for stream ${streamName}`, { error }); throw error; } } async publishMessages(streamName, messages, options) { try { const publishPromises = messages.map(async (message) => { const subject = `${streamName}.message`; const ack = await this.jetstream.publish(subject, Buffer.from(message)); return ack; }); const acks = await Promise.all(publishPromises); return acks.map((ack) => ack.seq.toString()); } catch (error) { this.logger.error(`Error publishing messages to ${streamName}`, { error, }); throw error; } } async consumeMessages(streamName, groupName, consumerName, options) { try { const consumer = await this.jetstream.consumers.get(streamName, groupName); const messages = []; const fetchOptions = { max_messages: options?.batchSize || 1, expires: options?.blockTimeout || enums_1.HMSH_BLOCK_TIME_MS, }; const fetchedMessages = await consumer.fetch(fetchOptions); for await (const msg of fetchedMessages) { messages.push({ id: msg.seq.toString(), data: (0, utils_1.parseStreamMessage)(msg.string()), }); } return messages; } catch (error) { this.logger.error(`Error consuming messages from ${streamName}`, { error, }); throw error; } } async ackAndDelete(streamName, groupName, messageIds) { try { await this.acknowledgeMessages(streamName, groupName, messageIds); return messageIds.length; } catch (error) { this.logger.error(`Error in ack and delete for stream ${streamName}`, { error, }); throw error; } } async acknowledgeMessages(streamName, groupName, messageIds, options) { //no-op return messageIds.length; } async deleteMessages(streamName, groupName, messageIds, options) { try { await Promise.all(messageIds.map((id) => this.jsm.streams.deleteMessage(streamName, parseInt(id)))); return messageIds.length; } catch (error) { this.logger.error(`Error deleting messages from ${streamName}`, { error, }); throw error; } } async retryMessages(streamName, groupName, options) { return []; } async getStreamStats(streamName) { try { const info = await this.jsm.streams.info(streamName); return { messageCount: info.state.messages, }; } catch (error) { this.logger.error(`Error getting stats for ${streamName}`, { error }); throw error; } } async getStreamDepth(streamName) { try { const info = await this.jsm.streams.info(streamName); return info.state.messages; } catch (error) { this.logger.error(`Error getting depth for ${streamName}`, { error }); throw error; } } async getStreamDepths(streamNames) { try { const results = await Promise.all(streamNames.map(async ({ stream }) => ({ stream, depth: await this.getStreamDepth(stream), }))); return results; } catch (error) { this.logger.error('Error getting multiple stream depths', { error }); throw error; } } async trimStream(streamName, options) { try { // Retrieve the current stream info const streamInfo = await this.jsm.streams.info(streamName); const config = { ...streamInfo.config }; // Clone to avoid direct mutation // Apply new limits based on options if (options.maxLen !== undefined) { config.max_msgs = options.maxLen; } if (options.maxAge !== undefined) { config.max_age = options.maxAge * 1e9; // Convert maxAge to nanoseconds } // Update the stream with the modified config await this.jsm.streams.update(streamName, config); return 0; // Trimming is applied automatically based on updated config } catch (error) { this.logger.error(`Error trimming stream ${streamName}`, { error }); throw error; } } getProviderSpecificFeatures() { return { supportsBatching: true, supportsDeadLetterQueue: true, supportsOrdering: true, supportsTrimming: true, supportsRetry: false, supportsNotifications: false, maxMessageSize: 1024 * 1024, maxBatchSize: 256, }; } } exports.NatsStreamService = NatsStreamService;