UNPKG

@fairmint/canton-node-sdk

Version:
195 lines 8.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SubscribeToUpdates = exports.TransactionShape = void 0; const zod_1 = require("zod"); const WebSocketClient_1 = require("../../../../../core/ws/WebSocketClient"); const event_format_builder_1 = require("../utils/event-format-builder"); const path = '/v2/updates'; /** * The `TransactionShape` enum defines the event shape for `Transaction`s and can have two different flavors AcsDelta * and LedgerEffects. * * ```protobuf * enum TransactionShape { * TRANSACTION_SHAPE_ACS_DELTA = 1; * TRANSACTION_SHAPE_LEDGER_EFFECTS = 2; * } * ``` * * - AcsDelta * * The transaction shape that is sufficient to maintain an accurate ACS view. This translates to create and archive * events. The field witness_parties in events are populated as stakeholders, transaction filter will apply * accordingly. * - LedgerEffects * * The transaction shape that allows maintaining an ACS and also conveys detailed information about all exercises. This * translates to create, consuming exercise and non-consuming exercise. The field witness_parties in events are * populated as cumulative informees, transaction filter will apply accordingly. */ var TransactionShape; (function (TransactionShape) { TransactionShape["TRANSACTION_SHAPE_ACS_DELTA"] = "TRANSACTION_SHAPE_ACS_DELTA"; TransactionShape["TRANSACTION_SHAPE_LEDGER_EFFECTS"] = "TRANSACTION_SHAPE_LEDGER_EFFECTS"; })(TransactionShape || (exports.TransactionShape = TransactionShape = {})); const SubscribeToUpdatesParamsSchema = zod_1.z.object({ /** Optional list of parties to scope the filter. */ parties: zod_1.z.array(zod_1.z.string()).optional(), /** Optional template filters applied server-side. */ templateIds: zod_1.z.array(zod_1.z.string()).optional(), /** Include created event blob in TemplateFilter results (default false). */ includeCreatedEventBlob: zod_1.z.boolean().optional(), /** Beginning of the requested ledger section. Defaults to ledger end if not provided. */ beginExclusive: zod_1.z.number().optional(), /** * End of the requested ledger section (optional). * * When specified: Creates a bounded subscription that closes after reaching this offset. When omitted: Creates a * persistent streaming connection that remains open indefinitely, delivering real-time updates as they occur on the * ledger. The connection will remain active until manually closed or an error occurs. * * Use bounded subscriptions (with endInclusive) when catching up on historical data. Use unbounded subscriptions * (without endInclusive) for real-time streaming. */ endInclusive: zod_1.z.number().optional(), /** Include reassignments in the stream (default true). */ includeReassignments: zod_1.z.boolean().optional(), /** Include topology events in the stream (default false). */ includeTopologyEvents: zod_1.z.boolean().optional(), /** Transaction shape (default TRANSACTION_SHAPE_LEDGER_EFFECTS). */ transactionShape: zod_1.z.nativeEnum(TransactionShape).optional(), }); /** * Subscribes to ledger updates via WebSocket connection. * * Supports two modes of operation: * * 1. **Bounded subscription**: Specify both `beginExclusive` and `endInclusive` to fetch a specific range of historical * transactions. The connection closes after reaching `endInclusive`. * 2. **Persistent streaming**: Specify only `beginExclusive` (or omit both) to create a long-lived WebSocket connection * that streams real-time updates indefinitely. The connection remains open until manually closed via the returned * subscription object or an error occurs. * * @example * ```typescript * // Bounded subscription (historical data) * await client.subscribeToUpdates({ * beginExclusive: 1000, * endInclusive: 2000, * parties: ['party1'], * onMessage: (msg) => console.log(msg) * }); * * // Persistent streaming (real-time updates) * await client.subscribeToUpdates({ * beginExclusive: 2000, * // endInclusive omitted - connection stays open * parties: ['party1'], * onMessage: (msg) => console.log(msg) * }); * ```; */ class SubscribeToUpdates { constructor(client) { this.client = client; } async connect(params) { const validated = SubscribeToUpdatesParamsSchema.parse(params); // Determine beginExclusive (default to ledger end if not specified) let { beginExclusive } = validated; if (beginExclusive === undefined) { const ledgerEnd = await this.client.getLedgerEnd({}); beginExclusive = ledgerEnd.offset; } // Build party list - default to client's party list if not provided const partyList = validated.parties && validated.parties.length > 0 ? validated.parties : this.client.buildPartyList(); // Build event format const eventFormat = (0, event_format_builder_1.buildEventFormat)({ parties: partyList, ...(validated.templateIds !== undefined && { templateIds: validated.templateIds }), ...(validated.includeCreatedEventBlob !== undefined && { includeCreatedEventBlob: validated.includeCreatedEventBlob, }), }); // Build update format with optional features const updateFormat = { includeTransactions: { eventFormat, transactionShape: validated.transactionShape ?? TransactionShape.TRANSACTION_SHAPE_LEDGER_EFFECTS, }, }; // Add reassignments if enabled (default true) if (validated.includeReassignments !== false) { updateFormat.includeReassignments = { filtersByParty: eventFormat.filtersByParty, verbose: false, }; } // Add topology events if enabled (default false) if (validated.includeTopologyEvents === true) { updateFormat.includeTopologyEvents = { includeParticipantAuthorizationEvents: { parties: partyList, }, }; } // Build request message const requestMessage = { beginExclusive, endInclusive: validated.endInclusive, verbose: false, updateFormat, }; const wsClient = new WebSocketClient_1.WebSocketClient(this.client); return new Promise((resolve, reject) => { let settled = false; void wsClient .connect(path, requestMessage, { onMessage: (raw) => { try { // Skip Zod validation for response types - just use the raw parsed JSON // Zod validation is only needed for input types, not outputs const parsed = raw; // Call user's onMessage callback if provided if (typeof params.onMessage === 'function') { params.onMessage(parsed); } // Check if it's an error if (typeof parsed === 'object' && ('errors' in parsed || 'error' in parsed)) { if (!settled) { settled = true; reject(parsed); } } } catch (e) { if (!settled) { settled = true; reject(e); } } }, onError: (err) => { if (!settled) { settled = true; reject(err); } }, onClose: () => { if (!settled) { settled = true; resolve(); } }, }) .catch((err) => { if (!settled) { settled = true; reject(err); } }); }); } } exports.SubscribeToUpdates = SubscribeToUpdates; //# sourceMappingURL=subscribe-to-updates.js.map