Skip to content

Conversation

@vladvelici
Copy link
Contributor

@vladvelici vladvelici commented Sep 9, 2025

Add clipped and totalClientIds fields to summary types. https://ably.atlassian.net/browse/CHA-1100

https://ably.atlassian.net/browse/CHA-1178

Summary by CodeRabbit

  • New Features

    • Summaries now include explicit clipping indicators and a total distinct-client count so truncated results are clearly reported.
  • Behavior

    • Summary entries default clipping indicators to false when omitted, ensuring consistent summary output for reactions and votes.
  • Documentation

    • Field descriptions clarified to explain clipping semantics and total distinct clientIds.
  • Tests

    • Added unit tests verifying clipped defaults and preservation of explicit clipped values.
  • Chores

    • Relaxed minimal Realtime bundle size thresholds.

@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Walkthrough

Exported SummaryUniqueValues and added clipped/totalClientIds fields to summary-related types; Message.expandFields() now normalizes missing clipped flags for specific summary entry types during expansion. A unit test verifies the normalization behavior and module size thresholds were adjusted.

Changes

Cohort / File(s) Summary of Changes
Type declarations
ably.d.ts
Exported SummaryUniqueValues. Added clipped: boolean to SummaryClientIdList. Added clipped: boolean and totalClientIds: number to SummaryClientIdCounts with JSDoc.
Message model normalization
src/common/lib/types/message.ts
In Message.expandFields() normalize missing clipped to false for summary entry types ending with :distinct.v1, :unique.v1, :multiple.v1 (apply to nested entries) and for :flag.v1 (top-level). No public API signature changes.
Tests
test/realtime/message.test.js
Added test "should ensure clipped field is set to false where not explicitly provided" verifying Message.fromEncoded normalizes clipped across distinct/unique/multiple/flag summary entries; preserves explicit values.
Build script
scripts/moduleReport.ts
Increased minimal useful Realtime bundle thresholds: raw from 103 KiB → 104 KiB and gzip from 31 KiB → 32 KiB.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as Client code
  participant DefaultMessage as DefaultMessage.fromEncoded
  participant Message as Message.expandFields
  participant Summary as Summary entries

  Client->>DefaultMessage: call fromEncoded(encodedMsg)
  DefaultMessage->>Message: construct Message and call expandFields()
  Message->>Summary: iterate summary entries
  alt type ends with :distinct.v1 / :unique.v1 / :multiple.v1
    Note right of Summary#00DD99: nested entries checked\nmissing `clipped` → set false
    Summary-->>Message: set nested entries' `clipped = false` if undefined
  else type ends with :flag.v1
    Note right of Summary#F6C85F: top-level flag normalized\nmissing `clipped` → set false
    Summary-->>Message: set top-level `clipped = false` if undefined
  else
    Summary-->>Message: preserve existing `clipped` if present
  end
  Message-->>DefaultMessage: return normalized Message
  DefaultMessage-->>Client: return Message
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my whiskers, set defaults with care,
Where clipped was missing, I leave none bare.
Counts and lists tidy, totals in sight,
I hop through summaries, making them right.
🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The changes fully satisfy the requirements of CHA-1178 by adding the clipped and totalClientIds fields and associated logic but contain no implementation or tests for the “my reactions” chat SDK method described in CHA-1100. Either implement the missing “my reactions” chat SDK method and related spec tests for CHA-1100 or remove CHA-1100 from the linked issues to ensure the PR only covers its intended scope.
Out of Scope Changes Check ⚠️ Warning Beyond the annotated summary type extensions and normalization logic for clipped defaults, the PR also alters the minimal Realtime bundle size thresholds in scripts/moduleReport.ts, which is unrelated to the linked issues and the stated objective of adding clipped and totalClientIds fields. Remove the bundle size threshold adjustment from this PR or split it into a separate change to keep the scope focused on the clipped and totalClientIds enhancements.
✅ 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 identifies the addition of the clipped field to the annotation summary types, which represents a core aspect of the pull request’s functional changes, aligning with the main modifications in type declarations and logic. Although it doesn’t mention the totalClientIds addition, it remains specific and descriptive rather than being generic or off-topic.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-clipped-summary-field

