@secondlayer/sdk 6.39.0 → 6.40.1

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.d.ts CHANGED
@@ -509,6 +509,12 @@ type StreamsEventBase = {
509
509
  * Optional for back-compat; the API always sets it on Streams responses.
510
510
  */
511
511
  finalized?: boolean
512
+ /**
513
+ * Labels whose filter group this event satisfied, present only when the
514
+ * request used a labelled `filters` map. The SDK dispatches on it for you
515
+ * (`on.<label>`), so handlers rarely read it directly.
516
+ */
517
+ matched?: string[]
512
518
  };
513
519
  type StreamsEventOf<
514
520
  T extends StreamsEventType,
@@ -578,6 +584,34 @@ type StreamsReorgsListEnvelope = {
578
584
  };
579
585
  /** A filter that matches a single value or any value in a list. */
580
586
  type StreamsFilterValue = string | readonly string[];
587
+ /**
588
+ * One labelled filter group. Fields inside a group AND together; groups in a
589
+ * map OR together server-side.
590
+ */
591
+ type StreamsLabelledFilter = {
592
+ types?: readonly StreamsEventType[]
593
+ contractId?: StreamsFilterValue
594
+ sender?: StreamsFilterValue
595
+ recipient?: StreamsFilterValue
596
+ assetIdentifier?: string
597
+ };
598
+ /** A map of label → filter group. Labels are yours; the server echoes them. */
599
+ type StreamsFilterMap = Record<string, StreamsLabelledFilter>;
600
+ /**
601
+ * The events one label can yield. A group that declares `types` narrows to
602
+ * exactly those variants, so a per-label handler needs no `event_type` guard;
603
+ * a group without `types` keeps the full union.
604
+ */
605
+ type StreamsEventForFilter<
606
+ F,
607
+ D extends boolean = false
608
+ > = F extends {
609
+ types: readonly (infer T extends StreamsEventType)[]
610
+ } ? D extends true ? Extract<IndexEvent, {
611
+ event_type: T
612
+ }> : Extract<StreamsEvent, {
613
+ event_type: T
614
+ }> : D extends true ? IndexEvent : StreamsEvent;
581
615
  type StreamsEventsListParams = {
582
616
  cursor?: string | null
583
617
  fromHeight?: number
@@ -589,6 +623,12 @@ type StreamsEventsListParams = {
589
623
  sender?: StreamsFilterValue
590
624
  recipient?: StreamsFilterValue
591
625
  assetIdentifier?: string
626
+ /**
627
+ * Labelled filter groups. The groups OR together in one scan and each
628
+ * returned event carries the labels it matched (`event.matched`) — two
629
+ * unrelated concerns share one page, one cursor, one checkpoint.
630
+ */
631
+ filters?: StreamsFilterMap
592
632
  limit?: number
593
633
  };
594
634
  type StreamsEventsStreamParams = {
@@ -599,6 +639,8 @@ type StreamsEventsStreamParams = {
599
639
  sender?: StreamsFilterValue
600
640
  recipient?: StreamsFilterValue
601
641
  assetIdentifier?: string
642
+ /** Labelled filter groups; see {@link StreamsEventsListParams.filters}. */
643
+ filters?: StreamsFilterMap
602
644
  batchSize?: number
603
645
  emptyBackoffMs?: number
604
646
  maxPages?: number
@@ -614,6 +656,8 @@ type StreamsEventsSubscribeParams = {
614
656
  sender?: StreamsFilterValue
615
657
  recipient?: StreamsFilterValue
616
658
  assetIdentifier?: string
659
+ /** Labelled filter groups; see {@link StreamsEventsListParams.filters}. */
660
+ filters?: StreamsFilterMap
617
661
  /** Abort to unsubscribe (the returned function does the same). */
618
662
  signal?: AbortSignal
619
663
  /** Called for each pushed event, in order. */
@@ -665,7 +709,8 @@ type StreamsReorgContext = {
665
709
  };
666
710
  type StreamsEventsConsumeParams<
667
711
  TTx = never,
668
- D extends boolean = false
712
+ D extends boolean = false,
713
+ F extends StreamsFilterMap = Record<never, never>
669
714
  > = {
670
715
  fromCursor?: string | null
671
716
  /**
@@ -699,13 +744,33 @@ type StreamsEventsConsumeParams<
699
744
  sender?: StreamsFilterValue
700
745
  recipient?: StreamsFilterValue
701
746
  assetIdentifier?: string
747
+ /**
748
+ * Labelled filter groups. The groups OR together in ONE server-side scan,
749
+ * so two unrelated concerns share one page, one cursor, and one checkpoint
750
+ * instead of two consume loops. Pair with `on` to handle each label.
751
+ */
752
+ filters?: F
753
+ /**
754
+ * One handler per label in `filters`. Every label must be handled, so
755
+ * adding a label is a compile error until its handler exists.
756
+ *
757
+ * A label's declared `types` narrows its handler's events, so `payload` is
758
+ * typed without an `event_type` guard. Labels are dispatched in declaration
759
+ * order: a label's own events stay in cursor order, but events of different
760
+ * labels do not interleave within a page — add `onBatch` when you need
761
+ * strict global order across labels.
762
+ */
763
+ on?: { [K in keyof F] : (events: StreamsEventForFilter<F[K], D>[], ctx: StreamsBatchContext & WithSinkTx<TTx>) => void | Promise<void> }
702
764
  batchSize?: number
703
765
  /**
704
766
  * Apply a page of canonical events. Persist `ctx.cursor` in the same
705
767
  * transaction as your writes. Returning a cursor overrides `ctx.cursor` as
706
768
  * the resume point (advanced manual control); returning nothing uses it.
769
+ *
770
+ * Optional only when `on` handles the page instead; a consume call with
771
+ * neither throws.
707
772
  */
708
- onBatch: (events: D extends true ? IndexEvent[] : StreamsEvent[], envelope: StreamsEventsEnvelope, ctx: StreamsBatchContext & WithSinkTx<TTx>) => void | string | null | undefined | Promise<void> | Promise<string | null | undefined>
773
+ onBatch?: (events: D extends true ? IndexEvent[] : StreamsEvent[], envelope: StreamsEventsEnvelope, ctx: StreamsBatchContext & WithSinkTx<TTx>) => void | string | null | undefined | Promise<void> | Promise<string | null | undefined>
709
774
  /**
710
775
  * Roll your projection back to `reorg.fork_point_height`, persisting
711
776
  * `ctx.cursor` in the same transaction. Called once per *new* reorg
@@ -864,9 +929,10 @@ type StreamsClient = {
864
929
  * stops it.
865
930
  */
866
931
  consume<
932
+ const F extends StreamsFilterMap = Record<never, never>,
867
933
  TTx = never,
868
934
  D extends boolean = false
869
- >(params: StreamsEventsConsumeParams<TTx, D> & {
935
+ >(params: StreamsEventsConsumeParams<TTx, D, F> & {
870
936
  sink?: ConsumerSink<TTx>
871
937
  }): Promise<StreamsEventsConsumeResult>
872
938
  /**
package/dist/index.js CHANGED
@@ -655,7 +655,8 @@ async function consumeStreamsEvents(opts) {
655
655
  const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
656
656
  const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
657
657
  const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
658
- let cursor = opts.fromCursor ?? await opts.sink?.loadCursor() ?? null;
658
+ const committedCursor = await opts.sink?.loadCursor() ?? null;
659
+ let cursor = opts.fromCursor ?? committedCursor;
659
660
  const handledReorgs = new Set;
660
661
  let pages = 0;
661
662
  let emptyPolls = 0;
@@ -669,7 +670,8 @@ async function consumeStreamsEvents(opts) {
669
670
  contractId: opts.contractId,
670
671
  sender: opts.sender,
671
672
  recipient: opts.recipient,
672
- assetIdentifier: opts.assetIdentifier
673
+ assetIdentifier: opts.assetIdentifier,
674
+ filters: opts.filters
673
675
  }), {
674
676
  retryCount: opts.retryCount,
675
677
  retryDelay: opts.retryDelay,
@@ -746,7 +748,8 @@ async function* iterateStreamsBatches(opts) {
746
748
  contractId: opts.contractId,
747
749
  sender: opts.sender,
748
750
  recipient: opts.recipient,
749
- assetIdentifier: opts.assetIdentifier
751
+ assetIdentifier: opts.assetIdentifier,
752
+ filters: opts.filters
750
753
  });
751
754
  const checkpoint = envelope.next_cursor ?? cursor;
752
755
  if (envelope.events.length > 0 || envelope.reorgs.length > 0) {
@@ -783,7 +786,8 @@ async function* streamStreamsEvents(opts) {
783
786
  contractId: opts.contractId,
784
787
  sender: opts.sender,
785
788
  recipient: opts.recipient,
786
- assetIdentifier: opts.assetIdentifier
789
+ assetIdentifier: opts.assetIdentifier,
790
+ filters: opts.filters
787
791
  });
788
792
  pages++;
789
793
  for (const event of envelope.events) {
@@ -817,7 +821,8 @@ async function consumeIndexFeed(opts) {
817
821
  const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
818
822
  const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
819
823
  const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
820
- let cursor = opts.fromCursor ?? await opts.sink?.loadCursor() ?? null;
824
+ const committedCursor = await opts.sink?.loadCursor() ?? null;
825
+ let cursor = opts.fromCursor ?? committedCursor;
821
826
  const handledReorgs = new Set;
822
827
  let pages = 0;
823
828
  let emptyPolls = 0;
@@ -1598,7 +1603,8 @@ function subscribeStreamsEvents(opts) {
1598
1603
  contract_id: params.contractId,
1599
1604
  sender: params.sender,
1600
1605
  recipient: params.recipient,
1601
- asset_identifier: params.assetIdentifier
1606
+ asset_identifier: params.assetIdentifier,
1607
+ filters: params.filters ? JSON.stringify(params.filters) : undefined
1602
1608
  })}`;
1603
1609
  const res = await opts.fetchImpl(url, {
1604
1610
  headers: {
@@ -1842,7 +1848,8 @@ function createStreamsClient(options) {
1842
1848
  contractId,
1843
1849
  sender,
1844
1850
  recipient,
1845
- assetIdentifier
1851
+ assetIdentifier,
1852
+ filters
1846
1853
  }) => {
1847
1854
  return listEvents({
1848
1855
  cursor,
@@ -1852,9 +1859,13 @@ function createStreamsClient(options) {
1852
1859
  contractId,
1853
1860
  sender,
1854
1861
  recipient,
1855
- assetIdentifier
1862
+ assetIdentifier,
1863
+ filters
1856
1864
  });
1857
1865
  };
1866
+ function encodeFilters(filters) {
1867
+ return filters ? JSON.stringify(filters) : undefined;
1868
+ }
1858
1869
  async function listEvents(params = {}) {
1859
1870
  return request(`/v1/streams/events${buildQuery({
1860
1871
  cursor: params.cursor,
@@ -1866,9 +1877,56 @@ function createStreamsClient(options) {
1866
1877
  recipient: params.recipient,
1867
1878
  asset_identifier: params.assetIdentifier,
1868
1879
  types: params.types,
1869
- not_types: params.notTypes
1880
+ not_types: params.notTypes,
1881
+ filters: encodeFilters(params.filters)
1870
1882
  })}`);
1871
1883
  }
1884
+ function consumeEvents(params) {
1885
+ const labels = params.filters ? Object.keys(params.filters) : [];
1886
+ if (labels.length === 0 && !params.onBatch) {
1887
+ throw new ValidationError("consume requires onBatch, or filters + on handlers.", 400);
1888
+ }
1889
+ if (labels.length > 0 && !params.on && !params.onBatch) {
1890
+ throw new ValidationError("consume with filters requires on handlers (or onBatch).", 400);
1891
+ }
1892
+ const dispatch = params.on;
1893
+ const project = (events) => params.decoded ? events.map(decode) : events;
1894
+ const onBatch = async (events, envelope, ctx) => {
1895
+ for (const label of labels) {
1896
+ const forLabel = events.filter((event) => event.matched?.includes(label));
1897
+ if (forLabel.length === 0)
1898
+ continue;
1899
+ await dispatch?.[label]?.(project(forLabel), ctx);
1900
+ }
1901
+ const userOnBatch = params.onBatch;
1902
+ return await userOnBatch?.(project(events), envelope, ctx);
1903
+ };
1904
+ return consumeStreamsEvents({
1905
+ fromCursor: params.fromCursor,
1906
+ sink: params.sink,
1907
+ onProgress: params.onProgress,
1908
+ mode: params.mode,
1909
+ finalizedOnly: params.finalizedOnly,
1910
+ types: params.types,
1911
+ notTypes: params.notTypes,
1912
+ contractId: params.contractId,
1913
+ sender: params.sender,
1914
+ recipient: params.recipient,
1915
+ assetIdentifier: params.assetIdentifier,
1916
+ filters: params.filters,
1917
+ batchSize: params.batchSize ?? 100,
1918
+ fetchEvents,
1919
+ onBatch,
1920
+ onReorg: params.onReorg,
1921
+ emptyBackoffMs: params.emptyBackoffMs,
1922
+ maxPages: params.maxPages,
1923
+ maxEmptyPolls: params.maxEmptyPolls,
1924
+ signal: params.signal,
1925
+ retryCount: params.retryCount,
1926
+ retryDelay: params.retryDelay,
1927
+ onError: params.onError
1928
+ });
1929
+ }
1872
1930
  return {
1873
1931
  consume(params = {}) {
1874
1932
  return iterateStreamsBatches({
@@ -1890,33 +1948,7 @@ function createStreamsClient(options) {
1890
1948
  byTxId(txId) {
1891
1949
  return request(`/v1/streams/events/${encodeURIComponent(txId)}`);
1892
1950
  },
1893
- consume(params) {
1894
- const onBatch = params.decoded ? (events, envelope, ctx) => params.onBatch(events.map(decode), envelope, ctx) : params.onBatch;
1895
- return consumeStreamsEvents({
1896
- fromCursor: params.fromCursor,
1897
- sink: params.sink,
1898
- onProgress: params.onProgress,
1899
- mode: params.mode,
1900
- finalizedOnly: params.finalizedOnly,
1901
- types: params.types,
1902
- notTypes: params.notTypes,
1903
- contractId: params.contractId,
1904
- sender: params.sender,
1905
- recipient: params.recipient,
1906
- assetIdentifier: params.assetIdentifier,
1907
- batchSize: params.batchSize ?? 100,
1908
- fetchEvents,
1909
- onBatch,
1910
- onReorg: params.onReorg,
1911
- emptyBackoffMs: params.emptyBackoffMs,
1912
- maxPages: params.maxPages,
1913
- maxEmptyPolls: params.maxEmptyPolls,
1914
- signal: params.signal,
1915
- retryCount: params.retryCount,
1916
- retryDelay: params.retryDelay,
1917
- onError: params.onError
1918
- });
1919
- },
1951
+ consume: consumeEvents,
1920
1952
  stream(params = {}) {
1921
1953
  return streamStreamsEvents({
1922
1954
  fromCursor: params.fromCursor,
@@ -2845,5 +2877,5 @@ export {
2845
2877
  ApiError
2846
2878
  };
2847
2879
 
2848
- //# debugId=8D2C49296338C5CF64756E2164756E21
2880
+ //# debugId=E4A30787B92D2A4C64756E2164756E21
2849
2881
  //# sourceMappingURL=index.js.map