Skip to content

Conversation

@AndyTWF
Copy link
Collaborator

@AndyTWF AndyTWF commented Oct 11, 2025

Description

In the rest of the SDK, we've not provided event filters on subscription events - this is a fairly trivial thing for users to write (if/switch). Also, when dealing with multiple events in the list, the if/switch is required anyway and in many languages must be exhaustive, so the real benefit is when you're listening to a single event (but still marginal).

Furthermore, the overloading is somewhat cumbersome to maintain.

This change removes the event filtering for presence.

Spec change: ably/specification#390

Checklist

  • QA'd by the author.
  • Unit tests created (if applicable).
  • Integration tests created (if applicable).
  • Follow coding style guidelines found here.
  • TypeDoc updated (if applicable).
  • Updated the website documentation (if applicable).
  • Browser tests created (if applicable).
  • In repo demo app updated (if applicable).

Testing Instructions (Optional)

  • Explain how to test the changes in this PR.
  • Provide specific steps or commands to execute.

Summary by CodeRabbit

  • Refactor
    • Presence subscriptions simplified: a listener is now required and subscribing to specific event types is no longer supported. A single listener receives all presence events; unsubscribe behavior remains. This is a breaking change—update integrations accordingly.
  • Tests
    • Test suite updated and consolidated to use generic presence event listening; removed cases relying on event-type filtering and no-argument subscription.

@coderabbitai
Copy link

coderabbitai bot commented Oct 11, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Presence.subscribe API reduced to a single signature subscribe(listener: PresenceListener): Subscription. Implementation in DefaultPresence simplified to register a single wrapped listener for all presence events and a single unsubscribe path. Tests updated to use the generic listener; one unit test for missing-argument error removed.

Changes

Cohort / File(s) Summary of Changes
Core API: Presence subscribe simplification
src/core/presence.ts
Removed overloads; changed Presence.subscribe and DefaultPresence.subscribe to a single subscribe(listener: PresenceListener): Subscription. Eliminated event-specific subscription branches and per-event unsubscribe logic; kept presence-enabled check and unified logging.
Integration tests: generic listener usage
test/core/presence.integration.test.ts
Replaced event-type-specific subscriptions (Enter/Update/Leave) with a single subscription receiving all presence events; adjusted titles, helpers, and unsubscribe verification to the unified listener flow.
Unit tests: removed missing-argument case
test/core/presence.test.ts
Deleted the test asserting an error when subscribing with no arguments; other presence unit tests remain unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Presence
  participant DefaultPresence as Impl

  Note over Client,Presence: New unified subscription API
  Client->>Presence: subscribe(listener)
  Presence->>Impl: registerWrappedListener(listener)
  rect rgba(220,240,255,0.35)
    Note right of Impl: All presence events forwarded to the same listener\n(no per-event filtering)
    Impl-->>Client: onPresenceEvent(event)
    Impl-->>Client: onPresenceEvent(event)
  end
  Client->>Presence: unsubscribe()
  Presence->>Impl: removeWrappedListener(listener)
  Impl-->>Client: stopped receiving events
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Presence

  Note over Client,Presence: Previous (removed) event-specific subscription
  Client-x Presence: subscribe(eventType | [eventTypes], listener?)
  Note over Presence: Overloads and per-event subscribe/unsubscribe logic removed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and concisely summarizes the primary change of removing event filtering from the presence API, aligning with the refactored subscribe interface that no longer accepts specific event types. It is specific enough for a teammate to understand the core intent without listing implementation details.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4bca40c and 4b452d7.

📒 Files selected for processing (3)
  • src/core/presence.ts (2 hunks)
  • test/core/presence.integration.test.ts (4 hunks)
  • test/core/presence.test.ts (0 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot temporarily deployed to staging/pull/675/typedoc October 11, 2025 10:40 Inactive
@github-actions
Copy link

github-actions bot commented Oct 11, 2025

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 96.42% (🎯 96%) 3805 / 3946
🟢 Statements 96.42% (🎯 96%) 3805 / 3946
🟢 Functions 98.37% (🎯 98%) 302 / 307
🟢 Branches 97.3% (🎯 96%) 976 / 1003
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/core/presence.ts 98.07% 100% 100% 98.07% 224-226
Generated in workflow #2979 for commit 4b452d7 by the Vitest Coverage Report Action

AndyTWF added a commit to ably/specification that referenced this pull request Oct 11, 2025
If ably/ably-chat-js#675 is approved we will no
longer allow subscription to specific events.
@AndyTWF AndyTWF requested review from splindsay-92 and vladvelici and removed request for vladvelici October 13, 2025 09:01
@AndyTWF AndyTWF marked this pull request as ready for review October 13, 2025 09:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e174682 and 4bca40c.

📒 Files selected for processing (3)
  • src/core/presence.ts (2 hunks)
  • test/core/presence.integration.test.ts (4 hunks)
  • test/core/presence.test.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • test/core/presence.test.ts
🧰 Additional context used
📓 Path-based instructions (11)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/file-conventions.mdc)

