Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure and clean up message and presencemessage types #1941

Merged
merged 1 commit into from
Jan 9, 2025

Conversation

SimonWoolf
Copy link
Member

@SimonWoolf SimonWoolf commented Jan 8, 2025

Motivation: I need to add a new AnnotationMessage message type, and the existing message classes were just... not nice to work with. I have:

  • extract common functionality to a new BaseMessage file
  • introduce proper WireMessage and WirePresenceMessage classes, such that:
    • you just call Message.encode() to get a WireMessage, and WireMessage.decode() to get a Message, and the type system will tell you which one you have
    • the toJSON override hack only applies to internal wire-protocol usesand doesn't interfere with the normal public Message/PresenceMessage instances
    • all necessary transformations between userfriendly message and wire protocol messages (encoding the data and the action)
      are now done by decode() and encode(), rather than (as before) the encoding happening in-place and the action encoding happening only on json-stringification
    • ProtocolMessage is now always a wire-protocol message and always holds WireMessages and WirePresence; all decoding is done by the channel
    • The PresencePlugin interface is now just the PresenceMessage and WirePresenceMessage types, rather than ad-hoc individual functions
    • The fromWireProtocol, encode, and decode functions in the DefaultMessage interface are no longer needed and can be removed
  • Cleaned up and simplified the message decoding step in the channel, which was unnecessarily complicated

Also removed an unused field from RealtimeChannel, removed ATTACH_RESUME from the list of channel modes (it's not a channel mode, it should never have been in there), and made the agreed changes to the MessageAction enum.

This ended up being quite a bit of a yakshave. It isn't quite all the changes I saw and wanted to make, but I think this is a good place to stop.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for message summaries with a new MESSAGE_SUMMARY action type.
    • Introduced new types and functions for improved message encryption and encoding.
  • Improvements

    • Streamlined message type definitions across the Ably Realtime library.
    • Refined presence message handling with more consistent wire protocol representations.
    • Enhanced clarity and consistency in encryption test cases.
  • Breaking Changes

    • Removed ATTACH_RESUME channel mode.
    • Deprecated certain message action types like MESSAGE_UNSET, ANNOTATION_CREATE, and ANNOTATION_DELETE.
    • Modified message and presence message creation methods to utilize new class methods.
  • Bug Fixes

    • Improved type safety in message encoding and decoding.
    • Enhanced consistency in message action handling.
  • Refactoring

    • Reorganized import statements and module structures.
    • Simplified message type definitions.
    • Moved common protocol message constants to a separate module.

Copy link

coderabbitai bot commented Jan 8, 2025

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

The pull request introduces a comprehensive refactoring of the Ably library's message handling system, focusing on restructuring message types, encoding/decoding processes, and standardizing message creation. The changes primarily involve updating type definitions, modifying import statements, and introducing new classes like BaseMessage, WireMessage, and WirePresenceMessage. The modifications streamline message processing across various components, emphasizing a more consistent and type-safe approach to handling realtime and REST messages.

Changes

File Change Summary
ably.d.ts Removed ATTACH_RESUME from ChannelModes, added MESSAGE_SUMMARY to MessageActions
src/common/lib/client/defaultrealtime.ts Updated imports for presence message handling, replaced functions with types
src/common/lib/client/modularplugins.ts Updated PresenceMessagePlugin interface to use types instead of functions
src/common/lib/client/presencemap.ts Updated usage of PresenceMessage to call methods directly instead of using imports
src/common/lib/client/realtimechannel.ts Modified message handling methods to use wire formats, updated import statements
src/common/lib/client/realtimepresence.ts Replaced PresenceMessage with WirePresenceMessage in handling presence messages
src/common/lib/client/restchannel.ts Updated message encoding methods to use wire formats
src/common/lib/client/restchannelmixin.ts Changed import from WireProtocolMessage to WireMessage
src/common/lib/client/restpresence.ts Updated import from WireProtocolPresenceMessage to WirePresenceMessage
src/common/lib/client/restpresencemixin.ts Changed import from WireProtocolPresenceMessage to WirePresenceMessage
src/common/lib/transport/comettransport.ts Updated import for actions from new module
src/common/lib/transport/connectionmanager.ts Separated actions import for clarity
src/common/lib/transport/protocol.ts Updated import for actions from new module, no functional changes
src/common/lib/transport/transport.ts Updated import for actions from new module
src/common/lib/types/basemessage.ts Introduced new types and functions for message handling
src/common/lib/types/defaultmessage.ts Streamlined imports and updated static methods for message creation
src/common/lib/types/defaultpresencemessage.ts Updated methods to use WirePresenceMessage
src/common/lib/types/message.ts Refactored message class structure, added new methods for encoding/decoding
src/common/lib/types/presencemessage.ts Introduced WirePresenceMessage, updated message handling methods
src/common/lib/types/protocolmessage.ts Restructured message handling to use wire formats
src/common/lib/types/protocolmessagecommon.ts Defined new constants for actions, flags, and channel modes
src/platform/web/modular/presencemessage.ts Updated export for constructPresenceMessage to use PresenceMessage
src/platform/web/modular/realtimepresence.ts Updated presence message handling to use class references
test/realtime/crypto.test.js Modified encoding method for tests, updated comparison logic
test/realtime/message.test.js Renamed test suite and updated expected values for actions
test/realtime/presence.test.js Updated presence message creation to use PresenceMessage.fromValues
test/realtime/sync.test.js Standardized presence message creation using createPM function
test/rest/presence.test.js Updated action property handling to use encoded representation
scripts/moduleReport.ts Added new files to the allowedFiles set for size contributions

Sequence Diagram

sequenceDiagram
    participant Client
    participant Message
    participant WireMessage
    participant BaseMessage

    Client->>Message: fromValues(data)
    Message->>BaseMessage: Inherit base properties
    Message-->>Client: Create Message instance
    Client->>Message: encode(options)
    Message->>WireMessage: Create WireMessage
    WireMessage-->>Client: Return encoded message
    Client->>WireMessage: decode(context)
    WireMessage->>BaseMessage: Process decoding
    WireMessage-->>Client: Return decoded Message
Loading

Poem

🐰 Hop, hop, through the message maze,
Wire and base, in coding phase,
Encoding dance, decoding spree,
Ably's messages now flow free!
Type-safe rabbit's jubilee! 🎉

Finishing Touches

  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot temporarily deployed to staging/pull/1941/bundle-report January 8, 2025 00:19 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/features January 8, 2025 00:19 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/typedoc January 8, 2025 00:19 Inactive
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: 8

🧹 Nitpick comments (21)
src/common/lib/types/presencemessage.ts (1)

119-145: Optimize error handling in the decode method

In the WirePresenceMessage class, the decode method logs errors but does not rethrow them or provide a mechanism for the calling code to handle them. To enhance error traceability and handling, consider rethrowing the error after logging or returning an error object alongside the decoded message.

🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] File contributes 1165B to bundle, exceeding allowed maximum of 100B

