@stacks/node-publisher-client 0.2.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.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # @stacks/node-publisher-client
2
+
3
+ A TypeScript client library for consuming Stacks blockchain events from the [Stacks Node Publisher](https://github.com/stx-labs/stacks-node-publisher) service.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @stacks/node-publisher-client
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import {
15
+ StacksMessageStream,
16
+ MessagePath,
17
+ StreamPosition,
18
+ Message,
19
+ } from '@stacks/node-publisher-client';
20
+
21
+ // Create the stream client
22
+ const stream = new StacksMessageStream({
23
+ appName: 'my-app',
24
+ redisUrl: 'redis://localhost:6379',
25
+ });
26
+
27
+ // Connect to Redis
28
+ await stream.connect({ waitForReady: true });
29
+
30
+ // Define where to start streaming from
31
+ const getStartPosition = async (): Promise<StreamPosition> => {
32
+ return null; // Start from the beginning
33
+ };
34
+
35
+ // Handle incoming messages
36
+ const handleMessage = async (id: string, timestamp: string, message: Message) => {
37
+ console.log(`Received ${message.path} at ${timestamp}`);
38
+ };
39
+
40
+ // Start streaming
41
+ stream.start(getStartPosition, handleMessage);
42
+ ```
43
+
44
+ ## API Reference
45
+
46
+ ### `StacksMessageStream`
47
+
48
+ The main client class for connecting to and consuming events from a Stacks Node Publisher service.
49
+
50
+ #### Constructor Options
51
+
52
+ ```typescript
53
+ new StacksMessageStream({
54
+ appName: string; // Required: Unique identifier for your application
55
+ redisUrl?: string; // Redis connection URL (default: localhost)
56
+ redisStreamPrefix?: string; // Prefix for Redis stream keys
57
+ options?: {
58
+ selectedMessagePaths?: MessagePath[] | '*'; // Filter by message types (default: '*')
59
+ batchSize?: number; // Messages per batch (default: 100)
60
+ };
61
+ });
62
+ ```
63
+
64
+ #### Methods
65
+
66
+ ##### `connect(options: { waitForReady: boolean }): Promise<void>`
67
+
68
+ Connects to the Redis server.
69
+
70
+ - `waitForReady: true` - Blocks until connected (recommended for startup)
71
+ - `waitForReady: false` - Connects in the background
72
+
73
+ ```typescript
74
+ await stream.connect({ waitForReady: true });
75
+ ```
76
+
77
+ ##### `start(positionCallback, messageCallback): void`
78
+
79
+ Starts consuming the event stream.
80
+
81
+ - `positionCallback: () => Promise<StreamPosition>` - Called to determine where to start/resume streaming
82
+ - `messageCallback: (id, timestamp, message) => Promise<void>` - Called for each received message
83
+
84
+ ```typescript
85
+ stream.start(
86
+ async () => ({ indexBlockHash: '0x...', blockHeight: 150000 }),
87
+ async (id, timestamp, message) => {
88
+ // Process message
89
+ }
90
+ );
91
+ ```
92
+
93
+ ##### `stop(): Promise<void>`
94
+
95
+ Gracefully stops the stream and closes the Redis connection.
96
+
97
+ ```typescript
98
+ await stream.stop();
99
+ ```
100
+
101
+ ### `StreamPosition`
102
+
103
+ Defines where to start or resume the event stream.
104
+
105
+ ```typescript
106
+ type StreamPosition =
107
+ | { indexBlockHash: string; blockHeight: number } // Start from a specific block
108
+ | { messageId: string } // Start from a specific message ID
109
+ | null; // Start from the beginning
110
+ ```
111
+
112
+ ## Message Types
113
+
114
+ ### `MessagePath`
115
+
116
+ Enum of all available message paths:
117
+
118
+ | Path | Description |
119
+ |------|-------------|
120
+ | `MessagePath.NewBlock` | New Stacks block with transactions and events |
121
+ | `MessagePath.NewBurnBlock` | Bitcoin anchor block information |
122
+ | `MessagePath.NewMempoolTx` | Transactions entering the mempool |
123
+ | `MessagePath.DropMempoolTx` | Transactions removed from the mempool |
124
+ | `MessagePath.StackerDbChunks` | Signer and StackerDB data chunks |
125
+ | `MessagePath.NewMicroblocks` | Microblock data (legacy) |
126
+ | `MessagePath.ProposalResponse` | Miner block proposal responses |
127
+ | `MessagePath.AttachmentsNew` | Attachment data (legacy) |
128
+
129
+ ### `NewBlockMessage`
130
+
131
+ Contains full block data including:
132
+
133
+ - Block metadata (hash, height, timestamps)
134
+ - All transactions with execution results
135
+ - Events (STX transfers, contract events, NFT/FT operations)
136
+ - Miner rewards
137
+ - Signer information (epoch 3+)
138
+
139
+ ```typescript
140
+ interface NewBlockMessage {
141
+ block_hash: string;
142
+ block_height: number;
143
+ index_block_hash: string;
144
+ burn_block_hash: string;
145
+ burn_block_height: number;
146
+ burn_block_time: number;
147
+ transactions: NewBlockTransaction[];
148
+ events: NewBlockEvent[];
149
+ matured_miner_rewards: MinerReward[];
150
+ // ... additional fields
151
+ }
152
+ ```
153
+
154
+ ### `NewBurnBlockMessage`
155
+
156
+ Bitcoin block anchoring information:
157
+
158
+ ```typescript
159
+ interface NewBurnBlockMessage {
160
+ burn_block_hash: string;
161
+ burn_block_height: number;
162
+ burn_amount: number; // BTC satoshis
163
+ reward_recipients: { recipient: string; amt: number }[];
164
+ reward_slot_holders: string[]; // Bitcoin addresses
165
+ }
166
+ ```
167
+
168
+ ### `NewMempoolTxMessage`
169
+
170
+ Array of raw hex-encoded transactions entering the mempool:
171
+
172
+ ```typescript
173
+ type NewMempoolTxMessage = string[];
174
+ ```
175
+
176
+ ### `DropMempoolTxMessage`
177
+
178
+ Transactions removed from the mempool:
179
+
180
+ ```typescript
181
+ interface DropMempoolTxMessage {
182
+ dropped_txids: string[];
183
+ reason: 'ReplaceByFee' | 'ReplaceAcrossFork' | 'TooExpensive' | 'StaleGarbageCollect' | 'Problematic';
184
+ new_txid: string | null;
185
+ }
186
+ ```
187
+
188
+ ### `StackerDbChunksMessage`
189
+
190
+ Signer and StackerDB data chunks:
191
+
192
+ ```typescript
193
+ interface StackerDbChunksMessage {
194
+ contract_id: { issuer: [number, number[]]; name: string };
195
+ modified_slots: {
196
+ slot_id: number;
197
+ slot_version: number;
198
+ data: string; // hex string
199
+ sig: string; // hex signature
200
+ }[];
201
+ }
202
+ ```
203
+
204
+ ## Usage Examples
205
+
206
+ ### Filtering by Message Type
207
+
208
+ ```typescript
209
+ const stream = new StacksMessageStream({
210
+ appName: 'block-indexer',
211
+ redisUrl: 'redis://localhost:6379',
212
+ options: {
213
+ selectedMessagePaths: [MessagePath.NewBlock, MessagePath.NewBurnBlock],
214
+ },
215
+ });
216
+ ```
217
+
218
+ ### Resuming from Last Processed Block
219
+
220
+ ```typescript
221
+ const getStartPosition = async (): Promise<StreamPosition> => {
222
+ const lastBlock = await db.getLastProcessedBlock();
223
+ if (lastBlock) {
224
+ return {
225
+ indexBlockHash: lastBlock.indexBlockHash,
226
+ blockHeight: lastBlock.height,
227
+ };
228
+ }
229
+ return null; // Start from beginning if no prior state
230
+ };
231
+
232
+ stream.start(getStartPosition, handleMessage);
233
+ ```
234
+
235
+ ### Processing Block Events
236
+
237
+ ```typescript
238
+ import { MessagePath, NewBlockMessage, NewBlockEventType } from '@stacks/node-publisher-client';
239
+
240
+ const handleMessage = async (id: string, timestamp: string, message: Message) => {
241
+ if (message.path !== MessagePath.NewBlock) return;
242
+
243
+ const block: NewBlockMessage = message.payload;
244
+
245
+ for (const event of block.events) {
246
+ switch (event.type) {
247
+ case NewBlockEventType.StxTransfer:
248
+ console.log(`STX Transfer: ${event.stx_transfer_event.amount} microSTX`);
249
+ console.log(` From: ${event.stx_transfer_event.sender}`);
250
+ console.log(` To: ${event.stx_transfer_event.recipient}`);
251
+ break;
252
+
253
+ case NewBlockEventType.Contract:
254
+ console.log(`Contract Event: ${event.contract_event.contract_identifier}`);
255
+ console.log(` Topic: ${event.contract_event.topic}`);
256
+ break;
257
+
258
+ case NewBlockEventType.NftMint:
259
+ console.log(`NFT Minted: ${event.nft_mint_event.asset_identifier}`);
260
+ break;
261
+ }
262
+ }
263
+ };
264
+ ```
265
+
266
+ ### Handling Reconnection
267
+
268
+ The client automatically handles reconnection. You can listen for the `redisConsumerGroupDestroyed` event to perform additional cleanup:
269
+
270
+ ```typescript
271
+ stream.events.on('redisConsumerGroupDestroyed', () => {
272
+ console.log('Consumer group was destroyed, will automatically reconnect');
273
+ // Optionally refresh your position callback state
274
+ });
275
+ ```
276
+
277
+ ## TypeScript Support
278
+
279
+ This package is written in TypeScript and includes full type definitions. All message types are fully typed:
280
+
281
+ ```typescript
282
+ import type {
283
+ Message,
284
+ MessagePath,
285
+ StreamPosition,
286
+ NewBlockMessage,
287
+ NewBlockEvent,
288
+ NewBlockTransaction,
289
+ NewBurnBlockMessage,
290
+ NewMempoolTxMessage,
291
+ DropMempoolTxMessage,
292
+ StackerDbChunksMessage,
293
+ ClarityAbi,
294
+ } from '@stacks/node-publisher-client';
295
+ ```
296
+
297
+ ## License
298
+
299
+ GPL-3.0-only
@@ -0,0 +1,97 @@
1
+ import { RedisClientType } from 'redis';
2
+ import { EventEmitter } from 'node:events';
3
+ import { Message, MessagePath } from './messages';
4
+ /**
5
+ * The starting position for the message stream. Can be either an index block hash with block height
6
+ * or a message ID.
7
+ * - `indexBlockHash` + `blockHeight`: The index block hash and height of the Stacks block to start
8
+ * from. The backend will resolve this to the corresponding message ID. If the block hash doesn't
9
+ * exist but the height is higher than the highest available, it will start from the highest
10
+ * available block.
11
+ * - `messageId`: The message ID to start from. The backend will validate this ID exists and is not
12
+ * greater than the highest available ID.
13
+ *
14
+ * If neither is provided or validation fails, the stream will start from the beginning.
15
+ */
16
+ export type StreamPosition = {
17
+ indexBlockHash: string;
18
+ blockHeight: number;
19
+ } | {
20
+ messageId: string;
21
+ } | null;
22
+ /**
23
+ * The callback function for retrieving the starting position for the event stream. Should return
24
+ * either an index block hash with block height or a message ID. This callback is used to determine
25
+ * the starting message ID for the event stream and may be called periodically on reconnection.
26
+ */
27
+ export type StreamPositionCallback = () => Promise<StreamPosition>;
28
+ /**
29
+ * The callback function for event stream ingestion. Will be called for each message in the event
30
+ * stream. The callback should return a promise that resolves when the message has been processed.
31
+ */
32
+ export type MessageCallback = (
33
+ /** The message ID. */
34
+ id: string,
35
+ /** The timestamp of the message. */
36
+ timestamp: string,
37
+ /** The message */
38
+ message: Message) => Promise<void>;
39
+ /**
40
+ * Message paths to include in a message stream, where `*` means client wants to receive all
41
+ * messages.
42
+ */
43
+ export type SelectedMessagePaths = MessagePath[] | '*';
44
+ /**
45
+ * Stacks message stream options.
46
+ */
47
+ export type StreamOptions = {
48
+ /**
49
+ * Message paths to include in a message stream, where `*` means client wants to receive all
50
+ * messages.
51
+ */
52
+ selectedMessagePaths?: SelectedMessagePaths;
53
+ /**
54
+ * The batch size for the message stream.
55
+ */
56
+ batchSize?: number;
57
+ };
58
+ /**
59
+ * A client for a Stacks core node message stream.
60
+ */
61
+ export declare class StacksMessageStream {
62
+ static readonly GROUP_NAME = "primary_group";
63
+ static readonly CONSUMER_NAME = "primary_consumer";
64
+ readonly client: RedisClientType;
65
+ clientId: `${string}-${string}-${string}-${string}-${string}`;
66
+ private readonly selectedPaths;
67
+ private readonly redisStreamPrefix;
68
+ private readonly appName;
69
+ private readonly abort;
70
+ private readonly streamWaiter;
71
+ private readonly logger;
72
+ private readonly msgBatchSize;
73
+ /** For testing purposes only. The last message ID that was processed by this client. */
74
+ lastProcessedMessageId: string;
75
+ /** For testing purposes only. The connection status of the client. */
76
+ connectionStatus: 'not_started' | 'connected' | 'reconnecting' | 'ended';
77
+ readonly events: EventEmitter<{
78
+ redisConsumerGroupDestroyed: [];
79
+ msgReceived: [{
80
+ id: string;
81
+ }];
82
+ }>;
83
+ constructor(args: {
84
+ appName: string;
85
+ redisUrl?: string;
86
+ redisStreamPrefix?: string;
87
+ options?: StreamOptions;
88
+ });
89
+ sanitizeRedisClientName(name: string): string;
90
+ get redisClientName(): string;
91
+ connect({ waitForReady }: {
92
+ waitForReady: boolean;
93
+ }): Promise<void>;
94
+ start(positionCallback: StreamPositionCallback, messageCallback: MessageCallback): void;
95
+ private ingestEventStream;
96
+ stop(): Promise<void>;
97
+ }
package/dist/index.js ADDED
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StacksMessageStream = void 0;
4
+ const redis_1 = require("redis");
5
+ const api_toolkit_1 = require("@hirosystems/api-toolkit");
6
+ const node_crypto_1 = require("node:crypto");
7
+ const node_events_1 = require("node:events");
8
+ /**
9
+ * A client for a Stacks core node message stream.
10
+ */
11
+ class StacksMessageStream {
12
+ constructor(args) {
13
+ this.clientId = (0, node_crypto_1.randomUUID)();
14
+ this.logger = api_toolkit_1.logger.child({ module: 'StacksMessageStream' });
15
+ /** For testing purposes only. The last message ID that was processed by this client. */
16
+ this.lastProcessedMessageId = '0-0';
17
+ /** For testing purposes only. The connection status of the client. */
18
+ this.connectionStatus = 'not_started';
19
+ this.events = new node_events_1.EventEmitter();
20
+ this.selectedPaths = args.options?.selectedMessagePaths ?? '*';
21
+ this.abort = new AbortController();
22
+ this.streamWaiter = (0, api_toolkit_1.waiter)();
23
+ this.redisStreamPrefix = args.redisStreamPrefix ?? '';
24
+ if (this.redisStreamPrefix !== '' && !this.redisStreamPrefix.endsWith(':')) {
25
+ this.redisStreamPrefix += ':';
26
+ }
27
+ this.appName = this.sanitizeRedisClientName(args.appName);
28
+ this.msgBatchSize = args.options?.batchSize ?? 100;
29
+ this.client = (0, redis_1.createClient)({
30
+ url: args.redisUrl,
31
+ name: this.redisClientName,
32
+ disableOfflineQueue: true,
33
+ });
34
+ // Must have a listener for 'error' events to avoid unhandled exceptions
35
+ this.client.on('error', (err) => this.logger.error(err, `Redis error for client ${this.clientId}`));
36
+ this.client.on('reconnecting', () => {
37
+ this.connectionStatus = 'reconnecting';
38
+ this.logger.info(`Reconnecting to Redis for client ${this.clientId}`);
39
+ });
40
+ this.client.on('ready', () => {
41
+ this.connectionStatus = 'connected';
42
+ this.logger.info(`Redis connection ready for client ${this.clientId}`);
43
+ });
44
+ this.client.on('end', () => {
45
+ this.connectionStatus = 'ended';
46
+ this.logger.info(`Redis connection ended for client ${this.clientId}`);
47
+ });
48
+ }
49
+ // Sanitize the redis client name to only include valid characters (same approach used in the
50
+ // StackExchange.RedisClient https://github.com/StackExchange/StackExchange.Redis/pull/2654/files)
51
+ sanitizeRedisClientName(name) {
52
+ const nameSanitizer = /[^!-~]+/g;
53
+ return name.trim().replace(nameSanitizer, '-');
54
+ }
55
+ get redisClientName() {
56
+ return `${this.redisStreamPrefix}snp-consumer:${this.appName}:${this.clientId}`;
57
+ }
58
+ async connect({ waitForReady }) {
59
+ // Taken from `RedisBroker`.
60
+ if (waitForReady) {
61
+ while (true) {
62
+ try {
63
+ await this.client.connect();
64
+ this.logger.info(`Connected to Redis for client ${this.clientId}`);
65
+ break;
66
+ }
67
+ catch (err) {
68
+ this.logger.error(err, `Error connecting to Redis for client ${this.clientId}, retrying...`);
69
+ await (0, api_toolkit_1.timeout)(500);
70
+ }
71
+ }
72
+ }
73
+ else {
74
+ void this.client.connect().catch((err) => {
75
+ this.logger.error(err, `Error connecting to Redis for client ${this.clientId}, retrying...`);
76
+ void (0, api_toolkit_1.timeout)(500).then(() => this.connect({ waitForReady }));
77
+ });
78
+ }
79
+ }
80
+ start(positionCallback, messageCallback) {
81
+ this.logger.info(`Starting stream ingestion for client ${this.clientId}`);
82
+ const runIngest = async () => {
83
+ while (!this.abort.signal.aborted) {
84
+ try {
85
+ const startingPosition = await positionCallback();
86
+ this.logger.info(`Starting position: ${JSON.stringify(startingPosition)}`);
87
+ await this.ingestEventStream(startingPosition, messageCallback);
88
+ }
89
+ catch (error) {
90
+ if (this.abort.signal.aborted) {
91
+ this.logger.info('Stream ingestion aborted');
92
+ break;
93
+ }
94
+ else if (error.message?.includes('NOGROUP')) {
95
+ // The redis stream doesn't exist. This can happen if the redis server was restarted,
96
+ // or if the client is idle/offline, or if the client is processing messages too slowly.
97
+ // If this code path is reached, then we're obviously online so we just need to re-initialize
98
+ // the connection.
99
+ this.logger.error(error, `The Redis stream group for this client was destroyed by the server for client ${this.clientId}`);
100
+ this.events.emit('redisConsumerGroupDestroyed');
101
+ // re-announce connection, re-create group, etc
102
+ continue;
103
+ }
104
+ else {
105
+ // TODO: what are other expected errors and how should we handle them? For now we just retry
106
+ // forever.
107
+ this.logger.error(error, `Error reading or acknowledging from stream for client ${this.clientId}`);
108
+ this.logger.info('Reconnecting to Redis stream in 1 second...');
109
+ await (0, api_toolkit_1.timeout)(1000);
110
+ continue;
111
+ }
112
+ }
113
+ }
114
+ };
115
+ void runIngest()
116
+ .then(() => {
117
+ this.streamWaiter.finish();
118
+ })
119
+ .catch((err) => {
120
+ this.logger.error(err, 'Ingestion stream error');
121
+ });
122
+ }
123
+ async ingestEventStream(startingPosition, eventCallback) {
124
+ // Reset clientId for each new connection, this prevents race-conditions around cleanup
125
+ // for any previous connections.
126
+ this.clientId = (0, node_crypto_1.randomUUID)();
127
+ this.logger.info(`Connecting to Redis stream with clientId: ${this.clientId}`);
128
+ const streamKey = `${this.redisStreamPrefix}client:${this.clientId}`;
129
+ await this.client.clientSetName(this.redisClientName);
130
+ const handshakeMsg = {
131
+ client_id: this.clientId,
132
+ last_index_block_hash: startingPosition && 'indexBlockHash' in startingPosition
133
+ ? startingPosition.indexBlockHash
134
+ : '',
135
+ last_block_height: startingPosition && 'blockHeight' in startingPosition
136
+ ? startingPosition.blockHeight.toString()
137
+ : '',
138
+ last_message_id: startingPosition && 'messageId' in startingPosition ? startingPosition.messageId : '',
139
+ app_name: this.appName,
140
+ selected_paths: JSON.stringify(this.selectedPaths),
141
+ };
142
+ await this.client
143
+ .multi()
144
+ // Announce connection
145
+ .xAdd(this.redisStreamPrefix + 'connection_stream', '*', handshakeMsg)
146
+ // Create group for this stream
147
+ .xGroupCreate(streamKey, StacksMessageStream.GROUP_NAME, '$', {
148
+ MKSTREAM: true,
149
+ })
150
+ .exec();
151
+ // Start reading messages from the stream.
152
+ while (!this.abort.signal.aborted) {
153
+ // The backend creates the group with the correct starting position, so we use '>' here to
154
+ // get only messages after the last message ID.
155
+ const results = await this.client.xReadGroup(StacksMessageStream.GROUP_NAME, StacksMessageStream.CONSUMER_NAME, {
156
+ key: streamKey,
157
+ id: '>',
158
+ }, {
159
+ COUNT: this.msgBatchSize,
160
+ BLOCK: 1000, // Wait 1 second for new events.
161
+ });
162
+ if (!results) {
163
+ continue;
164
+ }
165
+ for (const stream of results) {
166
+ if (stream.messages.length > 0) {
167
+ this.logger.debug(`Received messages ${stream.messages[0].id} - ${stream.messages[stream.messages.length - 1].id} with client ${this.clientId}`);
168
+ }
169
+ for (const item of stream.messages) {
170
+ await eventCallback(item.id, item.message.timestamp, {
171
+ path: item.message.path,
172
+ payload: JSON.parse(item.message.body),
173
+ });
174
+ this.lastProcessedMessageId = item.id;
175
+ this.events.emit('msgReceived', { id: item.id });
176
+ await this.client
177
+ .multi()
178
+ // Acknowledge the message so that it is removed from the server's Pending Entries List (PEL)
179
+ .xAck(streamKey, StacksMessageStream.GROUP_NAME, item.id)
180
+ // Delete the message from the stream so that it doesn't get reprocessed
181
+ .xDel(streamKey, item.id)
182
+ .exec();
183
+ }
184
+ }
185
+ }
186
+ }
187
+ async stop() {
188
+ this.abort.abort();
189
+ await this.streamWaiter;
190
+ await this.client.close().catch((error) => {
191
+ if (error.message?.includes('client is closed')) {
192
+ // ignore
193
+ }
194
+ else {
195
+ throw error;
196
+ }
197
+ });
198
+ }
199
+ }
200
+ exports.StacksMessageStream = StacksMessageStream;
201
+ StacksMessageStream.GROUP_NAME = 'primary_group';
202
+ StacksMessageStream.CONSUMER_NAME = 'primary_consumer';
203
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type DropMempoolTxReasonType = 'ReplaceByFee' | 'ReplaceAcrossFork' | 'TooExpensive' | 'StaleGarbageCollect' | 'Problematic';
2
+ /** Message sent when a transaction is dropped from the mempool. */
3
+ export interface DropMempoolTxMessage {
4
+ dropped_txids: string[];
5
+ reason: DropMempoolTxReasonType;
6
+ new_txid: string | null;
7
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=drop-mempool-tx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop-mempool-tx.js","sourceRoot":"","sources":["../../src/messages/drop-mempool-tx.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ import { DropMempoolTxMessage } from './drop-mempool-tx';
2
+ import { NewBlockMessage } from './new-block';
3
+ import { NewBurnBlockMessage } from './new-burn-block';
4
+ import { NewMempoolTxMessage } from './new-mempool-tx';
5
+ import { StackerDbChunksMessage } from './stackerdb-chunks';
6
+ export * from './drop-mempool-tx';
7
+ export * from './new-block';
8
+ export * from './new-burn-block';
9
+ export * from './new-mempool-tx';
10
+ export * from './stackerdb-chunks';
11
+ /**
12
+ * The path of the Stacks message as sent by the Stacks node.
13
+ */
14
+ export declare enum MessagePath {
15
+ NewBlock = "/new_block",
16
+ NewBurnBlock = "/new_burn_block",
17
+ NewMempoolTx = "/new_mempool_tx",
18
+ DropMempoolTx = "/drop_mempool_tx",
19
+ NewMicroblocks = "/new_microblocks",
20
+ StackerDbChunks = "/stackerdb_chunks",
21
+ ProposalResponse = "/proposal_response",
22
+ AttachmentsNew = "/attachments/new"
23
+ }
24
+ export type Message = {
25
+ path: MessagePath.NewBlock;
26
+ payload: NewBlockMessage;
27
+ } | {
28
+ path: MessagePath.NewBurnBlock;
29
+ payload: NewBurnBlockMessage;
30
+ } | {
31
+ path: MessagePath.NewMempoolTx;
32
+ payload: NewMempoolTxMessage;
33
+ } | {
34
+ path: MessagePath.DropMempoolTx;
35
+ payload: DropMempoolTxMessage;
36
+ } | {
37
+ path: MessagePath.StackerDbChunks;
38
+ payload: StackerDbChunksMessage;
39
+ } | {
40
+ path: MessagePath.NewMicroblocks;
41
+ payload: unknown;
42
+ } | {
43
+ path: MessagePath.ProposalResponse;
44
+ payload: unknown;
45
+ } | {
46
+ path: MessagePath.AttachmentsNew;
47
+ payload: unknown;
48
+ };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.MessagePath = void 0;
18
+ __exportStar(require("./drop-mempool-tx"), exports);
19
+ __exportStar(require("./new-block"), exports);
20
+ __exportStar(require("./new-burn-block"), exports);
21
+ __exportStar(require("./new-mempool-tx"), exports);
22
+ __exportStar(require("./stackerdb-chunks"), exports);
23
+ /**
24
+ * The path of the Stacks message as sent by the Stacks node.
25
+ */
26
+ var MessagePath;
27
+ (function (MessagePath) {
28
+ MessagePath["NewBlock"] = "/new_block";
29
+ MessagePath["NewBurnBlock"] = "/new_burn_block";
30
+ MessagePath["NewMempoolTx"] = "/new_mempool_tx";
31
+ MessagePath["DropMempoolTx"] = "/drop_mempool_tx";
32
+ MessagePath["NewMicroblocks"] = "/new_microblocks";
33
+ MessagePath["StackerDbChunks"] = "/stackerdb_chunks";
34
+ MessagePath["ProposalResponse"] = "/proposal_response";
35
+ MessagePath["AttachmentsNew"] = "/attachments/new";
36
+ })(MessagePath || (exports.MessagePath = MessagePath = {}));
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAMA,oDAAkC;AAClC,8CAA4B;AAC5B,mDAAiC;AACjC,mDAAiC;AACjC,qDAAmC;AAEnC;;GAEG;AACH,IAAY,WASX;AATD,WAAY,WAAW;IACrB,sCAAuB,CAAA;IACvB,+CAAgC,CAAA;IAChC,+CAAgC,CAAA;IAChC,iDAAkC,CAAA;IAClC,kDAAmC,CAAA;IACnC,oDAAqC,CAAA;IACrC,sDAAuC,CAAA;IACvC,kDAAmC,CAAA;AACrC,CAAC,EATW,WAAW,2BAAX,WAAW,QAStB"}
@@ -0,0 +1,357 @@
1
+ export declare enum NewBlockEventType {
2
+ Contract = "contract_event",
3
+ StxTransfer = "stx_transfer_event",
4
+ StxMint = "stx_mint_event",
5
+ StxBurn = "stx_burn_event",
6
+ StxLock = "stx_lock_event",
7
+ NftTransfer = "nft_transfer_event",
8
+ NftMint = "nft_mint_event",
9
+ NftBurn = "nft_burn_event",
10
+ FtTransfer = "ft_transfer_event",
11
+ FtMint = "ft_mint_event",
12
+ FtBurn = "ft_burn_event"
13
+ }
14
+ interface NewBlockEventBase {
15
+ /** 0x-prefix transaction hash. */
16
+ txid: string;
17
+ event_index: number;
18
+ committed: boolean;
19
+ }
20
+ export interface NewBlockContractEvent extends NewBlockEventBase {
21
+ type: NewBlockEventType.Contract;
22
+ contract_event: {
23
+ /** Fully qualified contract ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.kv-store" */
24
+ contract_identifier: string;
25
+ topic: string;
26
+ /** @deprecated Use `raw_value` instead. */
27
+ value: unknown;
28
+ /** Hex encoded Clarity value. */
29
+ raw_value: string;
30
+ };
31
+ }
32
+ export interface NewBlockStxTransferEvent extends NewBlockEventBase {
33
+ type: NewBlockEventType.StxTransfer;
34
+ stx_transfer_event: {
35
+ recipient: string;
36
+ sender: string;
37
+ amount: string;
38
+ /** Hex-encoded string. Only provided when a memo was specified in the Clarity `stx-transfer?` function (requires a Stacks 2.1 contract). */
39
+ memo?: string;
40
+ };
41
+ }
42
+ export interface NewBlockStxMintEvent extends NewBlockEventBase {
43
+ type: NewBlockEventType.StxMint;
44
+ stx_mint_event: {
45
+ recipient: string;
46
+ amount: string;
47
+ };
48
+ }
49
+ interface NewBlockStxBurnEvent extends NewBlockEventBase {
50
+ type: NewBlockEventType.StxBurn;
51
+ stx_burn_event: {
52
+ sender: string;
53
+ amount: string;
54
+ };
55
+ }
56
+ export interface NewBlockStxLockEvent extends NewBlockEventBase {
57
+ type: NewBlockEventType.StxLock;
58
+ committed: boolean;
59
+ stx_lock_event: {
60
+ /** String quoted base10 integer. */
61
+ locked_amount: string;
62
+ /** String quoted base10 integer. */
63
+ unlock_height: string;
64
+ /** STX principal associated with the locked tokens. */
65
+ locked_address: string;
66
+ /** Fully qualified contract ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.pox" or "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.pox-2" */
67
+ contract_identifier?: string;
68
+ };
69
+ }
70
+ interface NewBlockNftTransferEvent extends NewBlockEventBase {
71
+ type: NewBlockEventType.NftTransfer;
72
+ nft_transfer_event: {
73
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
74
+ asset_identifier: string;
75
+ recipient: string;
76
+ sender: string;
77
+ /** @deprecated Use `raw_value` instead. */
78
+ value: unknown;
79
+ /** Hex encoded Clarity value. */
80
+ raw_value: string;
81
+ };
82
+ }
83
+ export interface NewBlockNftMintEvent extends NewBlockEventBase {
84
+ type: NewBlockEventType.NftMint;
85
+ nft_mint_event: {
86
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
87
+ asset_identifier: string;
88
+ recipient: string;
89
+ /** @deprecated Use `raw_value` instead. */
90
+ value: unknown;
91
+ /** Hex encoded Clarity value. */
92
+ raw_value: string;
93
+ };
94
+ }
95
+ interface NewBlockNftBurnEvent extends NewBlockEventBase {
96
+ type: NewBlockEventType.NftBurn;
97
+ nft_burn_event: {
98
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
99
+ asset_identifier: string;
100
+ sender: string;
101
+ /** @deprecated Use `raw_value` instead. */
102
+ value: unknown;
103
+ /** Hex encoded Clarity value. */
104
+ raw_value: string;
105
+ };
106
+ }
107
+ interface NewBlockFtTransferEvent extends NewBlockEventBase {
108
+ type: NewBlockEventType.FtTransfer;
109
+ ft_transfer_event: {
110
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
111
+ asset_identifier: string;
112
+ recipient: string;
113
+ sender: string;
114
+ amount: string;
115
+ };
116
+ }
117
+ export interface NewBlockFtMintEvent extends NewBlockEventBase {
118
+ type: NewBlockEventType.FtMint;
119
+ ft_mint_event: {
120
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
121
+ asset_identifier: string;
122
+ recipient: string;
123
+ amount: string;
124
+ };
125
+ }
126
+ interface NewBlockFtBurnEvent extends NewBlockEventBase {
127
+ type: NewBlockEventType.FtBurn;
128
+ ft_burn_event: {
129
+ /** Fully qualified asset ID, e.g. "ST2ZRX0K27GW0SP3GJCEMHD95TQGJMKB7G9Y0X1MH.contract-name.asset-name" */
130
+ asset_identifier: string;
131
+ sender: string;
132
+ amount: string;
133
+ };
134
+ }
135
+ export interface BurnchainOpRegisterAssetNft {
136
+ register_asset: {
137
+ asset_type: 'nft';
138
+ burn_header_hash: string;
139
+ l1_contract_id: string;
140
+ l2_contract_id: string;
141
+ txid: string;
142
+ };
143
+ }
144
+ export interface BurnchainOpRegisterAssetFt {
145
+ register_asset: {
146
+ asset_type: 'ft';
147
+ burn_header_hash: string;
148
+ l1_contract_id: string;
149
+ l2_contract_id: string;
150
+ txid: string;
151
+ };
152
+ }
153
+ export interface BurnchainOpStackStx {
154
+ stack_stx: {
155
+ auth_id: number;
156
+ burn_block_height: number;
157
+ burn_header_hash: string;
158
+ burn_txid: string;
159
+ max_amount: number;
160
+ num_cycles: number;
161
+ reward_addr: string;
162
+ sender: {
163
+ address: string;
164
+ address_hash_bytes: string;
165
+ address_version: number;
166
+ };
167
+ signer_key: string;
168
+ stacked_ustx: number;
169
+ vtxindex: number;
170
+ };
171
+ }
172
+ export interface BurnchainOpDelegateStx {
173
+ delegate_stx: {
174
+ burn_block_height: number;
175
+ burn_header_hash: string;
176
+ burn_txid: string;
177
+ delegate_to: {
178
+ address: string;
179
+ address_hash_bytes: string;
180
+ address_version: number;
181
+ };
182
+ delegated_ustx: number;
183
+ reward_addr: [
184
+ number,
185
+ string
186
+ ];
187
+ sender: {
188
+ address: string;
189
+ address_hash_bytes: string;
190
+ address_version: number;
191
+ };
192
+ until_burn_height: number;
193
+ vtxindex: number;
194
+ };
195
+ }
196
+ type BurnchainOp = BurnchainOpRegisterAssetNft | BurnchainOpRegisterAssetFt | BurnchainOpStackStx | BurnchainOpDelegateStx;
197
+ export type NewBlockEvent = NewBlockContractEvent | NewBlockStxTransferEvent | NewBlockStxMintEvent | NewBlockStxBurnEvent | NewBlockStxLockEvent | NewBlockFtTransferEvent | NewBlockFtMintEvent | NewBlockFtBurnEvent | NewBlockNftTransferEvent | NewBlockNftMintEvent | NewBlockNftBurnEvent;
198
+ export type NewBlockTransactionStatus = 'success' | 'abort_by_response' | 'abort_by_post_condition';
199
+ type ClarityAbiTypeBuffer = {
200
+ buffer: {
201
+ length: number;
202
+ };
203
+ };
204
+ type ClarityAbiTypeResponse = {
205
+ response: {
206
+ ok: ClarityAbiType;
207
+ error: ClarityAbiType;
208
+ };
209
+ };
210
+ type ClarityAbiTypeOptional = {
211
+ optional: ClarityAbiType;
212
+ };
213
+ type ClarityAbiTypeTuple = {
214
+ tuple: {
215
+ name: string;
216
+ type: ClarityAbiType;
217
+ }[];
218
+ };
219
+ type ClarityAbiTypeList = {
220
+ list: {
221
+ type: ClarityAbiType;
222
+ length: number;
223
+ };
224
+ };
225
+ type ClarityAbiTypeUInt128 = 'uint128';
226
+ type ClarityAbiTypeInt128 = 'int128';
227
+ type ClarityAbiTypeBool = 'bool';
228
+ type ClarityAbiTypePrincipal = 'principal';
229
+ type ClarityAbiTypeNone = 'none';
230
+ type ClarityAbiTypePrimitive = ClarityAbiTypeUInt128 | ClarityAbiTypeInt128 | ClarityAbiTypeBool | ClarityAbiTypePrincipal | ClarityAbiTypeNone;
231
+ type ClarityAbiType = ClarityAbiTypePrimitive | ClarityAbiTypeBuffer | ClarityAbiTypeResponse | ClarityAbiTypeOptional | ClarityAbiTypeTuple | ClarityAbiTypeList;
232
+ interface ClarityAbiFunction {
233
+ name: string;
234
+ access: 'private' | 'public' | 'read_only';
235
+ args: {
236
+ name: string;
237
+ type: ClarityAbiType;
238
+ }[];
239
+ outputs: {
240
+ type: ClarityAbiType;
241
+ };
242
+ }
243
+ interface ClarityAbiVariable {
244
+ name: string;
245
+ access: 'variable' | 'constant';
246
+ type: ClarityAbiType;
247
+ }
248
+ interface ClarityAbiMap {
249
+ name: string;
250
+ key: {
251
+ name: string;
252
+ type: ClarityAbiType;
253
+ }[];
254
+ value: {
255
+ name: string;
256
+ type: ClarityAbiType;
257
+ }[];
258
+ }
259
+ interface ClarityAbiTypeFungibleToken {
260
+ name: string;
261
+ }
262
+ interface ClarityAbiTypeNonFungibleToken {
263
+ name: string;
264
+ type: ClarityAbiType;
265
+ }
266
+ export interface ClarityAbi {
267
+ functions: ClarityAbiFunction[];
268
+ variables: ClarityAbiVariable[];
269
+ maps: ClarityAbiMap[];
270
+ fungible_tokens: ClarityAbiTypeFungibleToken[];
271
+ non_fungible_tokens: ClarityAbiTypeNonFungibleToken[];
272
+ }
273
+ interface NewBlockExecutionCost {
274
+ read_count: number;
275
+ read_length: number;
276
+ runtime: number;
277
+ write_count: number;
278
+ write_length: number;
279
+ }
280
+ export interface NewBlockTransaction {
281
+ raw_tx: string;
282
+ status: NewBlockTransactionStatus;
283
+ raw_result: string;
284
+ txid: string;
285
+ tx_index: number;
286
+ contract_interface: ClarityAbi | null;
287
+ /** @deprecated Use `contract_interface` instead. The node renamed `contract_abi` to `contract_interface`. */
288
+ contract_abi?: ClarityAbi | null;
289
+ execution_cost: NewBlockExecutionCost;
290
+ microblock_sequence: number | null;
291
+ microblock_hash: string | null;
292
+ microblock_parent_hash: string | null;
293
+ vm_error?: string | null;
294
+ burnchain_op?: BurnchainOp | null;
295
+ }
296
+ export interface NewBlockMessage {
297
+ block_hash: string;
298
+ block_height: number;
299
+ burn_block_time: number;
300
+ burn_block_hash: string;
301
+ burn_block_height: number;
302
+ miner_txid: string;
303
+ index_block_hash: string;
304
+ parent_index_block_hash: string;
305
+ parent_block_hash: string;
306
+ parent_microblock: string;
307
+ parent_microblock_sequence: number;
308
+ parent_burn_block_hash: string;
309
+ parent_burn_block_height: number;
310
+ parent_burn_block_timestamp: number;
311
+ events: NewBlockEvent[];
312
+ transactions: NewBlockTransaction[];
313
+ matured_miner_rewards: {
314
+ from_index_consensus_hash: string;
315
+ from_stacks_block_hash: string;
316
+ /** STX principal */
317
+ recipient: string;
318
+ /** STX principal (available starting in Stacks 2.1) */
319
+ miner_address: string | null;
320
+ /** String quoted micro-STX amount. */
321
+ coinbase_amount: string;
322
+ /** String quoted micro-STX amount. */
323
+ tx_fees_anchored: string;
324
+ /** String quoted micro-STX amount. */
325
+ tx_fees_streamed_confirmed: string;
326
+ /** String quoted micro-STX amount. */
327
+ tx_fees_streamed_produced: string;
328
+ }[];
329
+ anchored_cost?: NewBlockExecutionCost;
330
+ confirmed_microblocks_cost?: NewBlockExecutionCost;
331
+ pox_v1_unlock_height?: number;
332
+ pox_v2_unlock_height?: number;
333
+ pox_v3_unlock_height?: number;
334
+ /** Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated */
335
+ cycle_number?: number;
336
+ /** AKA `coinbase_height`. In epoch2.x this is the same as `block_height`. In epoch3 this is used to track tenure heights. Only available starting in stacks-core 3.0.0.0.0-rc6 */
337
+ tenure_height?: number | null;
338
+ /** Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated */
339
+ reward_set?: {
340
+ pox_ustx_threshold: string;
341
+ rewarded_addresses: string[];
342
+ signers?: {
343
+ signing_key: string;
344
+ weight: number;
345
+ stacked_amt: string;
346
+ }[];
347
+ start_cycle_state: {
348
+ missed_reward_slots: [];
349
+ };
350
+ };
351
+ block_time: number | null;
352
+ signer_bitvec?: string | null;
353
+ signer_signature?: string[];
354
+ signer_signature_hash: string;
355
+ miner_signature: string;
356
+ }
357
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NewBlockEventType = void 0;
4
+ var NewBlockEventType;
5
+ (function (NewBlockEventType) {
6
+ NewBlockEventType["Contract"] = "contract_event";
7
+ NewBlockEventType["StxTransfer"] = "stx_transfer_event";
8
+ NewBlockEventType["StxMint"] = "stx_mint_event";
9
+ NewBlockEventType["StxBurn"] = "stx_burn_event";
10
+ NewBlockEventType["StxLock"] = "stx_lock_event";
11
+ NewBlockEventType["NftTransfer"] = "nft_transfer_event";
12
+ NewBlockEventType["NftMint"] = "nft_mint_event";
13
+ NewBlockEventType["NftBurn"] = "nft_burn_event";
14
+ NewBlockEventType["FtTransfer"] = "ft_transfer_event";
15
+ NewBlockEventType["FtMint"] = "ft_mint_event";
16
+ NewBlockEventType["FtBurn"] = "ft_burn_event";
17
+ })(NewBlockEventType || (exports.NewBlockEventType = NewBlockEventType = {}));
18
+ //# sourceMappingURL=new-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-block.js","sourceRoot":"","sources":["../../src/messages/new-block.ts"],"names":[],"mappings":";;;AAAA,IAAY,iBAYX;AAZD,WAAY,iBAAiB;IAC3B,gDAA2B,CAAA;IAC3B,uDAAkC,CAAA;IAClC,+CAA0B,CAAA;IAC1B,+CAA0B,CAAA;IAC1B,+CAA0B,CAAA;IAC1B,uDAAkC,CAAA;IAClC,+CAA0B,CAAA;IAC1B,+CAA0B,CAAA;IAC1B,qDAAgC,CAAA;IAChC,6CAAwB,CAAA;IACxB,6CAAwB,CAAA;AAC1B,CAAC,EAZW,iBAAiB,iCAAjB,iBAAiB,QAY5B"}
@@ -0,0 +1,20 @@
1
+ export interface NewBurnBlockMessage {
2
+ burn_block_hash: string;
3
+ burn_block_height: number;
4
+ /** Amount in BTC satoshis. */
5
+ burn_amount: number;
6
+ reward_recipients: [
7
+ {
8
+ /** Bitcoin address (b58 encoded). */
9
+ recipient: string;
10
+ /** Amount in BTC satoshis. */
11
+ amt: number;
12
+ }
13
+ ];
14
+ /**
15
+ * Array of the Bitcoin addresses that would validly receive PoX commitments during this block.
16
+ * These addresses may not actually receive rewards during this block if the block is faster
17
+ * than miners have an opportunity to commit.
18
+ */
19
+ reward_slot_holders: string[];
20
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=new-burn-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-burn-block.js","sourceRoot":"","sources":["../../src/messages/new-burn-block.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ /** Array of raw hex encoded mempool transactions. */
2
+ export type NewMempoolTxMessage = string[];
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=new-mempool-tx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-mempool-tx.js","sourceRoot":"","sources":["../../src/messages/new-mempool-tx.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ export interface StackerDbChunksModifiedSlot {
2
+ /** Slot identifier (unique for each DB instance) */
3
+ slot_id: number;
4
+ /** Slot version (a lamport clock) */
5
+ slot_version: number;
6
+ /** Chunk data (use the sha512_256 hashed of this for generating a signature) */
7
+ data: string;
8
+ /** signature over the above */
9
+ sig: string;
10
+ }
11
+ export interface StackerDbChunksMessage {
12
+ contract_id: {
13
+ issuer: [number, number[]];
14
+ name: string;
15
+ };
16
+ modified_slots: StackerDbChunksModifiedSlot[];
17
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=stackerdb-chunks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackerdb-chunks.js","sourceRoot":"","sources":["../../src/messages/stackerdb-chunks.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@stacks/node-publisher-client",
3
+ "version": "0.2.0",
4
+ "description": "A client to consume Stacks events from the Stacks Node Publisher service",
5
+ "main": "./dist/index.js",
6
+ "typings": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "rimraf ./dist && tsc --project tsconfig.build.json",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "files": [
12
+ "dist/"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/stx-labs/stacks-node-publisher.git",
17
+ "directory": "client"
18
+ },
19
+ "keywords": [
20
+ "stacks",
21
+ "node-publisher",
22
+ "client"
23
+ ],
24
+ "author": "Stacks Labs",
25
+ "license": "GPL-3.0-only",
26
+ "devDependencies": {
27
+ "rimraf": "^6.0.1",
28
+ "typescript": "^5.7.2"
29
+ },
30
+ "dependencies": {
31
+ "@hirosystems/api-toolkit": "^1.12.0",
32
+ "redis": "^5.10.0"
33
+ }
34
+ }