Create all Typescript and TSX files using kebab-case.

src/**/*.{ts,tsx}: Import Ably as: import * as Ably from 'ably'
Include spec point comments in code using // @CHA- (e.g., // @CHA-M10a)

Files:

  • src/core/presence.ts

⚙️ CodeRabbit configuration file

src/**/*.{ts,tsx}: If there are any changes to the public-facing API, add a review comment: "Public API changes detected. Please update the documentation and the Ably CLI."

Files:

  • src/core/presence.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use relative imports within the project; import Ably as import * as Ably from 'ably'
Prefer named exports over default exports and group related exports
Use PascalCase for interface names; do not prefix with 'I'; document interfaces and properties with JSDoc, including @throws and @return
Use PascalCase for enums and enum members; include JSDoc for enums and members
Use PascalCase for class names; implement interfaces explicitly; avoid inheritance when possible; document classes
Prefix private members with underscore; group members by visibility; document all public members
Document all public methods with JSDoc using @param, @returns, @throws
Use descriptive parameter names; prefer parameter objects for related params; use optional params with defaults when appropriate
Use Ably.ErrorInfo for errors with message, code, and HTTP-derived status code; use ErrorCodes from src/core/errors.ts
Use consistent logging levels (trace, debug, error); include context object; never log Ably channel instances; structure logs clearly
Minimize type assertions; prefer type guards; never use any (use unknown if unavoidable) and document assertions
Prefer async/await over raw promises; handle rejections; document async behavior
For each @[Testable]@ spec point, include the spec annotation (e.g., // @CHA-M10a) in the relevant production code

**/*.{ts,tsx}: Use relative imports within the project
Import Ably as import * as Ably from 'ably'
Export interfaces and types that are part of the public API
Prefer named exports over default exports
Group related exports together
Use PascalCase for interface names
Use descriptive interface names that reflect purpose
Do not prefix interfaces with 'I'
Use JSDoc comments for interfaces with a clear description
Document all interface properties inline with JSDoc
Document errors with @throws on interfaces when relevant
Document return types with @return in interface-related docs when relevant
Link referenced types in docs using ...

Files:

  • src/core/presence.ts
  • test/core/presence.integration.test.ts
{src,test}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create all TypeScript and TSX files using kebab-case file names

Files:

  • src/core/presence.ts
  • test/core/presence.integration.test.ts
{src,test,react,demo}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{src,test,react,demo}/**/*.{ts,tsx}: Use relative imports within the project
Use PascalCase for classes, interfaces, and enums
Prefix private members with an underscore (e.g., _roomId, _channel)
Avoid any; use unknown if necessary, and prefer strong typing
Use async/await over raw promises

Files:

  • src/core/presence.ts
  • test/core/presence.integration.test.ts
src/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/core/**/*.{ts,tsx}: Error handling must use Ably.ErrorInfo with ErrorCodes enum; construct as new Ably.ErrorInfo(message, code, statusCode)
Log key operations with _logger.trace/_logger.debug/_logger.error using context objects; never log Ably channel instances

Files:

  • src/core/presence.ts
test/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/file-conventions.mdc)

Create all Typescript and TSX files using kebab-case.

test/**/*.{ts,tsx}: Include spec point comments in tests using // CHA- (e.g., // CHA-M10a)
Use Vitest (describe, it, expect) for tests
Use custom matchers from test/helper/expectations.ts for error assertions (e.g., toBeErrorInfo, toThrowErrorInfo)
Follow Arrange-Act-Assert structure in tests
Use data-driven tests with .each() when appropriate

Files:

  • test/core/presence.integration.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)