📜 Recent review details

Configuration used: CodeRabbit UI

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 354a6ae and 61cf320.

📒 Files selected for processing (1)
  • scripts/moduleReport.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • scripts/moduleReport.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-node (18.x)

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/2078/bundle-report September 9, 2025 07:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/features September 9, 2025 07:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/typedoc September 9, 2025 07:49 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 (1)
ably.d.ts (1)

3148-3151: Clipped flag on SummaryClientIdList looks good; consider a tiny doc tweak

Doc is clear. Optional nit: call out that clientIds may be a subset when clipped.

Apply to tighten the JSDoc:

-  /** Whether the list of clientIds has been clipped due to exceeding the maximum number of
-   * clients. */
+  /** Whether the list of clientIds has been clipped due to exceeding the maximum number of
+   * clients. When true, `clientIds` contains only a subset; use `total` for the full count. */
   clipped?: boolean;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • 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 af2ce0e and c8d861e.

📒 Files selected for processing (1)
  • ably.d.ts (2 hunks)
🧰 Additional context used
🪛 GitHub Check: lint
ably.d.ts

[warning] 3165-3165:
Expected JSDoc block to be aligned

🪛 GitHub Actions: Lint
ably.d.ts

[warning] 1-1: Prettier formatting issues found in ably.d.ts. Run 'prettier --write' to fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: test-npm-package
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-browser (webkit)
🔇 Additional comments (2)
ably.d.ts (2)

1-1: Fix Prettier formatting to unblock lint pipeline

CI reports Prettier issues in ably.d.ts. Please run the project formatter (e.g., pnpm format or prettier --write ably.d.ts) to normalize spacing, including the JSDoc alignment noted above.


3165-3169: Align JSDoc formatting and clarify totalClientIds wording

  /** Whether the list of clientIds has been clipped due to exceeding the maximum number of
-    * clients. */
+   * clients. */
   clipped?: boolean;
- /** The total number of distinct clientIds in the map (equal to length of map if clipped is false). */
+ /** The total number of distinct identified clientIds in `clientIds`
+  * (equals `Object.keys(clientIds).length` when `clipped` is false). Excludes
+  * unidentified contributions counted in `totalUnidentified`. */
   totalClientIds: number;

Confirm the service always provides totalClientIds in production payloads (otherwise make it optional to avoid a breaking change).

@vladvelici vladvelici requested a review from AndyTWF September 9, 2025 14:03
ably.d.ts Outdated
clientIds: string[];
/** Whether the list of clientIds has been clipped due to exceeding the maximum number of
* clients. */
clipped?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this likely to become non-nullable in the future? Would it be worth making it non-nullable and then having it just forced to false in expandFields()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's omitempty and that's a good thing I'd say, so not sure we'll ever explicitly set clipped: false?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this will be mainly driven by what other languages will do and how much faff it introduces there - in languages like Go and C# it would require null checks to do a simple if (clipped).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't imagine in Go we will make it

clipped *bool `json:"omitempty"`

but rather

clipped bool `json:"omitempty"`

For languages where it matters there's no point to have a third "this wasn't set" state, just use false, but in JS this maps to the JSON representation and all checks are identical (if (x.clipped) {} is the same even if clipped field is optional).

Copy link
Contributor

Choose a reason for hiding this comment

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

I went away and thought about it for a bit and my gut feeling is that the field should match its semantics. There's no current case for unset here (we always know if its clipped) so I think it should be a non-nullable boolean.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with Andy. We should use a nullable type in the client API if and only if it makes semantic sense for it to be nullable, i.e. it can be unset. And whichever way we decide, we should be consistent about it between SDKs.

So a Message might or might not have a clientId: if it was published by an unidentified client, it just won't have one. So it is right for that field to be nullable. But in the case of 'clipped', there's no semantic 'unset' distinct from false, if the summary isn't clipped it should just say false.

