Skip to content

feat: add message/channel paginator stack with thread minimal-init and state stores - #1795

Merged
MartinCupela merged 53 commits into
release-v10from
feat/message-paginator-master-merge
Aug 3, 2026
Merged

feat: add message/channel paginator stack with thread minimal-init and state stores#1795
MartinCupela merged 53 commits into
release-v10from
feat/message-paginator-master-merge

Conversation

@MartinCupela

@MartinCupela MartinCupela commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR is a broader SDK architecture update, not just a message pagination introduction.
In addition to the new paginator stack, it introduces minimal Thread initialization (without initial server thread payload), reactive StateStore-based state in ChannelState, new orchestration/event-pipeline utilities.

What’s included

1) New pagination foundation + message pagination

  • Introduces the new paginator system under src/pagination/paginators/*:
    • BasePaginator
    • ChannelPaginator
    • MessagePaginator
    • MessageReplyPaginator
    • ReminderPaginator
  • Adds supporting pagination utilities:
    • filter/sort compilers
    • cursor derivation helpers
    • index + normalization utilities
  • Adds MessagePaginator integration for channel and thread flows:
    • reply querying through paginator
    • message focus signal support
    • ingest/update/delete/reaction/truncation handling
    • jump-to-message state emission fixes

2) Multi-list channel orchestration

  • Adds ChannelPaginatorsOrchestrator for managing multiple channel lists/paginators.
  • Adds EventHandlerPipeline for composable/ordered event handling.
  • Supports ownership strategies when a channel matches multiple paginators (including priority-based ownership).
  • Handles list updates/removals/re-emits across relevant channel events.

3) Thread minimal-init support (no initial thread payload required)

  • Thread constructor now supports initialization from:
    • client + channel + parentMessage (with optional draft)
    • or existing threadData
  • Enables creating a usable thread instance before full hydration.
  • Keeps reload/hydration and pagination bootstrap behavior available after creation.
  • Includes broader thread event/state synchronization updates (participants/reply count/read/replies).

4) Reactive ChannelState stores

  • Adds StateStore-backed reactive slices in ChannelState:
    • watchers
    • typing
    • read
    • members (+ member count sync)
    • own capabilities
    • muted users
  • Preserves existing access patterns while introducing store-backed updates.

5) Message operations + local update flows

  • Adds MessageOperations and MessageOperationStatePolicy.
  • Adds/expands optimistic local update APIs for Channel/Thread message actions:
    • send
    • retry
    • update
    • delete
  • Adds message send retry cache behavior and related test coverage.

6) Config/setup and request customization improvements

  • Introduces and exports InstanceConfigurationService.
  • Moves setup/config state handling to dedicated configuration module and root exports.
  • Adds custom mark-read request support for both Channel and Thread.
  • Removes thread parameter from Channel custom mark-read request signature.

Testing

Large test expansion and updates across:

  • paginator units (Base, Channel, Message, MessageReply, compiler/index utilities)
  • orchestrator + event pipeline
  • channel/channel_state/thread behaviors
  • message operations + delivery tracking
  • composer/text composer interactions
  • merge utility behavior

Todo

  • internal docs
  • docs

Support setting paginator items directly, optional request retries, offline DB in ChannelPaginator, identification of pagination restart based on query shape change.
…eat/message-paginator

# Conflicts:
#	src/channel.ts
#	src/client.ts
….messages.deleted and message.deleted events
@MartinCupela MartinCupela changed the title feat: add MessagePaginator feat: add message/channel paginator stack with thread minimal-init and state stores Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Size Change: +96.3 kB (+23.54%) 🚨

Total Size: 505 kB

📦 View Changed
Filename Size Change
dist/cjs/index.browser.js 168 kB +32.1 kB (+23.57%) 🚨
dist/cjs/index.node.js 170 kB +32.2 kB (+23.39%) 🚨
dist/esm/index.mjs 167 kB +32 kB (+23.65%) 🚨

compressed-size-action

isekovanic and others added 15 commits July 8, 2026 14:44
…rge' into feat/message-paginator-master-merge
## CLA

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required).
- [ ] Code changes are tested

## Description of the changes, What, Why and How?

## Changelog

-
## CLA

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required).
- [ ] Code changes are tested

## Description of the changes, What, Why and How?

## Changelog

-
## CLA

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required).
- [ ] Code changes are tested

## Description of the changes, What, Why and How?

## Changelog

-
…ith paginators (#1806)

### Summary

Completes the `message-paginator` initiative on the LLC side: the
channel's **messages, thread replies, and pinned messages are no longer
stored on `channel.state`**. Each list now lives in a paginator that is
the single source of truth (interval storage + a canonical `ItemIndex`):

- **Main message list** → `channel.messagePaginator`
- **Thread replies** → `thread.messagePaginator` (owned by the `Thread`
object)
- **Pinned messages** → `channel.pinnedMessagesPaginator` (new
`PinnedMessagePaginator`)

### What changed

- **`ChannelState` storage removed:** `messages`, `latestMessages`,
`messageSets`, `messagePagination`, `threads`, `pinnedMessages`, and
their mutators (`addMessageSorted`/`addMessagesSorted`, `removeMessage`,
`findMessage`, `findMessageByTimestamp`, `filterErrorMessages`,
`loadMessageIntoState`, `clearMessages`, `initMessages`, `pruneOldest`,
`addReaction`/`removeReaction`, `updateUserMessages`,
`deleteUserMessages`,
`addPinnedMessages`/`addPinnedMessage`/`removePinnedMessage`,
`removeQuotedMessageReferences`, + internal helpers).
- **`PinnedMessagePaginator`** added and populated from channel events;
pin/unpin falls out of `ingestItem` + a `{ pinned: true }` filter (no
bespoke branching).
- **`MessageIntervalPaginator`** extracted as the unread-free base of
`MessagePaginator`; it tracks the latest message (`latestMessageId` +
`latestMessage`), advanced on ingest and mirrored into reactive state.
- **`channel.state.last_message_at`** is now a **read-only getter**
derived from `messagePaginator.latestMessage` (setter + backing field
removed).
- **`channel.state.isUpToDate` / `setIsUpToDate` removed** —
live-message routing (don't disrupt a scrolled-away view) is handled
structurally by the paginator's interval model.
- **`Channel._trackLatestMessage` removed.** `user.updated` /
`user.deleted` propagation now scans active channels
(`reflectUserUpdate` / `applyMessageDeletionForUser` self-filter by
author id). A targeted user-reference index is planned
(`specs/user-reference-index`).
- **`utils` cleanup:** removed `addToMessageList`,
`messageSetPagination`, `binarySearchByDateEqualOrNearestGreater`,
`deleteUserMessages`, and the `MessageSet` / message-set pagination
types.
- **Reactive `headItems`** added to `PaginatorState` (newest-loaded
window).
- **Removed the unused `MessageReplyPaginator`** (dead sibling; the
thread reply list uses `MessagePaginator`).

### Breaking changes

Full list + before → after migration table in
**`docs/breaking-changes-v14-v15.md`**. Highlights:

| Before (v14) | After (v15) |
| --- | --- |
| `channel.state.messages` | `channel.messagePaginator.state.items` /
`.items` |
| `channel.state.threads[parentId]` |
`thread.messagePaginator.state.items` |
| `channel.state.pinnedMessages` |
`channel.pinnedMessagesPaginator.state.items` |
| `channel.state.addMessageSorted(m)` |
`channel.messagePaginator.ingestItem(m)` |
| `channel.state.removeMessage({ id })` |
`channel.messagePaginator.removeItem({ id })` |
| `channel.state.findMessage(id)` |
`channel.messagePaginator.getItem(id)` |
| `channel.state.isUpToDate` / `setIsUpToDate` |
`messagePaginator.isActiveIntervalAtHead` / `hasMoreHead` /
`jumpToTheLatestMessage()` |
| `channel.state.last_message_at = …` | read-only (derived); ingest a
message on the paginator |

Behavioral note: `last_message_at` now advances on every incoming
message regardless of the viewer's scroll position (the old `isUpToDate`
suppression is gone) — it's a channel-level fact.

### Follow-ups (not in this PR)

- **`specs/user-reference-index`** — replace the active-channel scan on
`user.updated`/`user.deleted` with a `userId → message-reference` index
(design-gated).
-  Unifying the remaining flat paginators on interval storage
- Offline migration

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…o feat/message-paginator-master-merge

# Conflicts:
#	src/channel.ts
#	src/channel_state.ts
#	src/client.ts
#	src/messageComposer/messageComposer.ts
#	src/messageComposer/middleware/textComposer/types.ts
#	src/messageComposer/textComposer.ts
#	src/messageDelivery/MessageDeliveryReporter.ts
#	src/messageDelivery/MessageReceiptsTracker.ts
#	src/moderation.ts
#	src/pagination/BasePaginator.ts
#	src/pagination/ReminderPaginator.ts
#	src/pagination/UserGroupPaginator.ts
#	src/signing.ts
#	src/thread.ts
#	src/types.ts
#	src/utils.ts
#	test/unit/CooldownTimer.test.ts
#	test/unit/MessageComposer/messageComposer.test.ts
#	test/unit/MessageComposer/middleware/messageComposer/cleanData.test.ts
#	test/unit/MessageComposer/textComposer.test.ts
#	test/unit/channel.test.js
#	test/unit/channel_state.test.js
#	test/unit/client.test.js
#	test/unit/messageDelivery/MessageDeliveryReporter.test.ts
#	test/unit/messageDelivery/MessageReceiptsTracker.test.ts
#	test/unit/pagination/UserGroupPaginator.test.ts
#	test/unit/threads.test.ts
#	test/unit/utils.test.js
#	test/unit/utils.test.ts
@arnautov-anton
arnautov-anton force-pushed the feat/message-paginator-master-merge branch from 4b57520 to d00e67f Compare July 31, 2026 08:06
@MartinCupela
MartinCupela merged commit 62f0507 into release-v10 Aug 3, 2026
4 checks passed
@MartinCupela
MartinCupela deleted the feat/message-paginator-master-merge branch August 3, 2026 09:40
github-actions Bot pushed a commit that referenced this pull request Aug 3, 2026
## [10.0.0-rc.1](v9.50.2...v10.0.0-rc.1) (2026-08-03)

### ⚠ BREAKING CHANGES

* paginator-based message storage replaces
channel.state.messages and channel.state.last_message_at. The message
list, thread replies and pinned messages are now owned by
channel.messagePaginator, thread.messagePaginator and
channel.pinnedMessagesPaginator. OpenAPI-generated clients replace the
hand-written request/response types. See
docs/breaking-changes-v14-v15.md for the full migration guide.

### Features

* add message/channel paginator stack with thread minimal-init and state stores ([#1795](#1795)) ([62f0507](62f0507))
* integrate OpenAPI generated clients ([#1760](#1760)) ([0776bc4](0776bc4))
* v10 ([cd3141e](cd3141e))
@stream-ci-bot

Copy link
Copy Markdown

🎉 This PR is included in version 10.0.0-rc.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants