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