in JS this maps to the JSON representation

This is not a strong consideration for the question of what the SDK type should be.There are plenty of fields we don't populate on the wire to save space but are still populated (and typed as such) by the SDK when a message arrives, like message.timestamp/id/connectionId (populated by the sdk from the ChannelMessage), or the version from the serial for CREATEs, etc. The SDK is expected to unpack the wire representation a bit to present a nicer API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated it to be explicitly set

@vladvelici vladvelici force-pushed the add-clipped-summary-field branch from c8d861e to b70acb7 Compare September 10, 2025 11:07
@github-actions github-actions bot temporarily deployed to staging/pull/2078/features September 10, 2025 11:08 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/bundle-report September 10, 2025 11:08 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/typedoc September 10, 2025 11:08 Inactive
@vladvelici vladvelici requested a review from AndyTWF September 10, 2025 11:55
@github-actions github-actions bot temporarily deployed to staging/pull/2078/bundle-report September 16, 2025 15:04 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/features September 16, 2025 15:04 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/typedoc September 16, 2025 15:04 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 (4)
src/common/lib/types/message.ts (2)

124-139: Default ‘clipped’ only when missing (avoid generic falsy checks)

Use undefined checks so explicit false is preserved without redundant writes.

Apply:

-        if (type.endsWith(':distinct.v1') || type.endsWith(':unique.v1') || type.endsWith(':multiple.v1')) {
-          for (const [, entry] of Object.entries(summaryEntry)) {
-            if (!entry.clipped) {
-              entry.clipped = false;
-            }
-          }
-        } else if (type.endsWith(':flag.v1')) {
-          if (!(summaryEntry as API.SummaryClientIdList).clipped) {
-            (summaryEntry as API.SummaryClientIdList).clipped = false;
-          }
-        }
+        if (type.endsWith(':distinct.v1') || type.endsWith(':unique.v1') || type.endsWith(':multiple.v1')) {
+          for (const [, entry] of Object.entries(summaryEntry)) {
+            if (entry.clipped === undefined) {
+              entry.clipped = false;
+            }
+          }
+        } else if (type.endsWith(':flag.v1')) {
+          if ((summaryEntry as API.SummaryClientIdList).clipped === undefined) {
+            (summaryEntry as API.SummaryClientIdList).clipped = false;
+          }
+        }

If TS target supports it, entry.clipped ??= false; is even cleaner.


124-139: Future‑proof type detection

String suffix checks couple behavior to versioned keys. Consider a shape/type‑guard approach (e.g., detect a dict of entries vs a flat list object) or narrow to a common value type (e.g., Record<string, { clipped?: boolean }>), to make v2/vN extensions safer.

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

1371-1371: Remove debug logging from test

console.log(message.summary); adds noise to CI output.

Apply:

-        console.log(message.summary);
+        // no-op

1430-1431: Clean up commented assertion

Leftover debug line can be dropped.

Apply:

-        // expect(false).to.equal(true);
📜 Review details

Configuration used: CodeRabbit UI

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 b70acb7 and 052a0f0.

📒 Files selected for processing (3)
  • ably.d.ts (3 hunks)
  • src/common/lib/types/message.ts (2 hunks)
  • test/realtime/message.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ably.d.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/common/lib/types/message.ts (1)
ably.d.ts (2)
  • SummaryEntry (3180-3185)
  • SummaryClientIdList (3141-3151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-browser (firefox)
  • GitHub Check: test-browser (webkit)
🔇 Additional comments (3)
src/common/lib/types/message.ts (2)

124-139: In‑place mutation — confirm invariants

expandFields() mutates this.summary (and nested entries). Given WireMessage.decodeWithErr shallow‑copies from the encoded values, this also mutates the caller‑provided object. Confirm this is intended and won’t surprise integrators relying on the original reference.


111-111: Change not applied — Message.summary still typed as any; update types, regenerate d.ts, audit call sites

  • src/common/lib/types/message.ts:170 still has summary?: any.
  • ably.d.ts references message.summary — regenerate the typings after changing the source type to Record<string, API.SummaryEntry>.
  • Audit Message.fromValues usages in src/common/lib/client/restchannel.ts and src/common/lib/client/realtimechannel.ts for code that assumes any.

Likely an incorrect or invalid review comment.

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

1334-1431: Coverage for defaulting ‘clipped’ looks good

The assertions thoroughly exercise distinct/unique/multiple/flag cases and preserve explicit trues. Nice.

this.createdAt = this.timestamp;
}
}
if (this.summary) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we don't have a spec entry for this behaviour, we should add one (and add a comment here)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

expect(updateMessage.serial).to.equal(undefined);
});

it('should ensure clipped field is set to false where not explicitly provided', async function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto re a spec comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@vladvelici vladvelici force-pushed the add-clipped-summary-field branch from 052a0f0 to 51ca01a Compare September 22, 2025 08:43
@vladvelici vladvelici requested a review from AndyTWF September 22, 2025 08:43
@github-actions github-actions bot temporarily deployed to staging/pull/2078/bundle-report September 22, 2025 08:44 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/typedoc September 22, 2025 08:44 Inactive
@vladvelici vladvelici force-pushed the add-clipped-summary-field branch from 51ca01a to 354a6ae Compare September 22, 2025 08:46
@vladvelici
Copy link
Contributor Author

Added spec points reference, spec points are in this PR: ably/specification#382

@github-actions github-actions bot temporarily deployed to staging/pull/2078/features September 22, 2025 08:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/bundle-report September 22, 2025 08:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2078/typedoc September 22, 2025 08:47 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 (1)
src/common/lib/types/message.ts (1)

142-143: Consider using explicit undefined checks for better clarity.

The current condition !entry.clipped will be truthy for both undefined and false values, which means it would incorrectly override an explicitly set false value. Consider using explicit undefined checks to ensure only missing fields are normalized.

Apply this diff to use explicit undefined checks:

-            if (!entry.clipped) {
+            if (entry.clipped === undefined) {
               entry.clipped = false;
             }
-          if (!(summaryEntry as API.SummaryClientIdList).clipped) {
+          if ((summaryEntry as API.SummaryClientIdList).clipped === undefined) {
             (summaryEntry as API.SummaryClientIdList).clipped = false;
           }

Also applies to: 148-149

📜 Review details

Configuration used: CodeRabbit UI

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 052a0f0 and 354a6ae.

📒 Files selected for processing (3)
  • ably.d.ts (3 hunks)
  • src/common/lib/types/message.ts (1 hunks)
  • test/realtime/message.test.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/realtime/message.test.js
  • ably.d.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/common/lib/types/message.ts (1)
ably.d.ts (1)
  • SummaryClientIdList (3143-3153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-node (20.x)
  • GitHub Check: test-node (16.x)
  • GitHub Check: test-node (18.x)
  • GitHub Check: test-browser (webkit)
  • GitHub Check: test-browser (chromium)
  • GitHub Check: test-browser (firefox)
🔇 Additional comments (2)
src/common/lib/types/message.ts (2)

136-153: LGTM! Correct implementation of clipping defaults normalization.

The logic correctly implements the normalization behavior for different summary entry types:

  • For entries ending with :distinct.v1, :unique.v1, or :multiple.v1, it traverses nested entries and sets clipped to false if not explicitly defined
  • For entries ending with :flag.v1, it sets the top-level clipped field to false if not explicitly defined

The implementation aligns with the spec references (TM7c1c, TM7d1c) and matches the expected behavior described in the AI summary.


140-141: Resolve — iteration is type-safe: 'clipped' exists on mapped entry types.

ably.d.ts defines SummaryDistinctValues/SummaryUniqueValues as Record<string, SummaryClientIdList> and SummaryMultipleValues as Record<string, SummaryClientIdCounts>, and both SummaryClientIdList and SummaryClientIdCounts declare clipped: boolean; the loop in src/common/lib/types/message.ts (lines 137–146) is safe — no changes needed.

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