src/common/lib/types/protocolmessage.ts (2)

37-54: Handle potential undefined properties in fromDeserialized

In the deserialize function, properties like messages and presence are conditionally assigned. Ensure that the ProtocolMessage constructor handles undefined values gracefully to prevent potential null reference errors when these properties are accessed.


114-114: Check for nullability of presence when accessing

The presence property is optional in the ProtocolMessage class. When accessing presence in other parts of the code, make sure to perform null or undefined checks to avoid runtime exceptions.

src/common/lib/types/message.ts (1)

180-197: Improve error propagation in decodeWithErr method

In the decodeWithErr method of WireMessage, errors are logged but only returned alongside the decoded message without throwing. Consider whether the calling code expects exceptions to be thrown. If so, rethrow the error after logging to adhere to expected error handling patterns.

src/common/lib/types/basemessage.ts (6)

18-22: Remove redundant nested 'cipher' property in 'CipherOptions'

The cipher property within CipherOptions seems redundant and might cause confusion. Consider removing it if it's not necessary.

Apply this diff to simplify CipherOptions:

 export type CipherOptions = {
   channelCipher: {
     encrypt: (data: Uint8Array | string) => Promise<Uint8Array>;
     algorithm: 'aes';
   };
-  cipher?: {
-    channelCipher: {
-      encrypt: (data: Uint8Array | string) => Promise<Uint8Array>;
-      algorithm: 'aes';
-    };
-  };
 };
🧰 Tools
🪛 Biome (1.9.4)

[error] 20-20: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B


52-52: Simplify null checks using optional chaining

The condition if (options && options.cipher) can be simplified using optional chaining for better readability.

Apply this diff:

- if (options && options.cipher) {
+ if (options?.cipher) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B


93-93: Simplify null checks using optional chaining

Similarly, the condition if (options != null && options.cipher) can be simplified.

Apply this diff:

- if (options != null && options.cipher) {
+ if (options?.cipher) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 93-93: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B


136-139: Use optional chaining for nested property access

The condition can be simplified using optional chaining to improve readability.

Apply this diff:

- if (
-   context.channelOptions != null &&
-   context.channelOptions.cipher &&
-   context.channelOptions.channelCipher
- ) {
+ if (context.channelOptions?.cipher && context.channelOptions.channelCipher) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 136-137: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B


116-116: Avoid assignments within expressions

Assigning within an expression can lead to confusing code. It's better to separate the assignment from the condition.

Refactor the code to avoid assignment in the while condition:

- while ((lastProcessedEncodingIndex = encodingsToProcess) > 0) {
+ lastProcessedEncodingIndex = encodingsToProcess;
+ while (lastProcessedEncodingIndex > 0) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 116-116: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B


188-189: Ensure proper error propagation

Reassigning the error inside the catch block may lead to loss of the original stack trace. Consider preserving the original error or rethrowing it appropriately.

Ensure that the original error is preserved when rethrowing:

} catch (e) {
- const err = e as ErrorInfo;
- throw new ErrorInfo(
+ throw new ErrorInfo(
    'Error processing the ' + xform + ' encoding, decoder returned ‘' + e.message + '’',
    e.code || 40013,
    400,
  );
}
🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B

src/common/lib/client/realtimepresence.ts (2)

Line range hint 288-303: Add a 'default' case to the switch statement

The switch statement on presence.action lacks a default case. Adding one can help catch unexpected actions and improve code robustness.

Add a default case to handle any unforeseen presence.action values:

      default:
        Logger.logAction(
          this.logger,
          Logger.LOG_ERROR,
          'RealtimePresence.setPresence()',
          'Unexpected presence action: ' + presence.action,
        );
        break;

319-319: Ensure 'presence.action' is defined before emitting

Using non-null assertion presence.action! assumes that presence.action is always defined. Add a null check to prevent possible runtime errors.

Modify the code to check for presence.action:

- this.subscriptions.emit(presence.action!, presence);
+ if (presence.action) {
+   this.subscriptions.emit(presence.action, presence);
+ } else {
+   Logger.logAction(
+     this.logger,
+     Logger.LOG_ERROR,
+     'RealtimePresence.setPresence()',
+     'Presence action is undefined.',
+   );
+ }
src/common/lib/client/realtimechannel.ts (1)

618-639: Optimize message decoding with concurrent processing

Using await inside a for loop leads to sequential execution. Consider using Promise.all to decode messages concurrently, improving performance.

Apply this diff to decode messages concurrently:

- let messages: Message[] = [];
- for (let i = 0; i < encoded.length; i++) {
-   const { decoded, err } = await encoded[i].decodeWithErr(this._decodingContext, this.logger);
-   messages[i] = decoded;
-
-   if (err) {
-     // Error handling...
-   }
- }
+ const decodedResults = await Promise.all(
+   encoded.map((msg) => msg.decodeWithErr(this._decodingContext, this.logger))
+ );
+ const messages = decodedResults.map(({ decoded }) => decoded);
+ // Handle errors from decodedResults if necessary

Ensure that error handling is adjusted appropriately.

src/common/lib/client/modularplugins.ts (1)

8-8: LGTM! Consider adding JSDoc comments.

The refactoring from utility functions to type imports aligns well with the PR objectives, making the interface more type-safe and maintainable.

Consider adding JSDoc comments to document the purpose of these types in the interface:

export interface PresenceMessagePlugin {
  /** Type for user-friendly presence messages */
  PresenceMessage: typeof PresenceMessage;
  /** Type for wire protocol presence messages */
  WirePresenceMessage: typeof WirePresenceMessage;
}

Also applies to: 13-14

src/common/lib/types/protocolmessagecommon.ts (2)

26-29: Make ActionName array construction type-safe.

The current implementation could be made more type-safe using TypeScript features.

-export const ActionName: string[] = [];
-Object.keys(actions).forEach(function (name) {
-  ActionName[(actions as { [key: string]: number })[name]] = name;
-});
+export const ActionName = Object.entries(actions).reduce(
+  (acc, [name, code]) => {
+    acc[code] = name;
+    return acc;
+  },
+  [] as string[]
+);
🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] File contributes 550B to bundle, exceeding allowed maximum of 100B


31-43: Document bitwise operations in flags.

The bitwise operations in flags would benefit from documentation explaining the bit positions and their significance.

 export const flags: { [key: string]: number } = {
   /* Channel attach state flags */
+  // Bits 0-15 reserved for attach state flags
   HAS_PRESENCE: 1 << 0,    // 0x0001
   HAS_BACKLOG: 1 << 1,     // 0x0002
   RESUMED: 1 << 2,         // 0x0004
   TRANSIENT: 1 << 4,       // 0x0010
   ATTACH_RESUME: 1 << 5,   // 0x0020
   /* Channel mode flags */
+  // Bits 16-31 reserved for channel mode flags
   PRESENCE: 1 << 16,       // 0x00010000
   PUBLISH: 1 << 17,        // 0x00020000
   SUBSCRIBE: 1 << 18,      // 0x00040000
   PRESENCE_SUBSCRIBE: 1 << 19, // 0x00080000
 };
🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] File contributes 550B to bundle, exceeding allowed maximum of 100B

src/common/lib/client/restpresencemixin.ts (1)

6-6: Consider renaming underscore-prefixed utility.

The _fromEncodedArray utility appears to be internal but is exported. Consider either:

  1. Removing the underscore prefix if it's meant to be public
  2. Moving it to an internal utilities file if it's truly private

Also, the type assertion could be made safer:

-        ) as Utils.Properties<WirePresenceMessage>[];
+        ) satisfies Utils.Properties<WirePresenceMessage>[];

Also applies to: 31-33

src/common/lib/client/restpresence.ts (1)

34-36: Extract shared type definitions.

This type assertion pattern is duplicated from restpresencemixin.ts. Consider extracting it to a shared type:

// In types/presencemessage.ts
export type DecodedWirePresence = Utils.Properties<WirePresenceMessage>[];

// Then in both files:
) satisfies DecodedWirePresence;
src/common/lib/client/restchannel.ts (1)

Line range hint 83-88: Update error message to match the code.

The error message mentions "single-argument form" but the code handles multiple argument forms (string + data, object, array).

-      throw new ErrorInfo(
-        'The single-argument form of publish() expects a message object or an array of message objects',
-        40013,
-        400,
-      );
+      throw new ErrorInfo(
+        'publish() expects a message object, an array of message objects, or a name and data',
+        40013,
+        400,
+      );
test/realtime/sync.test.js (1)

731-744: Consider adding a comment explaining the action values.

While the code correctly uses numeric action values (2 for 'enter'), it might be helpful to add a comment mapping these numeric values to their corresponding actions for better readability.

 await syncerChannel.processMessage(
   createPM({
     action: 14,
     id: 'messageid:0',
     connectionId: 'connid',
     timestamp: 2000000000000,
     presence: [
       {
+        // action: 2 represents ENTER
         clientId: interrupterClientId,
         action: 2,
       },
     ],
   }),
 );
test/rest/presence.test.js (1)

92-94: Consider adding test coverage for new message handling classes.

While the changes correctly implement the new message encoding approach, consider adding test cases to cover:

  • The new BaseMessage functionality
  • Edge cases in WireMessage and WirePresenceMessage decode/encode paths
  • Error handling scenarios in the new message transformation flow

Would you like me to help generate additional test cases for these scenarios?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d6483a and e84f548.

📒 Files selected for processing (28)
  • ably.d.ts (2 hunks)
  • src/common/lib/client/defaultrealtime.ts (2 hunks)
  • src/common/lib/client/modularplugins.ts (1 hunks)
  • src/common/lib/client/presencemap.ts (3 hunks)
  • src/common/lib/client/realtimechannel.ts (8 hunks)
  • src/common/lib/client/realtimepresence.ts (8 hunks)
  • src/common/lib/client/restchannel.ts (4 hunks)
  • src/common/lib/client/restchannelmixin.ts (2 hunks)
  • src/common/lib/client/restpresence.ts (2 hunks)
  • src/common/lib/client/restpresencemixin.ts (2 hunks)
  • src/common/lib/transport/comettransport.ts (1 hunks)
  • src/common/lib/transport/connectionmanager.ts (1 hunks)
  • src/common/lib/transport/protocol.ts (1 hunks)
  • src/common/lib/transport/transport.ts (1 hunks)
  • src/common/lib/types/basemessage.ts (1 hunks)
  • src/common/lib/types/defaultmessage.ts (2 hunks)
  • src/common/lib/types/defaultpresencemessage.ts (2 hunks)
  • src/common/lib/types/message.ts (6 hunks)
  • src/common/lib/types/presencemessage.ts (4 hunks)
  • src/common/lib/types/protocolmessage.ts (4 hunks)
  • src/common/lib/types/protocolmessagecommon.ts (1 hunks)
  • src/platform/web/modular/presencemessage.ts (2 hunks)
  • src/platform/web/modular/realtimepresence.ts (1 hunks)
  • test/realtime/crypto.test.js (4 hunks)
  • test/realtime/message.test.js (1 hunks)
  • test/realtime/presence.test.js (5 hunks)
  • test/realtime/sync.test.js (7 hunks)
  • test/rest/presence.test.js (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/common/lib/transport/connectionmanager.ts
🧰 Additional context used
🪛 GitHub Actions: Bundle size report
src/common/lib/types/protocolmessagecommon.ts

[error] File contributes 550B to bundle, exceeding allowed maximum of 100B

src/common/lib/types/presencemessage.ts

[error] File contributes 1165B to bundle, exceeding allowed maximum of 100B

src/common/lib/types/basemessage.ts

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B

🪛 Biome (1.9.4)
src/common/lib/types/basemessage.ts

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 93-93: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 116-116: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 136-137: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 15-15: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


[error] 20-20: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-node (16.x)
🔇 Additional comments (35)
src/common/lib/types/protocolmessage.ts (1)

6-8: Ensure actions and related types are correctly updated

The actions and ActionName imports have been adjusted. Verify that all references to these have been updated accordingly throughout the codebase to prevent any mismatches or runtime errors.

src/common/lib/types/message.ts (1)

30-31: Ensure correct defaulting in stringifyAction function

The stringifyAction function uses action || 0 to default undefined actions to 0. If action is 0, which is a valid index, this operation would still return 0. Verify that this logic correctly handles all cases, and consider using a more explicit defaulting mechanism, such as checking for undefined.

src/common/lib/types/basemessage.ts (1)

Line range hint 595-601: Verify compatibility of 'padStart' method

The use of padStart may not be supported in all environments. Ensure that the target environment supports this method or include a polyfill if necessary.

Confirm that the runtime environment supports padStart, or provide an alternative method for padding.

🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 93-93: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 116-116: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 136-137: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 15-15: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


[error] 20-20: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

🪛 GitHub Actions: Bundle size report

[error] File contributes 3389B to bundle, exceeding allowed maximum of 100B

src/common/lib/client/realtimepresence.ts (1)

Line range hint 434-438: Use consistent timestamp source for synthesized messages

When synthesizing leave messages, using Date.now() may lead to discrepancies with server-provided timestamps.

Confirm whether using Date.now() is appropriate, or consider using a consistent timestamp source aligned with server time.

src/common/lib/client/realtimechannel.ts (2)

363-363: Ensure 'modes' are correctly typed

Casting Utils.allToUpperCase(this.channelOptions.modes) to API.ChannelMode[] may mask type issues. Verify that this.channelOptions.modes contains valid API.ChannelMode values.

Ensure that the modes array is properly validated and typed before casting.


479-483: Confirm compatibility of 'WirePresenceMessage' with 'protocolMessageFromValues'

In the sendPresence method, ensure that protocolMessageFromValues correctly handles WirePresenceMessage[] for the presence field.

Verify that protocolMessageFromValues accepts WirePresenceMessage[] and processes it as expected.

src/platform/web/modular/realtimepresence.ts (1)

3-3: LGTM! Clean separation of message types

The changes properly separate the wire protocol message type (WirePresenceMessage) from the public message type (PresenceMessage), aligning with the PR's objective of improving message type handling.

Let's verify that this separation is consistently applied across the codebase:

Also applies to: 7-8

✅ Verification successful

Verified: Wire protocol message type is properly encapsulated

The WirePresenceMessage type is consistently used only in internal implementation files for wire protocol handling and is not exposed in public interfaces, maintaining a clean separation between wire protocol and public message types.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any direct usage of WirePresenceMessage in public interfaces
rg -g '!*.test.*' -g '!**/types/*' "WirePresenceMessage" src/

Length of output: 1618

src/common/lib/types/defaultmessage.ts (2)

19-21: LGTM! Clean delegation to Message.fromValues

The simplified implementation properly delegates to the base class, reducing code duplication.


12-12: Consider adding runtime type validation for WireMessage casting

The type assertions as WireMessage could be unsafe if the input doesn't match the expected structure. Consider adding runtime validation before the cast.

Let's check if there's any existing validation:

Also applies to: 16-16

✅ Verification successful

Type assertion is properly validated through decode pipeline

The as WireMessage type assertion is safe as the encoded message goes through proper validation:

  • WireMessage instantiation via fromValues
  • Comprehensive decode pipeline with error handling
  • Runtime validation during decode process
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for WireMessage type validation
ast-grep --pattern 'function $name($params) {
  $$$
  as WireMessage
  $$$
}'

