@thru/replay 0.2.19 → 0.2.20

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
@@ -797,7 +797,7 @@ function createEventReplay(options) {
797
797
  orderBy: PAGE_ORDER_ASC3,
798
798
  pageToken: cursor
799
799
  });
800
- const baseFilter = slotLiteralFilter("event.slot", startSlot);
800
+ const baseFilter = eventBackfillFilter(startSlot, options.resumeAfter);
801
801
  const mergedFilter = combineFilters(baseFilter, options.filter);
802
802
  const response = await client.listEvents(
803
803
  protobuf.create(proto.ListEventsRequestSchema, {
@@ -808,13 +808,19 @@ function createEventReplay(options) {
808
808
  return backfillPage(response.events, response.page);
809
809
  };
810
810
  const createSubscribeLive = (client) => (startSlot) => {
811
- const mergedFilter = combineFilters(slotLiteralFilter("event.slot", startSlot), options.filter);
811
+ const mergedFilter = combineFilters(
812
+ slotLiteralFilter("event.slot", startSlot),
813
+ options.filter
814
+ );
812
815
  const request = protobuf.create(proto.StreamEventsRequestSchema, {
813
816
  filter: mergedFilter
814
817
  });
815
818
  return mapAsyncIterable(
816
819
  client.streamEvents(request),
817
- (resp) => streamResponseToEvent(resp)
820
+ (resp) => {
821
+ const event = streamResponseToEvent(resp);
822
+ return shouldEmitLiveEvent(event, startSlot, options.resumeAfter) ? event : null;
823
+ }
818
824
  );
819
825
  };
820
826
  const onReconnect = options.clientFactory ? () => {
@@ -852,6 +858,48 @@ function eventKey(event) {
852
858
  const slotPart = event.slot?.toString() ?? "0";
853
859
  return `${slotPart}:${event.callIdx ?? 0}`;
854
860
  }
861
+ function eventBackfillFilter(startSlot, resumeAfter) {
862
+ const boundary = parseEventId(resumeAfter);
863
+ if (!boundary || startSlot > boundary.slot) {
864
+ return slotLiteralFilter("event.slot", startSlot);
865
+ }
866
+ return protobuf.create(proto.FilterSchema, {
867
+ expression: `event.slot > uint(${boundary.slot.toString()}) || (event.slot == uint(${boundary.slot.toString()}) && (event.block_offset > uint(${boundary.blockOffset.toString()}) || (event.block_offset == uint(${boundary.blockOffset.toString()}) && event.call_idx > uint(${boundary.callIdx.toString()}))))`
868
+ });
869
+ }
870
+ function parseEventId(resumeAfter) {
871
+ if (!resumeAfter?.eventId) return null;
872
+ return parseCanonicalEventId(resumeAfter.eventId, resumeAfter.slot);
873
+ }
874
+ function parseCanonicalEventId(eventId, expectedSlot) {
875
+ if (!eventId) return null;
876
+ const match = /^ts(\d+)_(\d+)_(\d+)$/.exec(eventId);
877
+ if (!match) return null;
878
+ const [slotPart, blockOffsetPart, callIdxPart] = match.slice(1);
879
+ const slot = BigInt(slotPart);
880
+ if (slot !== expectedSlot) return null;
881
+ return {
882
+ slot,
883
+ blockOffset: BigInt(blockOffsetPart),
884
+ callIdx: BigInt(callIdxPart)
885
+ };
886
+ }
887
+ function isAfterBoundary(event, boundary) {
888
+ if (event.slot !== boundary.slot) return event.slot > boundary.slot;
889
+ if (event.blockOffset !== boundary.blockOffset) {
890
+ return event.blockOffset > boundary.blockOffset;
891
+ }
892
+ return event.callIdx > boundary.callIdx;
893
+ }
894
+ function shouldEmitLiveEvent(event, startSlot, resumeAfter) {
895
+ const boundary = parseEventId(resumeAfter);
896
+ if (!boundary || startSlot > boundary.slot) return true;
897
+ const eventSlot = event.slot ?? 0n;
898
+ if (eventSlot > boundary.slot) return true;
899
+ if (eventSlot < boundary.slot) return false;
900
+ const eventPosition = parseCanonicalEventId(event.eventId, boundary.slot);
901
+ return eventPosition ? isAfterBoundary(eventPosition, boundary) : false;
902
+ }
855
903
 
856
904
  // src/page-assembler.ts
857
905
  var PAGE_SIZE = 4096;