soroban-events 2.0.2 → 3.0.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 +427 -140
- package/package.json +5 -3
- package/src/backfill.js +1 -1
- package/src/cli.js +277 -0
- package/src/dead-letter.js +163 -0
- package/src/engine.js +23 -3
- package/src/event-filter.js +172 -0
- package/src/event-query.js +194 -0
- package/src/export.js +158 -0
- package/src/handoff.js +17 -0
- package/src/index.d.ts +434 -360
- package/src/index.js +5 -0
- package/src/live-backfill.js +1 -1
- package/src/replay.js +26 -3
- package/src/streamer.js +335 -33
package/README.md
CHANGED
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
# soroban-events
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Resilient, windowed event streaming and XDR decoding for Soroban RPC.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`soroban-events` provides production-oriented primitives for retrieving, decoding, processing, storing, replaying, and exporting Soroban contract events.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Event deduplication
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
- Safe windowed retrieval for ledger ranges larger than the Soroban RPC range limit.
|
|
10
|
+
- Cursor-based pagination.
|
|
11
|
+
- Event-ID deduplication.
|
|
12
|
+
- Unlimited internal pagination with `limit: null`.
|
|
13
|
+
- Reliable ledger checkpointing.
|
|
14
|
+
- Durable file checkpoints.
|
|
15
|
+
- Memory and SQLite event stores.
|
|
16
|
+
- Backfill and historical replay.
|
|
17
|
+
- Backfill/live handoff protection.
|
|
18
|
+
- Reorg detection and recovery.
|
|
19
|
+
- RPC health and ledger-retention inspection.
|
|
20
|
+
- Configurable RPC failover.
|
|
21
|
+
- Circuit-breaker protection.
|
|
22
|
+
- Adaptive rate-limit handling.
|
|
23
|
+
- Exponential retry with jitter.
|
|
24
|
+
- Event filtering and custom predicates.
|
|
25
|
+
- Deterministic event querying.
|
|
26
|
+
- Event processing pipelines.
|
|
27
|
+
- JSONL and CSV export.
|
|
28
|
+
- Streaming export.
|
|
29
|
+
- Optional dead-letter queue primitives.
|
|
30
|
+
- CLI commands for status, backfill, replay, and health inspection.
|
|
31
|
+
- TypeScript declarations.
|
|
32
|
+
- Packed npm-consumer verification.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
Node.js 22.12.0 or newer.
|
|
37
|
+
|
|
38
|
+
The runtime requirement follows the current Stellar SDK dependency requirements.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
21
41
|
|
|
22
42
|
```bash
|
|
23
43
|
npm install soroban-events
|
|
24
44
|
```
|
|
25
45
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Quick start
|
|
46
|
+
## Basic usage
|
|
29
47
|
|
|
30
48
|
```js
|
|
31
|
-
import { SorobanEventStreamer } from
|
|
49
|
+
import { SorobanEventStreamer } from "soroban-events";
|
|
32
50
|
|
|
33
51
|
const streamer = new SorobanEventStreamer(
|
|
34
|
-
|
|
52
|
+
"https://soroban-testnet.stellar.org"
|
|
35
53
|
);
|
|
36
54
|
|
|
37
|
-
const latest = await streamer.getLatestLedger();
|
|
38
|
-
|
|
39
55
|
const events = await streamer.getEventsWindowed({
|
|
40
|
-
startLedger:
|
|
41
|
-
endLedger:
|
|
42
|
-
|
|
43
|
-
limit: 10
|
|
56
|
+
startLedger: 1000000,
|
|
57
|
+
endLedger: 1010000,
|
|
58
|
+
limit: null
|
|
44
59
|
});
|
|
45
60
|
|
|
46
61
|
for (const event of events) {
|
|
@@ -48,183 +63,455 @@ for (const event of events) {
|
|
|
48
63
|
}
|
|
49
64
|
```
|
|
50
65
|
|
|
51
|
-
##
|
|
66
|
+
## Latest ledger
|
|
52
67
|
|
|
53
68
|
```js
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
const latest = await streamer.getLatestLedger();
|
|
70
|
+
|
|
71
|
+
console.log(latest);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Metadata can be requested when ledger information beyond the sequence is needed:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const latest = await streamer.getLatestLedger({
|
|
78
|
+
metadata: true
|
|
64
79
|
});
|
|
80
|
+
|
|
81
|
+
console.log(latest.sequence);
|
|
82
|
+
console.log(latest.hash);
|
|
65
83
|
```
|
|
66
84
|
|
|
67
|
-
##
|
|
85
|
+
## Large event ranges
|
|
86
|
+
|
|
87
|
+
Soroban RPC limits individual ledger-range queries. `soroban-events` automatically divides large ranges into safe windows.
|
|
88
|
+
|
|
89
|
+
Internal consumers such as backfill and consume use unlimited pagination so events are not silently truncated by a default event limit.
|
|
90
|
+
|
|
91
|
+
Public window retrieval accepts either a finite limit or `null` for unlimited retrieval:
|
|
68
92
|
|
|
69
93
|
```js
|
|
70
|
-
const events = await streamer.
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const events = await streamer.getEventsWindowed({
|
|
95
|
+
startLedger: 1000000,
|
|
96
|
+
endLedger: 1200000,
|
|
97
|
+
limit: null
|
|
73
98
|
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Backfill
|
|
74
102
|
|
|
75
|
-
|
|
103
|
+
```js
|
|
104
|
+
import { backfill } from "soroban-events";
|
|
105
|
+
|
|
106
|
+
await backfill(streamer, {
|
|
107
|
+
startLedger: 1000000,
|
|
108
|
+
endLedger: 1100000,
|
|
109
|
+
onEvent: async (event) => {
|
|
110
|
+
console.log(event);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
76
113
|
```
|
|
77
114
|
|
|
78
|
-
|
|
115
|
+
Backfill supports checkpointing, event stores, pipelines, progress reporting, and concurrent window processing.
|
|
116
|
+
|
|
117
|
+
## Replay
|
|
79
118
|
|
|
80
119
|
```js
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
120
|
+
import { EventReplay } from "soroban-events";
|
|
121
|
+
|
|
122
|
+
const replay = new EventReplay(streamer);
|
|
123
|
+
|
|
124
|
+
await replay.run({
|
|
125
|
+
startLedger: 1000000,
|
|
126
|
+
endLedger: 1010000,
|
|
127
|
+
onEvent: async (event) => {
|
|
128
|
+
console.log(event);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
92
131
|
```
|
|
93
132
|
|
|
94
|
-
|
|
133
|
+
Replay supports checkpoint recovery, pipelines, persistence, and abort signals.
|
|
134
|
+
|
|
135
|
+
## Streaming
|
|
95
136
|
|
|
96
137
|
```js
|
|
97
138
|
const controller = new AbortController();
|
|
98
139
|
|
|
99
|
-
setTimeout(() => controller.abort(), 30_000);
|
|
100
|
-
|
|
101
140
|
for await (const event of streamer.stream({
|
|
102
|
-
startLedger:
|
|
141
|
+
startLedger: 1000000,
|
|
103
142
|
signal: controller.signal
|
|
104
143
|
})) {
|
|
105
144
|
console.log(event);
|
|
106
145
|
}
|
|
146
|
+
|
|
147
|
+
controller.abort();
|
|
107
148
|
```
|
|
108
149
|
|
|
109
|
-
|
|
150
|
+
Stream checkpointing does not advance past a ledger until all events from that ledger have been successfully consumed.
|
|
110
151
|
|
|
111
|
-
|
|
152
|
+
## Checkpointing
|
|
153
|
+
|
|
154
|
+
Checkpoint stores use the raw store contract:
|
|
112
155
|
|
|
113
156
|
```js
|
|
114
|
-
|
|
157
|
+
await store.save(key, ledger);
|
|
158
|
+
const ledger = await store.load(key);
|
|
159
|
+
await store.clear(key);
|
|
160
|
+
```
|
|
115
161
|
|
|
116
|
-
|
|
117
|
-
|
|
162
|
+
For monotonic checkpoint management and explicit rewinds, use `CheckpointManager`:
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
const manager = new CheckpointManager(store);
|
|
166
|
+
|
|
167
|
+
await manager.save(ledger, "my-consumer");
|
|
168
|
+
|
|
169
|
+
const resumeLedger = await manager.resumeFrom(
|
|
170
|
+
"my-consumer",
|
|
171
|
+
startLedger
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
await manager.rewind(
|
|
175
|
+
rewindLedger,
|
|
176
|
+
"my-consumer"
|
|
177
|
+
);
|
|
118
178
|
```
|
|
119
179
|
|
|
120
|
-
|
|
180
|
+
Normal checkpoint saves cannot move backwards. Explicit rewinds are used for recovery workflows.
|
|
121
181
|
|
|
122
|
-
|
|
182
|
+
## File checkpoints
|
|
123
183
|
|
|
124
|
-
|
|
184
|
+
```js
|
|
185
|
+
import {
|
|
186
|
+
FileCheckpointStore,
|
|
187
|
+
CheckpointManager
|
|
188
|
+
} from "soroban-events";
|
|
125
189
|
|
|
126
|
-
|
|
190
|
+
const store = new FileCheckpointStore("./checkpoints");
|
|
191
|
+
const checkpoints = new CheckpointManager(store);
|
|
192
|
+
|
|
193
|
+
await checkpoints.save(
|
|
194
|
+
1000000,
|
|
195
|
+
"my-consumer"
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const resumeLedger = await checkpoints.resumeFrom(
|
|
199
|
+
"my-consumer",
|
|
200
|
+
1
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
File checkpoints use atomic replacement so a partially written checkpoint does not replace the last valid checkpoint.
|
|
205
|
+
|
|
206
|
+
## Event filtering
|
|
127
207
|
|
|
128
208
|
```js
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
txHash,
|
|
138
|
-
topics,
|
|
139
|
-
value,
|
|
140
|
-
inSuccessfulContractCall
|
|
141
|
-
}
|
|
209
|
+
import { createEventFilter } from "soroban-events";
|
|
210
|
+
|
|
211
|
+
const filter = createEventFilter({
|
|
212
|
+
contractId: "C...",
|
|
213
|
+
type: "transfer"
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const matching = events.filter(filter);
|
|
142
217
|
```
|
|
143
218
|
|
|
144
|
-
|
|
219
|
+
Filters can match:
|
|
220
|
+
|
|
221
|
+
- contract ID
|
|
222
|
+
- event type
|
|
223
|
+
- topics
|
|
224
|
+
- transaction hash
|
|
225
|
+
- exact ledger
|
|
226
|
+
- ledger ranges
|
|
227
|
+
- multiple contracts
|
|
228
|
+
- custom predicates
|
|
229
|
+
|
|
230
|
+
## Event querying
|
|
145
231
|
|
|
146
232
|
```js
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
233
|
+
import { createEventQuery } from "soroban-events";
|
|
234
|
+
|
|
235
|
+
const query = createEventQuery(store);
|
|
236
|
+
|
|
237
|
+
const results = await query({
|
|
238
|
+
contractId: "C...",
|
|
239
|
+
startLedger: 1000000,
|
|
240
|
+
endLedger: 1010000,
|
|
241
|
+
limit: 100
|
|
153
242
|
});
|
|
154
243
|
```
|
|
155
244
|
|
|
156
|
-
|
|
157
|
-
|---|---:|---|
|
|
158
|
-
| `pollInterval` | `3000` | Polling interval for `stream()` |
|
|
159
|
-
| `windowSize` | `9500` | Maximum ledger window |
|
|
160
|
-
| `pageSize` | `1000` | RPC page size |
|
|
161
|
-
| `maxRetries` | `3` | Maximum retry attempts |
|
|
162
|
-
| `retryBaseMs` | `500` | Base retry delay |
|
|
163
|
-
|
|
164
|
-
## How it works
|
|
165
|
-
|
|
166
|
-
```text
|
|
167
|
-
Soroban RPC
|
|
168
|
-
|
|
|
169
|
-
v
|
|
170
|
-
ledger windows
|
|
171
|
-
|
|
|
172
|
-
v
|
|
173
|
-
cursor pagination
|
|
174
|
-
|
|
|
175
|
-
v
|
|
176
|
-
retry handling
|
|
177
|
-
|
|
|
178
|
-
v
|
|
179
|
-
deduplication
|
|
180
|
-
|
|
|
181
|
-
v
|
|
182
|
-
ScVal decoding
|
|
183
|
-
|
|
|
184
|
-
v
|
|
185
|
-
your application
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
No database is required.
|
|
189
|
-
|
|
190
|
-
No full indexer stack is required.
|
|
191
|
-
|
|
192
|
-
Use the normalized events in whatever application or storage layer you need.
|
|
245
|
+
Queries provide deterministic ordering and pagination.
|
|
193
246
|
|
|
194
|
-
##
|
|
247
|
+
## Event storage
|
|
248
|
+
|
|
249
|
+
The package provides memory and SQLite event stores.
|
|
250
|
+
|
|
251
|
+
Memory storage:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
import { MemoryEventStore } from "soroban-events";
|
|
255
|
+
|
|
256
|
+
const store = new MemoryEventStore();
|
|
257
|
+
|
|
258
|
+
await store.put(event);
|
|
259
|
+
|
|
260
|
+
const events = await store.query({
|
|
261
|
+
startLedger: 1000000,
|
|
262
|
+
endLedger: 1010000
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
SQLite storage:
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
import { SqliteEventStore } from "soroban-events";
|
|
270
|
+
|
|
271
|
+
const store = new SqliteEventStore("./events.db");
|
|
272
|
+
|
|
273
|
+
await store.put(event);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
SQLite uses Node's built-in SQLite support and therefore requires a supported Node.js runtime.
|
|
277
|
+
|
|
278
|
+
## Event pipelines
|
|
279
|
+
|
|
280
|
+
Pipelines can filter, transform, observe, batch, and handle event-processing failures.
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
import { EventPipeline } from "soroban-events";
|
|
284
|
+
|
|
285
|
+
const pipeline = new EventPipeline()
|
|
286
|
+
.filter((event) => event.type === "transfer")
|
|
287
|
+
.map((event) => ({
|
|
288
|
+
...event,
|
|
289
|
+
processed: true
|
|
290
|
+
}))
|
|
291
|
+
.tap((event) => {
|
|
292
|
+
console.log("processed", event.id);
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## RPC health
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
const health = await streamer.getHealth();
|
|
300
|
+
|
|
301
|
+
console.log(health.status);
|
|
302
|
+
console.log(health.latestLedger);
|
|
303
|
+
console.log(health.oldestLedger);
|
|
304
|
+
console.log(health.ledgerRetentionWindow);
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Retention can be checked directly:
|
|
195
308
|
|
|
196
|
-
|
|
309
|
+
```js
|
|
310
|
+
const result = await streamer.checkRetention(1000000);
|
|
311
|
+
|
|
312
|
+
console.log(result.retained);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## RPC failover
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
const streamer = new SorobanEventStreamer(
|
|
319
|
+
"https://primary-rpc.example",
|
|
320
|
+
{
|
|
321
|
+
failoverRpcUrls: [
|
|
322
|
+
"https://secondary-rpc.example",
|
|
323
|
+
"https://third-rpc.example"
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The streamer can rotate through configured RPC endpoints when transient failures exhaust retry attempts.
|
|
330
|
+
|
|
331
|
+
## Circuit breaker
|
|
332
|
+
|
|
333
|
+
Circuit-breaker behavior can be configured:
|
|
334
|
+
|
|
335
|
+
```js
|
|
336
|
+
const streamer = new SorobanEventStreamer(
|
|
337
|
+
rpcUrl,
|
|
338
|
+
{
|
|
339
|
+
circuitBreakerThreshold: 3,
|
|
340
|
+
circuitBreakerCooldownMs: 30000
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The circuit protects the consumer from repeatedly hammering an unavailable RPC endpoint.
|
|
346
|
+
|
|
347
|
+
## Adaptive retry
|
|
348
|
+
|
|
349
|
+
Retry behavior supports exponential backoff, rate-limit adaptation, maximum delay, and jitter:
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
const streamer = new SorobanEventStreamer(
|
|
353
|
+
rpcUrl,
|
|
354
|
+
{
|
|
355
|
+
maxRetries: 3,
|
|
356
|
+
retryBaseMs: 500,
|
|
357
|
+
retryMaxMs: 30000,
|
|
358
|
+
retryJitter: 0.2,
|
|
359
|
+
adaptiveRateLimit: true
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Reorg recovery
|
|
365
|
+
|
|
366
|
+
The package provides ledger consistency tracking and reorg recovery primitives.
|
|
367
|
+
|
|
368
|
+
Recovery can:
|
|
369
|
+
|
|
370
|
+
- detect ledger hash mismatches;
|
|
371
|
+
- determine an affected rewind point;
|
|
372
|
+
- rewind checkpoints;
|
|
373
|
+
- invalidate affected stored events;
|
|
374
|
+
- restore the backfill/live handoff boundary.
|
|
375
|
+
|
|
376
|
+
## Event export
|
|
377
|
+
|
|
378
|
+
JSONL:
|
|
379
|
+
|
|
380
|
+
```js
|
|
381
|
+
import { exportEvents } from "soroban-events";
|
|
382
|
+
|
|
383
|
+
const jsonl = exportEvents(events, {
|
|
384
|
+
format: "jsonl"
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
CSV:
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
const csv = exportEvents(events, {
|
|
392
|
+
format: "csv"
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Streaming export is also supported for large event collections.
|
|
397
|
+
|
|
398
|
+
## CLI
|
|
399
|
+
|
|
400
|
+
Check RPC status:
|
|
197
401
|
|
|
198
402
|
```bash
|
|
199
|
-
|
|
403
|
+
npx soroban-events status --rpc <RPC_URL>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Backfill a ledger range:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
npx soroban-events backfill --rpc <RPC_URL> --start <LEDGER> --end <LEDGER>
|
|
200
410
|
```
|
|
201
411
|
|
|
202
|
-
|
|
412
|
+
Replay a ledger range:
|
|
203
413
|
|
|
204
414
|
```bash
|
|
205
|
-
|
|
415
|
+
npx soroban-events replay --rpc <RPC_URL> --start <LEDGER> --end <LEDGER>
|
|
206
416
|
```
|
|
207
417
|
|
|
208
|
-
|
|
418
|
+
Health information:
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
npx soroban-events health --rpc <RPC_URL>
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
The CLI supports JSONL and CSV export options where applicable.
|
|
425
|
+
|
|
426
|
+
## TypeScript
|
|
427
|
+
|
|
428
|
+
TypeScript declarations are included with the package.
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
import {
|
|
432
|
+
SorobanEventStreamer,
|
|
433
|
+
CheckpointManager,
|
|
434
|
+
MemoryCheckpointStore
|
|
435
|
+
} from "soroban-events";
|
|
436
|
+
|
|
437
|
+
const store = new MemoryCheckpointStore();
|
|
438
|
+
const checkpoints = new CheckpointManager(store);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## Reliability guarantees
|
|
442
|
+
|
|
443
|
+
The library is designed around several important ingestion properties.
|
|
444
|
+
|
|
445
|
+
### Pagination safety
|
|
446
|
+
|
|
447
|
+
Large RPC ranges are split into safe ledger windows and paginated using RPC cursors.
|
|
448
|
+
|
|
449
|
+
### Event deduplication
|
|
450
|
+
|
|
451
|
+
Event IDs are deduplicated across pagination and processing boundaries.
|
|
209
452
|
|
|
210
|
-
|
|
453
|
+
### Checkpoint safety
|
|
211
454
|
|
|
212
|
-
|
|
455
|
+
Consumer checkpoints are advanced only after the relevant ledger has been completely processed.
|
|
213
456
|
|
|
214
|
-
|
|
457
|
+
### Restart recovery
|
|
215
458
|
|
|
216
|
-
|
|
459
|
+
A failed consumer can resume from its last durable checkpoint and reprocess the uncommitted ledger.
|
|
460
|
+
|
|
461
|
+
### Backfill/live handoff
|
|
462
|
+
|
|
463
|
+
The handoff layer protects the transition between historical backfill and live consumption from duplicate delivery.
|
|
464
|
+
|
|
465
|
+
### Reorg handling
|
|
466
|
+
|
|
467
|
+
Ledger consistency checks can detect hash mismatches and trigger controlled checkpoint rewind and storage invalidation.
|
|
468
|
+
|
|
469
|
+
### RPC resilience
|
|
470
|
+
|
|
471
|
+
Transient failures, rate limits, endpoint failures, and unavailable RPC services can be handled through retry, adaptive backoff, failover, and circuit-breaking mechanisms.
|
|
472
|
+
|
|
473
|
+
## Testing
|
|
474
|
+
|
|
475
|
+
Run the complete test suite:
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
npm test
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Run the packed-consumer test:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
npm run test:pack
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Run the release verification suite:
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
npm run test:release
|
|
491
|
+
```
|
|
217
492
|
|
|
218
|
-
|
|
493
|
+
The release verification includes:
|
|
219
494
|
|
|
220
|
-
|
|
495
|
+
- complete test suite;
|
|
496
|
+
- packed npm consumer verification;
|
|
497
|
+
- npm pack dry-run;
|
|
498
|
+
- Git whitespace validation.
|
|
221
499
|
|
|
222
|
-
##
|
|
500
|
+
## v3.0.0
|
|
223
501
|
|
|
224
|
-
|
|
502
|
+
Version 3 is a major compatibility boundary for the expanded public API.
|
|
225
503
|
|
|
226
|
-
|
|
504
|
+
Before upgrading, review:
|
|
227
505
|
|
|
228
|
-
|
|
506
|
+
- checkpoint store method signatures;
|
|
507
|
+
- `CheckpointManager` usage;
|
|
508
|
+
- streamer `limit` behavior;
|
|
509
|
+
- event filtering and querying;
|
|
510
|
+
- event storage APIs;
|
|
511
|
+
- export APIs;
|
|
512
|
+
- pipeline APIs;
|
|
513
|
+
- RPC failover and retry configuration;
|
|
514
|
+
- TypeScript declarations;
|
|
515
|
+
- Node.js 22.12.0 or newer requirement.
|
|
229
516
|
|
|
230
|
-
|
|
517
|
+
The v3.0.0 release is currently unreleased.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "soroban-events",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Resilient, windowed event streamer and XDR decoder for Soroban RPC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"cli": "node src/cli.js",
|
|
23
23
|
"test:unit": "node --test tests/*.test.js",
|
|
24
24
|
"audit": "node scripts/audit.mjs",
|
|
25
|
-
"pack:audit": "node scripts/pack-audit.mjs"
|
|
25
|
+
"pack:audit": "node scripts/pack-audit.mjs",
|
|
26
|
+
"test:pack": "node --test tests/packed-consumer.test.js",
|
|
27
|
+
"test:release": "npm test && npm run test:pack && npm pack --dry-run && git diff --check"
|
|
26
28
|
},
|
|
27
29
|
"keywords": [
|
|
28
30
|
"soroban",
|
|
@@ -39,7 +41,7 @@
|
|
|
39
41
|
],
|
|
40
42
|
"license": "MIT",
|
|
41
43
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
44
|
+
"node": ">=22.12.0"
|
|
43
45
|
},
|
|
44
46
|
"dependencies": {
|
|
45
47
|
"@stellar/stellar-sdk": "^17.1.0"
|