Length of output: 79


Script:

#!/bin/bash
# Find WireMessage type definition and usage
ast-grep --pattern 'type WireMessage = $_'

# Find fromEncoded implementation
ast-grep --pattern 'function fromEncoded($_) {
  $$$
}'

# Search for validation-related code
rg -A 5 "validateMessage|isValidMessage|checkMessage" --type ts

Length of output: 168


Script:

#!/bin/bash
# Check the content of the file
cat src/common/lib/types/defaultmessage.ts

# Look for message-related validation patterns
rg -A 5 "message.*valid|valid.*message|check.*message" --type ts

# Find type definitions related to messages
ast-grep --pattern 'interface $_Message {
  $$$
}'

Length of output: 1820


Script:

#!/bin/bash
# Check the base Message implementation
cat src/common/lib/types/message.ts

# Look for validation in the message module
rg -A 5 "fromEncoded" --type ts src/common/lib/types/message.ts

Length of output: 7330

src/platform/web/modular/presencemessage.ts (1)

2-2: LGTM! Improved message construction pattern

The change to use PresenceMessage.fromValues directly provides a cleaner and more consistent way to construct presence messages.

Let's verify this pattern is used consistently:

Also applies to: 24-24

✅ Verification successful

Pattern verification successful: fromValues is used consistently

The static factory method pattern using fromValues is consistently implemented across all message and entity types in the codebase, including PresenceMessage, Message, ErrorInfo, and others. The changes align with the established pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining direct fromValues usage
rg "fromValues\(" --type ts src/

Length of output: 5692

src/common/lib/types/defaultpresencemessage.ts (2)

22-24: LGTM! Consistent with DefaultMessage pattern

The implementation follows the same clean delegation pattern as DefaultMessage, maintaining consistency across the codebase.


12-12: Consider adding runtime type validation for WirePresenceMessage casting

Similar to DefaultMessage, the type assertions to WirePresenceMessage should include runtime validation to ensure type safety.

Let's check for existing validation patterns:

Also applies to: 19-19

src/common/lib/types/protocolmessagecommon.ts (1)

1-49: ⚠️ Potential issue

Address bundle size concerns.

The file contributes 550B to the bundle, exceeding the 100B limit. Consider splitting or optimizing the constants.

Let's analyze the impact of different approaches:

🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] File contributes 550B to bundle, exceeding allowed maximum of 100B

src/common/lib/client/restchannelmixin.ts (2)

5-5: LGTM! Import statement updated to use new message types.

The change aligns with the PR's objective of restructuring message types by importing WireMessage instead of WireProtocolMessage.


38-40: LGTM! Type casting updated for better type safety.

The type casting to Utils.Properties<WireMessage>[] aligns with the new message type structure.

src/common/lib/client/defaultrealtime.ts (1)

15-15: LGTM! Updated to use new presence message types.

The changes align with the PR's objective of improving message handling:

  1. Imports now use PresenceMessage and WirePresenceMessage types directly.
  2. Constructor configuration updated to use these types in RealtimePresence.

Also applies to: 38-39

src/common/lib/transport/protocol.ts (1)

1-2: LGTM! Improved modularity by separating protocol message actions.

The change moves actions to a dedicated protocolmessagecommon module while maintaining the core protocol message functionality.

src/common/lib/client/restchannel.ts (3)

7-7: LGTM! Import statements updated for better organization.

The changes improve code organization by:

  1. Using encodeArray for message encoding.
  2. Separating CipherOptions import.

Also applies to: 9-9


75-75: LGTM! Updated to use class methods for message creation.

The changes improve consistency by using Message.fromValues and Message.fromValuesArray class methods instead of imported functions.

Also applies to: 78-78, 81-81


111-111: LGTM! Improved wire message handling.

The changes make wire message handling more explicit:

  1. Encoding messages to wire format.
  2. Size calculation using wire messages.
  3. Serializing wire messages for publishing.

Also applies to: 114-114, 128-128