**/*.test.{ts,tsx}: Use the custom matcher toBeErrorInfo to validate that an error matches expected ErrorInfo properties (code, statusCode, message, cause) when testing error scenarios.
Use the custom matcher toThrowErrorInfo to check if a function throws an error matching expected ErrorInfo properties in tests.
Use the custom matcher toBeErrorInfoWithCode to validate that an error has a specific error code in tests.
Use the custom matcher toThrowErrorInfoWithCode to check if a function throws an error with a specific code in tests.
Use the custom matcher toBeErrorInfoWithCauseCode to validate that an error has a cause with a specific error code in tests.
Use waitForEventualHookValueToBeDefined utility to wait for a React hook value to become defined in asynchronous hook tests.
Use waitForEventualHookValue utility to wait for a React hook to return an expected value in asynchronous hook tests.
Use vi.waitFor to wait for asynchronous events to happen in tests.

**/*.test.{ts,tsx}: Mock the ably library as a module using vi.mock('ably') in test files that use Ably.
Each test file should use Vitest and follow the structure: import { describe, expect, it } from 'vitest'; group tests with describe and define test cases with it.
Use clear, descriptive names for test descriptions, following the format: it('should [expected behavior]').
Group related tests using describe blocks and use nested describes for sub-features.
Structure each test case with Arrange, Act, and Assert sections.
Test both positive and negative cases, including edge cases and boundary conditions.
Use clear, specific assertions with expect, and chain assertions when testing multiple aspects.
Use common assertion methods such as toBe(), toEqual(), toBeTruthy(), toBeFalsy(), and toThrowErrorInfo() in tests.
Create meaningful test data that clearly demonstrates the test case, and use constants for repeated values.
Test error cases explicitly using `toThrowErrorInf...

Files:

  • test/core/presence.integration.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/unit-testing.mdc)

Test files should end with .test.ts and the name should reflect the module being tested (e.g., message.test.ts for message.ts).

Unit test files should end with .test.ts and have clear, behavior-focused descriptions

Files:

  • test/core/presence.integration.test.ts
**/*.integration.test.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.integration.test.{ts,tsx}: Integration tests must use Vitest structure with describe/it/beforeEach and optional typed TestContext
Use vi.waitFor for async events and async/await with appropriate timeouts in integration tests
Do not mock modules in integration tests; use real Ably.Realtime clients

Files:

  • test/core/presence.integration.test.ts
test/**/*.integration.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.integration.test.{ts,tsx}: Integration test files must end with .integration.test.ts or .integration.test.tsx
Integration tests connect to a real Ably sandbox (no mocking)
Use newChatClient() helper to create fully connected clients in integration tests
Use vi.waitFor() for async event waiting in integration tests
Use random room IDs in integration tests to avoid conflicts

Files:

  • test/core/presence.integration.test.ts
test/core/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place unit tests for core SDK under test/core/ mirroring src/core/

Files:

  • test/core/presence.integration.test.ts
🧬 Code graph analysis (2)
src/core/presence.ts (2)
src/core/subscription.ts (1)
  • Subscription (12-19)
src/core/utils/event-emitter.ts (1)
  • wrap (86-89)
test/core/presence.integration.test.ts (2)
src/core/presence.ts (1)
  • PresenceEvent (85-95)
src/core/index.ts (1)
  • PresenceEvent (67-67)
🔇 Additional comments (3)
test/core/presence.integration.test.ts (1)

228-306: LGTM! Tests correctly updated for simplified API.

The integration tests have been properly refactored to use the generic subscribe(listener) API:

  • All subscribe calls now pass a single listener that receives all presence events
  • Comments accurately reflect listening to all presence events
  • Event-specific validation is handled via filtering in the test assertions
  • Unsubscribe behavior is correctly verified

The changes align with the PR objective of removing event filtering from the SDK.

src/core/presence.ts (2)

310-327: Implementation correctly simplified to single subscription path.

Assuming EventEmitter supports the wildcard listener pattern, the implementation correctly:

  • Validates that presence events are enabled (lines 314-317)
  • Wraps the listener for identity preservation during unsubscribe (line 319)
  • Registers the listener to receive all presence events (line 320)
  • Returns a subscription with proper cleanup (lines 321-326)
  • Maintains consistent logging (lines 311, 323)

The simplified implementation reduces complexity and aligns with the PR objective of removing event filtering.


310-327: No action needed: EventEmitter.on() supports all-events subscriptions. The InterfaceEventEmitter defines an overload on(callback: Callback<EventsMap>) for subscribing to all events.

AndyTWF added a commit to ably/specification that referenced this pull request Oct 13, 2025
If ably/ably-chat-js#675 is approved we will no
longer allow subscription to specific events.
In the rest of the SDK, we've not provided event filters on subscription
events - this is a fairly trivial thing for users to write (if/switch).
Also, when dealing with multiple events in the list, the if/switch is
required anyway, so the real benefit is when you're listening to a
single event (but still marginal).

Furthermore, the overloading is somewhat cumbersome to maintain.

This change removes the event filtering for presence.
@AndyTWF AndyTWF enabled auto-merge October 13, 2025 10:05
@AndyTWF AndyTWF force-pushed the presence-filter-removal branch from 4bca40c to 4b452d7 Compare October 13, 2025 10:05
@AndyTWF AndyTWF merged commit 80117ba into main Oct 13, 2025
11 of 12 checks passed
@AndyTWF AndyTWF deleted the presence-filter-removal branch October 13, 2025 10:07
@coderabbitai coderabbitai bot mentioned this pull request Oct 20, 2025
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants