@thru/replay 0.1.36 → 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/dist/index.cjs CHANGED
@@ -17,6 +17,12 @@ var ChainClient = class {
17
17
  query;
18
18
  streaming;
19
19
  callOptions;
20
+ getAccount(request) {
21
+ return this.query.getAccount(protobuf.create(proto.GetAccountRequestSchema, request), this.callOptions);
22
+ }
23
+ listAccounts(request) {
24
+ return this.query.listAccounts(protobuf.create(proto.ListAccountsRequestSchema, request), this.callOptions);
25
+ }
20
26
  listBlocks(request) {
21
27
  return this.query.listBlocks(protobuf.create(proto.ListBlocksRequestSchema, request), this.callOptions);
22
28
  }
@@ -303,18 +309,52 @@ var LivePump = class {
303
309
  }
304
310
  };
305
311
 
312
+ // src/retry.ts
313
+ var DEFAULT_RETRY_CONFIG = {
314
+ initialDelayMs: 1e3,
315
+ maxDelayMs: 3e4,
316
+ connectionTimeoutMs: 3e4
317
+ };
318
+ function calculateBackoff(attempt, config) {
319
+ const delay2 = config.initialDelayMs * Math.pow(2, attempt);
320
+ return Math.min(delay2, config.maxDelayMs);
321
+ }
322
+ function withTimeout(promise, timeoutMs) {
323
+ return new Promise((resolve, reject) => {
324
+ const timer = setTimeout(() => {
325
+ reject(new TimeoutError(`Operation timed out after ${timeoutMs}ms`));
326
+ }, timeoutMs);
327
+ promise.then((value) => {
328
+ clearTimeout(timer);
329
+ resolve(value);
330
+ }).catch((err) => {
331
+ clearTimeout(timer);
332
+ reject(err);
333
+ });
334
+ });
335
+ }
336
+ var TimeoutError = class extends Error {
337
+ constructor(message) {
338
+ super(message);
339
+ this.name = "TimeoutError";
340
+ }
341
+ };
342
+ function delay(ms) {
343
+ return new Promise((resolve) => setTimeout(resolve, ms));
344
+ }
345
+
306
346
  // src/replay-stream.ts
307
347
  var DEFAULT_METRICS = {
308
348
  bufferedItems: 0,
309
349
  emittedBackfill: 0,
310
350
  emittedLive: 0,
351
+ emittedReconnect: 0,
311
352
  discardedDuplicates: 0
312
353
  };
313
354
  function compareBigint(a, b) {
314
355
  if (a === b) return 0;
315
356
  return a < b ? -1 : 1;
316
357
  }
317
- var RETRY_DELAY_MS = 1e3;
318
358
  var ReplayStream = class {
319
359
  config;
320
360
  logger;
@@ -337,12 +377,15 @@ var ReplayStream = class {
337
377
  extractSlot,
338
378
  extractKey,
339
379
  safetyMargin,
340
- resubscribeOnEnd
380
+ resubscribeOnEnd,
381
+ onReconnect
341
382
  } = this.config;
342
383
  const shouldResubscribeOnEnd = resubscribeOnEnd ?? true;
343
384
  const keyOf = extractKey ?? ((item) => extractSlot(item).toString());
385
+ let currentSubscribeLive = subscribeLive;
386
+ let currentFetchBackfill = fetchBackfill;
344
387
  const createLivePump = (slot, startStreaming = false, emitFloor) => new LivePump({
345
- source: subscribeLive(slot),
388
+ source: currentSubscribeLive(slot),
346
389
  slotOf: extractSlot,
347
390
  keyOf,
348
391
  logger: this.logger,
@@ -428,9 +471,15 @@ var ReplayStream = class {
428
471
  }
429
472
  if (!drained.length) livePump.updateEmitFloor(currentSlot);
430
473
  this.logger.info("replay entering STREAMING state");
474
+ const retryConfig = DEFAULT_RETRY_CONFIG;
475
+ let retryAttempt = 0;
431
476
  while (true) {
432
477
  try {
433
- const next = await livePump.next();
478
+ const next = await withTimeout(
479
+ livePump.next(),
480
+ retryConfig.connectionTimeoutMs
481
+ );
482
+ retryAttempt = 0;
434
483
  if (next.done) {
435
484
  if (!shouldResubscribeOnEnd) break;
436
485
  throw new Error("stream ended");
@@ -448,20 +497,91 @@ var ReplayStream = class {
448
497
  livePump.updateEmitFloor(currentSlot);
449
498
  } catch (err) {
450
499
  const errMsg = err instanceof Error ? err.message : String(err);
500
+ const backoffMs = calculateBackoff(retryAttempt, retryConfig);
451
501
  this.logger.warn(
452
- `live stream disconnected (${errMsg}); reconnecting in ${RETRY_DELAY_MS}ms from slot ${currentSlot}`
502
+ `live stream disconnected (${errMsg}); reconnecting in ${backoffMs}ms from slot ${currentSlot} (attempt ${retryAttempt + 1})`
453
503
  );
454
- await delay(RETRY_DELAY_MS);
504
+ await delay(backoffMs);
455
505
  await safeClose(livePump);
506
+ retryAttempt++;
507
+ if (onReconnect) {
508
+ try {
509
+ const fresh = onReconnect();
510
+ currentSubscribeLive = fresh.subscribeLive;
511
+ if (fresh.fetchBackfill) {
512
+ currentFetchBackfill = fresh.fetchBackfill;
513
+ }
514
+ this.logger.info("created fresh client for reconnection");
515
+ } catch (factoryErr) {
516
+ this.logger.error(
517
+ `failed to create fresh client: ${factoryErr instanceof Error ? factoryErr.message : String(factoryErr)}; using existing`
518
+ );
519
+ }
520
+ }
521
+ if (onReconnect && currentSlot > 0n) {
522
+ for await (const item of this.miniBackfill(
523
+ currentSlot,
524
+ currentFetchBackfill,
525
+ extractSlot,
526
+ keyOf,
527
+ seenItem,
528
+ recordEmission
529
+ )) {
530
+ const itemSlot = extractSlot(item);
531
+ if (itemSlot > currentSlot) {
532
+ currentSlot = itemSlot;
533
+ }
534
+ yield item;
535
+ }
536
+ }
456
537
  const resumeSlot = currentSlot > 0n ? currentSlot : 0n;
457
538
  livePump = createLivePump(resumeSlot, true, currentSlot);
458
539
  }
459
540
  }
460
541
  }
542
+ /**
543
+ * Perform mini-backfill from lastProcessedSlot to catch up after reconnection.
544
+ * Ensures no data gaps from events that occurred during disconnection.
545
+ */
546
+ async *miniBackfill(fromSlot, fetchBackfill, extractSlot, keyOf, seenItem, recordEmission) {
547
+ this.logger.info(`mini-backfill starting from slot ${fromSlot}`);
548
+ const MINI_BACKFILL_TIMEOUT = 3e4;
549
+ const startTime = Date.now();
550
+ let cursor;
551
+ let itemsYielded = 0;
552
+ try {
553
+ while (true) {
554
+ if (Date.now() - startTime > MINI_BACKFILL_TIMEOUT) {
555
+ this.logger.warn(`mini-backfill timed out after ${MINI_BACKFILL_TIMEOUT}ms`);
556
+ break;
557
+ }
558
+ const page = await fetchBackfill({ startSlot: fromSlot, cursor });
559
+ const sorted = [...page.items].sort(
560
+ (a, b) => compareBigint(extractSlot(a), extractSlot(b))
561
+ );
562
+ for (const item of sorted) {
563
+ const slot = extractSlot(item);
564
+ const key = keyOf(item);
565
+ if (seenItem(slot, key)) {
566
+ this.metrics.discardedDuplicates += 1;
567
+ continue;
568
+ }
569
+ recordEmission(slot, key);
570
+ itemsYielded++;
571
+ this.metrics.emittedReconnect += 1;
572
+ yield item;
573
+ }
574
+ cursor = page.cursor;
575
+ if (page.done || cursor === void 0) break;
576
+ }
577
+ this.logger.info(`mini-backfill complete: ${itemsYielded} items yielded`);
578
+ } catch (err) {
579
+ this.logger.warn(
580
+ `mini-backfill failed: ${err instanceof Error ? err.message : String(err)}; proceeding with live stream`
581
+ );
582
+ }
583
+ }
461
584
  };
462
- function delay(ms) {
463
- return new Promise((resolve) => setTimeout(resolve, ms));
464
- }
465
585
  async function safeClose(pump) {
466
586
  try {
467
587
  await pump.close();
@@ -623,12 +743,26 @@ function bytesToHex(bytes) {
623
743
  for (const byte of bytes) hex += byte.toString(16).padStart(2, "0");
624
744
  return hex;
625
745
  }
746
+
747
+ // src/types.ts
748
+ function resolveClient(opts, optionsName) {
749
+ if (opts.clientFactory) {
750
+ return opts.clientFactory();
751
+ }
752
+ if (!opts.client) {
753
+ throw new Error(`${optionsName} requires either client or clientFactory`);
754
+ }
755
+ return opts.client;
756
+ }
757
+
758
+ // src/replay/event-replay.ts
626
759
  var DEFAULT_PAGE_SIZE3 = 512;
627
760
  var DEFAULT_SAFETY_MARGIN3 = 64n;
628
761
  var PAGE_ORDER_ASC3 = "slot asc";
629
762
  function createEventReplay(options) {
630
763
  const safetyMargin = options.safetyMargin ?? DEFAULT_SAFETY_MARGIN3;
631
- const fetchBackfill = async ({
764
+ let currentClient = resolveClient(options, "EventReplayOptions");
765
+ const createFetchBackfill = (client) => async ({
632
766
  startSlot,
633
767
  cursor
634
768
  }) => {
@@ -639,7 +773,7 @@ function createEventReplay(options) {
639
773
  });
640
774
  const baseFilter = slotLiteralFilter("event.slot", startSlot);
641
775
  const mergedFilter = combineFilters(baseFilter, options.filter);
642
- const response = await options.client.listEvents(
776
+ const response = await client.listEvents(
643
777
  protobuf.create(proto.ListEventsRequestSchema, {
644
778
  filter: mergedFilter,
645
779
  page
@@ -647,25 +781,33 @@ function createEventReplay(options) {
647
781
  );
648
782
  return backfillPage(response.events, response.page);
649
783
  };
650
- const subscribeLive = (startSlot) => {
784
+ const createSubscribeLive = (client) => (startSlot) => {
651
785
  const mergedFilter = combineFilters(slotLiteralFilter("event.slot", startSlot), options.filter);
652
786
  const request = protobuf.create(proto.StreamEventsRequestSchema, {
653
787
  filter: mergedFilter
654
788
  });
655
789
  return mapAsyncIterable(
656
- options.client.streamEvents(request),
790
+ client.streamEvents(request),
657
791
  (resp) => streamResponseToEvent(resp)
658
792
  );
659
793
  };
794
+ const onReconnect = options.clientFactory ? () => {
795
+ currentClient = options.clientFactory();
796
+ return {
797
+ subscribeLive: createSubscribeLive(currentClient),
798
+ fetchBackfill: createFetchBackfill(currentClient)
799
+ };
800
+ } : void 0;
660
801
  return new ReplayStream({
661
802
  startSlot: options.startSlot,
662
803
  safetyMargin,
663
- fetchBackfill,
664
- subscribeLive,
804
+ fetchBackfill: createFetchBackfill(currentClient),
805
+ subscribeLive: createSubscribeLive(currentClient),
665
806
  extractSlot: (event) => event.slot ?? 0n,
666
807
  extractKey: eventKey,
667
808
  logger: options.logger,
668
- resubscribeOnEnd: options.resubscribeOnEnd
809
+ resubscribeOnEnd: options.resubscribeOnEnd,
810
+ onReconnect
669
811
  });
670
812
  }
671
813
  function streamResponseToEvent(resp) {
@@ -859,7 +1001,7 @@ function snapshotToState(account) {
859
1001
  meta: account.meta,
860
1002
  data: account.data?.data ?? new Uint8Array(0),
861
1003
  isDelete: account.meta.flags?.isDeleted ?? false,
862
- source: "snapshot"
1004
+ source: "stream"
863
1005
  };
864
1006
  }
865
1007
  function assembledToState(assembled) {
@@ -871,9 +1013,248 @@ function assembledToState(assembled) {
871
1013
  meta: assembled.meta,
872
1014
  data: assembled.data,
873
1015
  isDelete: assembled.isDelete,
874
- source: "update"
1016
+ source: "stream"
875
1017
  };
876
1018
  }
1019
+ function getAccountToState(account) {
1020
+ if (!account.address?.value || !account.meta) {
1021
+ return null;
1022
+ }
1023
+ return {
1024
+ address: account.address.value,
1025
+ addressHex: bytesToHex2(account.address.value),
1026
+ slot: account.meta.lastUpdatedSlot ?? account.versionContext?.slot ?? 0n,
1027
+ seq: BigInt(account.meta.seq.toString()),
1028
+ meta: account.meta,
1029
+ data: account.data?.data ?? new Uint8Array(0),
1030
+ isDelete: account.meta.flags?.isDeleted ?? false,
1031
+ source: "backfill"
1032
+ };
1033
+ }
1034
+ function buildListAccountsOwnerFilter(owner, dataSizes, minUpdatedSlot) {
1035
+ let expression = "account.meta.owner.value == params.owner_bytes";
1036
+ if (dataSizes && dataSizes.length > 0) {
1037
+ const sizeConditions = dataSizes.map((size) => `account.meta.data_size == uint(${size})`).join(" || ");
1038
+ expression = `(${expression}) && (${sizeConditions})`;
1039
+ }
1040
+ if (minUpdatedSlot !== void 0 && minUpdatedSlot > 0n) {
1041
+ expression = `(${expression}) && account.meta.last_updated_slot >= params.min_updated_slot`;
1042
+ }
1043
+ const params = {
1044
+ owner_bytes: protobuf.create(proto.FilterParamValueSchema, { kind: { case: "bytesValue", value: new Uint8Array(owner) } })
1045
+ };
1046
+ if (minUpdatedSlot !== void 0 && minUpdatedSlot > 0n) {
1047
+ params["min_updated_slot"] = protobuf.create(proto.FilterParamValueSchema, { kind: { case: "uintValue", value: minUpdatedSlot } });
1048
+ }
1049
+ return protobuf.create(proto.FilterSchema, { expression, params });
1050
+ }
1051
+ async function* createAccountsByOwnerReplay(options) {
1052
+ const {
1053
+ owner,
1054
+ view = proto.AccountView.FULL,
1055
+ dataSizes,
1056
+ minUpdatedSlot,
1057
+ pageSize = 100,
1058
+ maxRetries = 3,
1059
+ pageAssemblerOptions,
1060
+ cleanupInterval = 1e4,
1061
+ onBackfillComplete,
1062
+ clientFactory,
1063
+ logger = NOOP_LOGGER
1064
+ } = options;
1065
+ let client = resolveClient(options, "AccountsByOwnerReplayOptions");
1066
+ const seenFromStream = /* @__PURE__ */ new Set();
1067
+ const fetchQueue = [];
1068
+ let highestSlotSeen = minUpdatedSlot ?? 0n;
1069
+ const assembler = new PageAssembler(pageAssemblerOptions);
1070
+ let cleanupTimer = null;
1071
+ const streamBuffer = [];
1072
+ let streamDone = false;
1073
+ let streamError = null;
1074
+ try {
1075
+ cleanupTimer = setInterval(() => {
1076
+ assembler.cleanup();
1077
+ }, cleanupInterval);
1078
+ const streamFilter = buildOwnerFilterWithMinSlot(owner, dataSizes, minUpdatedSlot);
1079
+ const stream = client.streamAccountUpdates({ view, filter: streamFilter });
1080
+ const streamProcessor = (async () => {
1081
+ try {
1082
+ for await (const response of stream) {
1083
+ const event = processResponseMulti(response, assembler);
1084
+ if (event) {
1085
+ if (event.type === "account") {
1086
+ seenFromStream.add(event.account.addressHex);
1087
+ if (event.account.slot > highestSlotSeen) {
1088
+ highestSlotSeen = event.account.slot;
1089
+ }
1090
+ }
1091
+ streamBuffer.push(event);
1092
+ }
1093
+ }
1094
+ } catch (err) {
1095
+ streamError = err;
1096
+ } finally {
1097
+ streamDone = true;
1098
+ }
1099
+ })();
1100
+ const yieldStreamBuffer = function* () {
1101
+ while (streamBuffer.length > 0) {
1102
+ const event = streamBuffer.shift();
1103
+ if (event.type === "account") {
1104
+ seenFromStream.add(event.account.addressHex);
1105
+ }
1106
+ yield event;
1107
+ }
1108
+ };
1109
+ const backfillFilter = buildListAccountsOwnerFilter(owner, dataSizes, minUpdatedSlot);
1110
+ let pageToken;
1111
+ do {
1112
+ const request = {
1113
+ view: proto.AccountView.META_ONLY,
1114
+ // Address + metadata only, no data
1115
+ filter: backfillFilter,
1116
+ page: protobuf.create(proto.PageRequestSchema, {
1117
+ pageSize,
1118
+ pageToken
1119
+ })
1120
+ };
1121
+ const response = await client.listAccounts(request);
1122
+ for (const account of response.accounts) {
1123
+ if (account.address?.value) {
1124
+ fetchQueue.push(account.address.value);
1125
+ }
1126
+ }
1127
+ pageToken = response.page?.nextPageToken;
1128
+ yield* yieldStreamBuffer();
1129
+ } while (pageToken);
1130
+ for (const address of fetchQueue) {
1131
+ const addressHex = bytesToHex2(address);
1132
+ if (seenFromStream.has(addressHex)) {
1133
+ continue;
1134
+ }
1135
+ yield* yieldStreamBuffer();
1136
+ if (seenFromStream.has(addressHex)) {
1137
+ continue;
1138
+ }
1139
+ let account = null;
1140
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1141
+ try {
1142
+ account = await client.getAccount({
1143
+ address: protobuf.create(proto.PubkeySchema, { value: address }),
1144
+ view: proto.AccountView.FULL
1145
+ });
1146
+ break;
1147
+ } catch (err) {
1148
+ if (attempt === maxRetries - 1) {
1149
+ logger.error(`[backfill] failed to fetch account ${addressHex} after ${maxRetries} attempts`, { error: err });
1150
+ } else {
1151
+ await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
1152
+ }
1153
+ }
1154
+ }
1155
+ if (account) {
1156
+ const state = getAccountToState(account);
1157
+ if (state) {
1158
+ if (state.slot > highestSlotSeen) {
1159
+ highestSlotSeen = state.slot;
1160
+ }
1161
+ yield { type: "account", account: state };
1162
+ }
1163
+ }
1164
+ }
1165
+ if (onBackfillComplete) {
1166
+ onBackfillComplete(highestSlotSeen);
1167
+ }
1168
+ const retryConfig = DEFAULT_RETRY_CONFIG;
1169
+ let retryAttempt = 0;
1170
+ let currentStream = stream;
1171
+ let currentStreamProcessor = streamProcessor;
1172
+ const createStreamProcessor = () => {
1173
+ if (clientFactory) {
1174
+ try {
1175
+ client = clientFactory();
1176
+ logger.info("[account-stream] created fresh client for reconnection");
1177
+ } catch (err) {
1178
+ logger.error("[account-stream] failed to create fresh client", { error: err });
1179
+ }
1180
+ }
1181
+ const newStreamFilter = buildOwnerFilterWithMinSlot(owner, dataSizes, highestSlotSeen > 0n ? highestSlotSeen : minUpdatedSlot);
1182
+ const newStream = client.streamAccountUpdates({ view, filter: newStreamFilter });
1183
+ const newProcessor = (async () => {
1184
+ try {
1185
+ for await (const response of newStream) {
1186
+ retryAttempt = 0;
1187
+ const event = processResponseMulti(response, assembler);
1188
+ if (event) {
1189
+ if (event.type === "account") {
1190
+ seenFromStream.add(event.account.addressHex);
1191
+ if (event.account.slot > highestSlotSeen) {
1192
+ highestSlotSeen = event.account.slot;
1193
+ }
1194
+ }
1195
+ streamBuffer.push(event);
1196
+ }
1197
+ }
1198
+ } catch (err) {
1199
+ streamError = err;
1200
+ } finally {
1201
+ streamDone = true;
1202
+ }
1203
+ })();
1204
+ return { stream: newStream, processor: newProcessor };
1205
+ };
1206
+ while (true) {
1207
+ yield* yieldStreamBuffer();
1208
+ if (streamDone) {
1209
+ if (streamError) {
1210
+ const backoffMs = calculateBackoff(retryAttempt, retryConfig);
1211
+ logger.warn(
1212
+ `[account-stream] disconnected (${streamError.message}); reconnecting in ${backoffMs}ms (attempt ${retryAttempt + 1})`
1213
+ );
1214
+ await delay(backoffMs);
1215
+ retryAttempt++;
1216
+ streamDone = false;
1217
+ streamError = null;
1218
+ streamBuffer.length = 0;
1219
+ const { stream: newStream, processor: newProcessor } = createStreamProcessor();
1220
+ currentStream = newStream;
1221
+ currentStreamProcessor = newProcessor;
1222
+ continue;
1223
+ } else {
1224
+ logger.warn("[account-stream] stream ended unexpectedly; reconnecting...");
1225
+ streamDone = false;
1226
+ const { stream: newStream, processor: newProcessor } = createStreamProcessor();
1227
+ currentStream = newStream;
1228
+ currentStreamProcessor = newProcessor;
1229
+ continue;
1230
+ }
1231
+ }
1232
+ await delay(10);
1233
+ }
1234
+ } finally {
1235
+ if (cleanupTimer) {
1236
+ clearInterval(cleanupTimer);
1237
+ }
1238
+ assembler.clear();
1239
+ }
1240
+ }
1241
+ function buildOwnerFilterWithMinSlot(owner, dataSizes, minSlot) {
1242
+ let expression = "(has(snapshot.meta.owner) && snapshot.meta.owner.value == params.owner) || (has(account_update.meta.owner) && account_update.meta.owner.value == params.owner)";
1243
+ if (dataSizes && dataSizes.length > 0) {
1244
+ const sizeConditions = dataSizes.map((size) => `snapshot.meta.data_size == uint(${size}) || account_update.meta.data_size == uint(${size})`).join(" || ");
1245
+ expression = `(${expression}) && (${sizeConditions})`;
1246
+ }
1247
+ if (minSlot !== void 0 && minSlot > 0n) {
1248
+ expression = `(${expression}) && (has(snapshot.address) || (has(account_update.slot) && account_update.slot >= params.min_slot))`;
1249
+ }
1250
+ const params = {
1251
+ owner: protobuf.create(proto.FilterParamValueSchema, { kind: { case: "bytesValue", value: new Uint8Array(owner) } })
1252
+ };
1253
+ if (minSlot !== void 0 && minSlot > 0n) {
1254
+ params["min_slot"] = protobuf.create(proto.FilterParamValueSchema, { kind: { case: "uintValue", value: minSlot } });
1255
+ }
1256
+ return protobuf.create(proto.FilterSchema, { expression, params });
1257
+ }
877
1258
  async function* createAccountReplay(options) {
878
1259
  const {
879
1260
  client,
@@ -975,60 +1356,6 @@ function processResponseMulti(response, assembler) {
975
1356
  return null;
976
1357
  }
977
1358
  }
978
- function buildOwnerFilter(owner, dataSizes, additionalFilter) {
979
- let expression = "(has(snapshot.meta.owner) && snapshot.meta.owner.value == params.owner) || (has(account_update.meta.owner) && account_update.meta.owner.value == params.owner)";
980
- if (dataSizes && dataSizes.length > 0) {
981
- const sizeConditions = dataSizes.map((size) => `snapshot.meta.data_size == uint(${size}) || account_update.meta.data_size == uint(${size})`).join(" || ");
982
- expression = `(${expression}) && (${sizeConditions})`;
983
- }
984
- if (additionalFilter?.expression) {
985
- expression = `(${expression}) && (${additionalFilter.expression})`;
986
- }
987
- const params = {
988
- owner: protobuf.create(proto.FilterParamValueSchema, { kind: { case: "bytesValue", value: new Uint8Array(owner) } })
989
- };
990
- if (additionalFilter?.params) {
991
- for (const [key, value] of Object.entries(additionalFilter.params)) {
992
- params[key] = protobuf.create(proto.FilterParamValueSchema, value);
993
- }
994
- }
995
- return protobuf.create(proto.FilterSchema, { expression, params });
996
- }
997
- async function* createAccountsByOwnerReplay(options) {
998
- const {
999
- client,
1000
- owner,
1001
- view = proto.AccountView.FULL,
1002
- dataSizes,
1003
- filter,
1004
- pageAssemblerOptions,
1005
- cleanupInterval = 1e4
1006
- } = options;
1007
- const assembler = new PageAssembler(pageAssemblerOptions);
1008
- let cleanupTimer = null;
1009
- try {
1010
- cleanupTimer = setInterval(() => {
1011
- assembler.cleanup();
1012
- }, cleanupInterval);
1013
- const ownerFilter = buildOwnerFilter(owner, dataSizes, filter);
1014
- const request = {
1015
- view,
1016
- filter: ownerFilter
1017
- };
1018
- const stream = client.streamAccountUpdates(request);
1019
- for await (const response of stream) {
1020
- const event = processResponseMulti(response, assembler);
1021
- if (event) {
1022
- yield event;
1023
- }
1024
- }
1025
- } finally {
1026
- if (cleanupTimer) {
1027
- clearInterval(cleanupTimer);
1028
- }
1029
- assembler.clear();
1030
- }
1031
- }
1032
1359
  var AccountSeqTracker = class {
1033
1360
  seqs = /* @__PURE__ */ new Map();
1034
1361
  /**
@@ -1181,16 +1508,22 @@ Object.defineProperty(exports, "FilterSchema", {
1181
1508
  exports.AccountSeqTracker = AccountSeqTracker;
1182
1509
  exports.ChainClient = ChainClient;
1183
1510
  exports.ConsoleSink = ConsoleSink;
1511
+ exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG;
1184
1512
  exports.MultiAccountReplay = MultiAccountReplay;
1185
1513
  exports.NOOP_LOGGER = NOOP_LOGGER;
1186
1514
  exports.PAGE_SIZE = PAGE_SIZE;
1187
1515
  exports.PageAssembler = PageAssembler;
1188
1516
  exports.ReplayStream = ReplayStream;
1517
+ exports.TimeoutError = TimeoutError;
1518
+ exports.calculateBackoff = calculateBackoff;
1189
1519
  exports.createAccountReplay = createAccountReplay;
1190
1520
  exports.createAccountsByOwnerReplay = createAccountsByOwnerReplay;
1191
1521
  exports.createBlockReplay = createBlockReplay;
1192
1522
  exports.createConsoleLogger = createConsoleLogger;
1193
1523
  exports.createEventReplay = createEventReplay;
1194
1524
  exports.createTransactionReplay = createTransactionReplay;
1525
+ exports.delay = delay;
1526
+ exports.resolveClient = resolveClient;
1527
+ exports.withTimeout = withTimeout;
1195
1528
  //# sourceMappingURL=index.cjs.map
1196
1529
  //# sourceMappingURL=index.cjs.map