src/common/lib/client/presencemap.ts (3)

4-4: LGTM! Import statement updated for consistency.

The import statement has been updated to directly import PresenceMessage, aligning with the PR's goal of restructuring message types.


83-85: LGTM! Consistent message creation pattern.

The code now uses PresenceMessage.fromValues() directly, which aligns with the PR's objective of standardizing message creation.


121-123: LGTM! Consistent message creation pattern.

The code maintains consistency in using PresenceMessage.fromValues() for message creation.

src/common/lib/transport/transport.ts (1)

1-1: LGTM! Import source updated for better modularity.

The actions import has been moved to protocolmessagecommon, which better aligns with the separation of common protocol message functionality.

src/common/lib/transport/comettransport.ts (1)

2-2: LGTM! Import source updated consistently.

The actions import has been updated to use protocolmessagecommon, maintaining consistency with other transport-related files.

test/realtime/sync.test.js (1)

73-94: LGTM! Standardized presence message creation.

The code now uses createPM consistently with numeric action values, which provides better type safety and standardization.

Also applies to: 119-140

test/realtime/crypto.test.js (1)

267-268: LGTM! Consistent refactoring of message encoding.

The changes correctly refactor the message encoding from a static method to an instance method, which is a good object-oriented practice.

Let's verify this refactoring is consistent across the codebase:

Also applies to: 290-291, 388-390, 432-434

✅ Verification successful

Refactoring verification successful: All message encoding uses instance method pattern

The codebase shows consistent usage of the instance method pattern for message encoding, with no remaining static Message.encode calls. All test files correctly use the new approach.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining static Message.encode calls that might have been missed
# Expect: No results, as all should be refactored to instance method

rg "Message\.encode\(" --type js

Length of output: 534


Script:

#!/bin/bash
# Find Message class definition and encode method
ast-grep --pattern 'class Message {
  $$$
}'

# Look for other potential static encode patterns
rg "encode\(" --type js -B1

Length of output: 3093

test/realtime/message.test.js (2)

1278-1311: LGTM! Standardized message action values and string representations.

The changes correctly implement the standardized message action values and their string representations:

  • 0 maps to message.create
  • 1 maps to message.update
  • Undefined action defaults to message.create

1319-1329: LGTM! Consistent message creation with version/timestamp handling.

The changes ensure that message creation correctly handles version and timestamp fields, with proper action values (0 for create, 1 for update).

test/realtime/presence.test.js (2)

92-92: Performance improvement: Reduced test timeout.

Reducing the timeout from 60s to 20s is a good optimization as presence operations should complete well within this timeframe.


1636-1644: LGTM! Standardized presence message creation.

The changes consistently use PresenceMessage.fromValues for creating presence messages, with proper action values.

Let's verify this standardization is complete:

Also applies to: 1792-1799

✅ Verification successful

Standardization of PresenceMessage creation verified

The codebase consistently uses PresenceMessage.fromValues for creating presence messages. The only instances of direct constructor usage are within the internal implementation of the PresenceMessage class itself, which is the correct pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any non-standardized presence message creation patterns
# Expect: No direct object creation, all should use fromValues

rg "new PresenceMessage\(" --type js

Length of output: 38


Script:

#!/bin/bash
# Search for any PresenceMessage instantiation patterns
ast-grep --pattern 'new PresenceMessage'

# Search for fromValues usage to confirm standardization
ast-grep --pattern 'PresenceMessage.fromValues'

# Backup regex search for any PresenceMessage related code
rg "PresenceMessage" --type js -A 2

Length of output: 9464

ably.d.ts (2)

2406-2410: Enhancement: Added support for message summaries.

The addition of MESSAGE_SUMMARY type and its integration into MessageAction adds support for annotation summaries.

Also applies to: 2420-2421


878-878: ⚠️ Potential issue

Breaking change: Removed ATTACH_RESUME from ChannelMode.

The removal of ATTACH_RESUME from ChannelMode type is a breaking change that may affect existing code.

Let's check for usage of this mode:

test/rest/presence.test.js (2)

94-94: LGTM! Consistent with the new message handling approach.

The change maintains consistency with the new architecture, using encode() for wire protocol message conversion. The numeric action code 3 for 'leave' is correct.


92-92: LGTM! Verify action codes in the protocol specification.

The change from direct JSON serialization to using encode() aligns with the new message handling architecture. The numeric action code 1 for 'present' matches the protocol specification.

Let's verify the action codes in the protocol specification:

✅ Verification successful

Action code 1 for 'present' state is correct according to the protocol

The presence action code 1 matches the protocol specification where 'present' is defined as the second element (index 1) in the actions array: ['absent', 'present', 'enter', 'leave', 'update'].

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for action code definitions in the codebase
rg -A 10 "enum.*Action|const.*Action" --type ts

Length of output: 1482


Script:

#!/bin/bash
# Search for presence-related action definitions
rg -A 10 "enum.*Presence|const.*presence.*action|presence.*Action" --type ts

# Also search for presence-related files
fd presence --type f --exec rg -l "action.*=.*1|PRESENT.*=.*1"

Length of output: 4745


Script:

#!/bin/bash
# Check the presence test file for action definitions
cat test/realtime/presence.test.js

# Search for protocol-related files with presence actions
rg -A 5 "present.*=.*1|PRESENT.*=.*1|actions.*=.*{" --type ts src/

Length of output: 65787


Script:

#!/bin/bash
# Search for protocol message types and action definitions
rg -A 10 "enum.*Protocol|type.*Protocol|const.*protocol" --type ts src/

# Also try to find presence-specific protocol definitions
fd presence --type f --exec rg "enum|const.*=.*{|type.*=.*{" {}

Length of output: 7976


Script:

#!/bin/bash
# Search for action definitions in protocol and presence files
rg "enum.*Action|const.*actions.*=|type.*Action.*=" src/common/lib/types/

# Also check presence message types
fd "presencemessage|protocolmessage" --type f -d 3 --exec cat {}

Length of output: 393

src/common/lib/types/presencemessage.ts Outdated Show resolved Hide resolved
src/common/lib/types/presencemessage.ts Outdated Show resolved Hide resolved
src/common/lib/types/message.ts Outdated Show resolved Hide resolved
src/common/lib/types/basemessage.ts Outdated Show resolved Hide resolved
src/common/lib/client/realtimepresence.ts Outdated Show resolved Hide resolved
src/common/lib/client/realtimepresence.ts Outdated Show resolved Hide resolved
src/common/lib/client/realtimechannel.ts Outdated Show resolved Hide resolved
src/common/lib/client/realtimechannel.ts Outdated Show resolved Hide resolved
@SimonWoolf SimonWoolf force-pushed the message-types-refactor branch from e84f548 to 9635f30 Compare January 8, 2025 01:41
@SimonWoolf
Copy link
Member Author

@coderabbitai pause

@github-actions github-actions bot temporarily deployed to staging/pull/1941/bundle-report January 8, 2025 01:42 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/features January 8, 2025 01:42 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/typedoc January 8, 2025 01:42 Inactive
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: 0

🧹 Nitpick comments (7)
src/common/lib/types/presencemessage.ts (1)

129-140: Consider enhancing error recovery in decode method.

The current error handling logs the error but continues with potentially incomplete or invalid data. Consider whether it would be more appropriate to:

  1. Return a specific error state in the result
  2. Propagate the error to the caller
  3. Set default/fallback values for failed decoding
src/common/lib/types/basemessage.ts (6)

52-53: Use optional chaining for cleaner null checks

The code can be simplified using optional chaining operator.

Apply this diff to improve readability:

-  if (options && options.cipher) {
+  if (options?.cipher) {
     if (!Crypto) Utils.throwMissingPluginError('Crypto');

-  if (options != null && options.cipher) {
+  if (options?.cipher) {
     return encrypt(msg, options);

Also applies to: 93-94

🧰 Tools
🪛 Biome (1.9.4)

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


100-196: Consider extracting encoding handlers into separate functions

The decode function contains a large switch statement handling different encoding types. Consider extracting each case into a separate handler function for better maintainability.

Example refactor:

const encodingHandlers = {
  base64: (data: any) => Platform.BufferUtils.base64Decode(String(data)),
  'utf-8': (data: any) => Platform.BufferUtils.utf8Decode(data),
  json: (data: any) => JSON.parse(data),
  // ... other handlers
};

function handleEncoding(xform: string, data: any, context: EncodingDecodingContext): any {
  const handler = encodingHandlers[xform];
  if (!handler) throw new Error('Unknown encoding');
  return handler(data);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 116-116: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 136-137: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


198-217: Consider using a discriminated union for message types

The wireToJSON function could benefit from more specific typing to handle different message formats.

Example type definition:

type MessageType = {
  encoding?: string;
  data: string | Buffer | null | undefined;
} & Record<string, any>;

export function wireToJSON(this: MessageType, ...args: any[]): MessageType {
  // ... existing implementation
}

219-241: Enhance error handling in populateFieldsFromParent

Consider providing more context in the error message and handling edge cases.

Apply this diff to improve error handling:

   switch (parent.action) {
     case actions.MESSAGE:
+      if (!parent.messages?.length) {
+        throw new ErrorInfo('No messages found in parent', 40000, 400);
+      }
       msgs = parent.messages!;
       break;
     case actions.PRESENCE:
     case actions.SYNC:
+      if (!parent.presence?.length) {
+        throw new ErrorInfo('No presence messages found in parent', 40000, 400);
+      }
       msgs = parent.presence!;
       break;
     default:
-      throw new ErrorInfo('Unexpected action ' + parent.action, 40000, 400);
+      throw new ErrorInfo(`Unexpected action ${parent.action} in parent message`, 40000, 400);
   }

243-262: Optimize string concatenation in strMsg

Consider using array join or template literals for better performance in string concatenation.

Apply this diff to improve performance:

   let result = '[' + cls;
+  const parts = [];
   for (const attr in m) {
     if (attr === 'data') {
       if (typeof m.data == 'string') {
-        result += '; data=' + m.data;
+        parts.push(`data=${m.data}`);
       } else if (Platform.BufferUtils.isBuffer(m.data)) {
-        result += '; data (buffer)=' + Platform.BufferUtils.base64Encode(m.data);
+        parts.push(`data (buffer)=${Platform.BufferUtils.base64Encode(m.data)}`);
       } else {
-        result += '; data (json)=' + JSON.stringify(m.data);
+        parts.push(`data (json)=${JSON.stringify(m.data)}`);
       }
     } else if (attr && (attr === 'extras' || attr === 'operation')) {
-      result += '; ' + attr + '=' + JSON.stringify(m[attr]);
+      parts.push(`${attr}=${JSON.stringify(m[attr])}`);
     } else if (m[attr] !== undefined) {
-      result += '; ' + attr + '=' + m[attr];
+      parts.push(`${attr}=${m[attr]}`);
     }
   }
-  result += ']';
-  return result;
+  return `${result}${parts.length ? '; ' : ''}${parts.join('; ')}]`;

264-273: Consider using more specific types for BaseMessage properties

The BaseMessage class could benefit from more specific types and documentation.

Apply this diff to improve type definitions:

 export abstract class BaseMessage {
+  /** Unique identifier for the message */
   id?: string;
+  /** Unix timestamp in milliseconds */
   timestamp?: number;
+  /** Client identifier */
   clientId?: string;
+  /** Connection identifier */
   connectionId?: string;
+  /** Message payload */
   data?: any;
+  /** Message encoding format */
   encoding?: string | null;
+  /** Additional metadata */
   extras?: Record<string, unknown>;
+  /** Message size in bytes */
   size?: number;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e84f548 and 9635f30.

📒 Files selected for processing (29)
  • ably.d.ts (2 hunks)
  • scripts/moduleReport.ts (1 hunks)
  • src/common/lib/client/defaultrealtime.ts (2 hunks)
  • src/common/lib/client/modularplugins.ts (1 hunks)
  • src/common/lib/client/presencemap.ts (3 hunks)
  • src/common/lib/client/realtimechannel.ts (9 hunks)
  • src/common/lib/client/realtimepresence.ts (8 hunks)
  • src/common/lib/client/restchannel.ts (4 hunks)
  • src/common/lib/client/restchannelmixin.ts (2 hunks)
  • src/common/lib/client/restpresence.ts (2 hunks)
  • src/common/lib/client/restpresencemixin.ts (2 hunks)
  • src/common/lib/transport/comettransport.ts (1 hunks)
  • src/common/lib/transport/connectionmanager.ts (1 hunks)
  • src/common/lib/transport/protocol.ts (1 hunks)
  • src/common/lib/transport/transport.ts (1 hunks)
  • src/common/lib/types/basemessage.ts (1 hunks)
  • src/common/lib/types/defaultmessage.ts (2 hunks)
  • src/common/lib/types/defaultpresencemessage.ts (2 hunks)
  • src/common/lib/types/message.ts (5 hunks)
  • src/common/lib/types/presencemessage.ts (4 hunks)
  • src/common/lib/types/protocolmessage.ts (4 hunks)
  • src/common/lib/types/protocolmessagecommon.ts (1 hunks)
  • src/platform/web/modular/presencemessage.ts (2 hunks)
  • src/platform/web/modular/realtimepresence.ts (1 hunks)
  • test/realtime/crypto.test.js (4 hunks)
  • test/realtime/message.test.js (1 hunks)
  • test/realtime/presence.test.js (5 hunks)
  • test/realtime/sync.test.js (7 hunks)
  • test/rest/presence.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
  • src/common/lib/client/restpresence.ts
  • src/platform/web/modular/presencemessage.ts
  • src/common/lib/transport/transport.ts
  • src/common/lib/client/presencemap.ts
  • src/common/lib/transport/comettransport.ts
  • src/common/lib/types/defaultpresencemessage.ts
  • src/platform/web/modular/realtimepresence.ts
  • test/rest/presence.test.js
  • src/common/lib/transport/connectionmanager.ts
  • src/common/lib/client/restpresencemixin.ts
  • src/common/lib/client/restchannelmixin.ts
  • src/common/lib/client/modularplugins.ts
  • test/realtime/crypto.test.js
  • src/common/lib/client/restchannel.ts
  • src/common/lib/types/protocolmessagecommon.ts
  • src/common/lib/transport/protocol.ts
  • src/common/lib/client/defaultrealtime.ts
  • src/common/lib/types/defaultmessage.ts
👮 Files not reviewed due to content moderation or server errors (4)
  • src/common/lib/client/realtimepresence.ts
  • test/realtime/sync.test.js
  • src/common/lib/client/realtimechannel.ts
  • test/realtime/message.test.js
🧰 Additional context used
🪛 GitHub Actions: Bundle size report
scripts/moduleReport.ts

[error] Process completed with exit code 1, indicating a failure in the module report generation

🪛 Biome (1.9.4)
src/common/lib/types/basemessage.ts

[error] 52-52: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 93-93: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 116-116: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 136-137: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 15-15: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


[error] 20-20: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
🔇 Additional comments (15)
scripts/moduleReport.ts (1)

267-270: Verify bundle size impact of newly allowed files.

The addition of these files to the allowedFiles set indicates they are expected to contribute significantly to the bundle size. This aligns with the PR's objective of restructuring message types.

Run the following script to analyze the bundle size contribution of the newly added files:

✅ Verification successful

No significant bundle size impact from type definition files

These files contain TypeScript interfaces and type definitions that are removed during compilation, with minimal runtime code impact. The additions to allowedFiles are appropriate for the message type system restructuring.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Analyze bundle size contribution of newly added files.

# Get the bundle size contribution of each file
ast-grep --pattern $'class (BaseMessage|PresenceMessage|WirePresenceMessage|WireMessage) {
  $$$
}'

# Get the total lines of code in each file
rg -U --multiline --multiline-dotall '^.*$' 'src/common/lib/types/basemessage.ts' 'src/common/lib/types/presencemessage.ts' 'src/common/lib/types/protocolmessagecommon.ts'

Length of output: 33095

🧰 Tools
🪛 GitHub Actions: Bundle size report

[error] Process completed with exit code 1, indicating a failure in the module report generation

src/common/lib/types/presencemessage.ts (1)

85-90: LGTM! Improved message encoding.

The encode method correctly transforms the presence message into its wire protocol representation by mapping the action string to its corresponding index.

src/common/lib/types/protocolmessage.ts (2)

37-39: LGTM! Improved error handling.

The error initialization is now more concise and only creates an ErrorInfo instance when needed.


42-51: LGTM! Enhanced type safety with wire protocol messages.

The use of WireMessage and WirePresenceMessage types improves type safety and clarifies the message transformation flow.

src/common/lib/types/message.ts (3)

23-28: LGTM! Added new message action.

The addition of 'message.summary' action aligns with the PR's objective of improving message handling.


153-162: Consider using an enum for the action property.

The action property in WireMessage is typed as number. For better type safety and maintainability, consider using an enum to represent the possible action values.


177-195: LGTM! Robust error handling in decodeWithErr.

The decodeWithErr method provides a comprehensive approach to handling decoding errors:

  • Returns both the decoded message and any error that occurred
  • Logs errors appropriately
  • Continues with field expansion even if decoding fails
test/realtime/presence.test.js (4)

1636-1644: LGTM! Improved presence message creation using fromValues()

The code now uses the more structured PresenceMessage.fromValues() method for creating presence messages, which is the recommended approach.


1792-1799: LGTM! Consistent use of fromValues() for presence messages

The code maintains consistency by using PresenceMessage.fromValues() here as well, with the correct action code.


1955-1968: LGTM! Proper message processing with createPM

The code correctly uses createPM for processing messages with appropriate action and presence data.


92-92: Verify if 20s timeout is sufficient for all test scenarios

The timeout has been reduced from 60s to 20s. While this improves test execution speed, verify that this is sufficient for all test scenarios, especially those involving network operations or multiple presence events.

Run the following script to check for any timeout-related failures in the test history:

✅ Verification successful

20s timeout is sufficient for presence test scenarios

The timeout reduction from 60s to 20s is appropriate. Analysis of the test file shows well-structured async operations with proper promise handling and no complex long-running operations. Most scenarios involve quick network operations that complete within seconds.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check git history for timeout-related test failures
# Look for patterns indicating timeout issues in test logs
rg -i "timeout|timed? out" test/realtime/presence.test.js

Length of output: 159


Script:

#!/bin/bash
# Search for async operations and network calls
rg -i "await|promise|http|request|connect" test/realtime/presence.test.js

Length of output: 18516

ably.d.ts (3)

2406-2410: LGTM! Well-documented MESSAGE_SUMMARY type

The new MESSAGE_SUMMARY type is clearly documented, explaining its purpose for containing rolled-up summaries of annotations.


2420-2421: LGTM! Updated MessageAction type union

The MessageAction type has been correctly updated to include the new MESSAGE_SUMMARY action.


878-878: ⚠️ Potential issue

Breaking change: ATTACH_RESUME removed from ChannelMode

The removal of ATTACH_RESUME from ChannelMode is a breaking change that may affect existing code relying on this functionality.

Run the following script to check for usage of the removed mode:

src/common/lib/types/basemessage.ts (1)

13-24: 🛠️ Refactor suggestion

Define function types explicitly instead of using 'Function'

The CipherOptions type uses Function as a type for encrypt. It's recommended to explicitly define the function's signature to enhance type safety and clarity.

Apply this diff to replace Function with more specific function types:

 export type CipherOptions = {
   channelCipher: {
-    encrypt: Function;
+    encrypt(data: Uint8Array | string): Promise<Uint8Array>;
     algorithm: 'aes';
   };
   cipher?: {
     channelCipher: {
-      encrypt: Function;
+      encrypt(data: Uint8Array | string): Promise<Uint8Array>;
       algorithm: 'aes';
     };
   };
 };

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 15-15: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)


[error] 20-20: Don't use 'Function' as a type.

Prefer explicitly define the function shape. This type accepts any function-like value, which can be a common source of bugs.

(lint/complexity/noBannedTypes)

Copy link

coderabbitai bot commented Jan 8, 2025

✅ Actions performed

Reviews paused.

@SimonWoolf SimonWoolf requested a review from a team January 8, 2025 10:26
@SimonWoolf
Copy link
Member Author

@lawrence-forooghian could you help me understand why Adding constructPresenceMessage to BaseRealtime does not increase the bundle size is an error?

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian could you help me understand why Adding constructPresenceMessage to BaseRealtime does not increase the bundle size is an error?

Will take a look 👍

@lawrence-forooghian
Copy link
Collaborator

lawrence-forooghian commented Jan 8, 2025

@lawrence-forooghian could you help me understand why Adding constructPresenceMessage to BaseRealtime does not increase the bundle size is an error?

The intention of the modular variant of the library is that it provides you with a base client, plus a bunch of additional exports you can import in order to get access to functionality that was intentionally excluded from the base client in order to reduce the bundle size of the base client. So, the thinking behind the diagnostic error that you've encountered was the following: if, given a base client BaseClient (i.e. BaseRealtime or BaseRest) and some other modular export someExport, you find that the size of a bundle containing BaseClient and someExport is the same as one containing just BaseClient, well, then, that means that all of someExport’s code must have been already contained in BaseClient. This means one of two things:

  1. BaseClient is not meant to contain someExport’s code, and someExport’s code is not being tree-shaken correctly
  2. BaseClient is meant to contain someExport’s code, and so there might not be much point in exposing someExport as a separate export in the modular variant (since you don't gain a bundle size reduction by omitting it) (although there is a possible exception here that the diagnostic script doesn't currently consider — it might be that you're exporting someExport as a separate function for some reason other than saving bundle size; e.g. purely because you want it to be exposed as a free-standing function in the public API instead of, say, a static method, but I think that doesn't apply to any of our current free-standing function exports; I think that we gain bundle size savings from all of them)

I suspect that in your case, it's the former. You're getting this error message because you've turned fromValues from a free-standing function into a static method on PresenceMessage; static methods can't be tree-shaken so this means that when BaseClient (transiently) imports the PresenceMessage class, it's now also importing the function which makes up the constructPresenceMessage modular export.

(We did previously use static methods for things like fromValues; I turned them into free-standing functions in 875282c as part of the tree-shaking work.)

@github-actions github-actions bot temporarily deployed to staging/pull/1941/bundle-report January 8, 2025 23:54 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/features January 8, 2025 23:54 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/typedoc January 8, 2025 23:54 Inactive
@SimonWoolf
Copy link
Member Author

aah ok I get it -- so the problem is that it can't prove that importing the whole module doesn't have side effects so it can't remove it, so I can keep the nice api by doing 2b867ad and then it works again. cool, thanks

@SimonWoolf SimonWoolf force-pushed the message-types-refactor branch from 2b867ad to 31b9fb2 Compare January 9, 2025 00:01
@github-actions github-actions bot temporarily deployed to staging/pull/1941/bundle-report January 9, 2025 00:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/features January 9, 2025 00:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/typedoc January 9, 2025 00:02 Inactive
Copy link
Member

@owenpearson owenpearson left a comment

Choose a reason for hiding this comment

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

looks way better :) one minor comment about tree shaking but the rest lgtm

@@ -435,7 +431,7 @@ class RealtimePresence extends EventEmitter {
_synthesizeLeaves(items: PresenceMessage[]): void {
const subscriptions = this.subscriptions;
items.forEach(function (item) {
const presence = presenceMessageFromValues({
const presence = PresenceMessage.fromValues({
Copy link
Member

Choose a reason for hiding this comment

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

I think we switched to standalone functions rather than static methods because that makes them tree-shakable separately from the class, so for example if someone is using the PresenceMessage class but isn't using presenceMessageFromValues then the standalone function isn't imported. I guess this would only happen if someone is using rest presence, since we use fromValues internally here so anyone using realtime presence will be using it either way.

Copy link
Member

Choose a reason for hiding this comment

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

fwiw i think the minified size of that function is probably so small that it doesn't really matter anyway but just fyi

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, but.. yeah, I'm very disinclined to spend any brainpower worrying about that one-line function now being tree-shaken in that specific circumstance

fwiw it looks like the minimal bundle size after this pr is now down about 3/4 of a kilobyte, mostly from improved BaseMessage codesharing and removing some unnecessary decoding abstractions in realtimechannel. I'd argue that worrying overly much about making sure these sort of scraps are tree-shakable while ignoring the low-hanging fruit (neglected unnecessarily long code in non-tree-shakable parts of the lib) is misdirected energy

Copy link
Collaborator

@ttypic ttypic left a comment

Choose a reason for hiding this comment

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

Everything looks good to me. I added a suggestion about using arrow functions in map. I believe it’s a safer approach in js. Even though the chance of it causing issues in our specific use case is very small, I would've done this.

src/common/lib/types/presencemessage.ts Outdated Show resolved Hide resolved
src/common/lib/types/presencemessage.ts Outdated Show resolved Hide resolved
@SimonWoolf SimonWoolf force-pushed the message-types-refactor branch from 31b9fb2 to e1e9966 Compare January 9, 2025 17:41
@github-actions github-actions bot temporarily deployed to staging/pull/1941/bundle-report January 9, 2025 17:42 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/features January 9, 2025 17:42 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1941/typedoc January 9, 2025 17:42 Inactive
@SimonWoolf
Copy link
Member Author

some browser test failures in page_refresh_ but this appears to be a known flake due to multiple frontdoors in sandbox, don't reprod locally with one, so ignoring

@SimonWoolf
Copy link
Member Author

Github appears to be glitching and is no longer showing the changes in the files/commits tab, for reference the commits that made up this PR are:

  • e1e9966 - Fix accidental non-tree-shakability of PresenceMessage
  • 0558fbc - Restructure and clean up message and presencemessage types
  • 52d143a - MessageAction enum changes
  • 888a8b3 - Types: remove ATTACH_RESUME from the list of channel modes
  • d524cea - RealtimeChannel: remove unused _requestedFlags field

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.

4 participants