Skip to content

Conversation

@ChiragAgg5k
Copy link
Member

@ChiragAgg5k ChiragAgg5k commented Oct 24, 2025

This PR contains updates to the Web SDK for version 21.3.0.

Summary by CodeRabbit

  • New Features

    • Realtime service: subscribe to channels, receive messages, automatic reconnect and lifecycle hooks.
  • Deprecations

    • client.subscribe deprecated; Realtime service is the recommended replacement (backwards compatibility retained).
  • Bug Fixes

    • Resolved session handling in realtime flows so session updates behave correctly.
  • Other

    • Documentation and CDN version updated for v21.3.0.

@coderabbitai
Copy link

coderabbitai bot commented Oct 24, 2025

Walkthrough

This PR updates the package to 21.3.0, bumps SDK version strings and docs, deprecates Client.subscribe in favor of a new Realtime service, and refactors Client.config to a stricter typed shape. It changes realtime connection/session resolution to prefer an explicit config.session over localStorage fallback and only includes project in socket queries when present. A new src/services/realtime.ts adds a full WebSocket-based Realtime class with subscription APIs, heartbeat, reconnection/backoff, event dispatching, and lifecycle hooks. The Realtime service is exported from src/index.ts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Areas to focus review on:

  • src/services/realtime.ts: WebSocket lifecycle, heartbeat, backoff/reconnect logic, subscription/unsubscribe edge cases, and error handling.
  • src/client.ts: changes to config typing, session resolution, and createSocket project query behavior.
  • Public API surface: new export in src/index.ts and the deprecated subscribe JSDoc for compatibility.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: Web SDK update for version 21.3.0" accurately identifies the pull request as a feature update that bumps the SDK to version 21.3.0. The title correctly references the version number and communicates that this is a significant update to the Web SDK. However, the term "update" is somewhat generic and doesn't convey the primary technical feature being introduced—the new Realtime service with WebSocket-based subscription capabilities. The title captures a real aspect of the changes (the version number) but doesn't fully highlight the main point from a feature perspective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbe7584 and 3e15fb5.

📒 Files selected for processing (2)
  • src/client.ts (5 hunks)
  • src/services/realtime.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/client.ts (1)
src/services/realtime.ts (1)
  • RealtimeResponseConnected (24-27)
🪛 ast-grep (0.39.6)
src/services/realtime.ts

[warning] 156-163: Message event listeners should validate the origin to prevent XSS attacks. Always check the event origin before processing the message.
Context: this.socket.addEventListener('message', (event: MessageEvent) => {
try {
const message = JSON.parse(event.data) as RealtimeResponse;
this.handleMessage(message);
} catch (error) {
console.error('Failed to parse message:', error);
}
})
Note: [CWE-346] Origin Validation Error [REFERENCES]
- https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

(event-origin-validation)


[warning] 156-163: Message event listener without origin validation detected. Always validate the event.origin property in message event handlers to prevent XSS attacks from malicious frames or windows.
Context: this.socket.addEventListener('message', (event: MessageEvent) => {
try {
const message = JSON.parse(event.data) as RealtimeResponse;
this.handleMessage(message);
} catch (error) {
console.error('Failed to parse message:', error);
}
})
Note: [CWE-346] Origin Validation Error [REFERENCES]
- https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
- https://cwe.mitre.org/data/definitions/346.html

(message-listener-origin-check)

🪛 Biome (2.1.2)
src/client.ts

[error] 537-537: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 543-543: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (11)
src/client.ts (4)

303-315: LGTM! Config structure properly typed.

The explicit typing of the config object with endpoint, endpointRealtime, and an index signature provides better type safety while maintaining flexibility for dynamic properties.


323-323: LGTM! Version bump to 21.3.0.

The SDK version correctly reflects the new release.


480-482: LGTM! Conditional project parameter.

The guard ensures the project query parameter is only included when a valid project ID is configured, avoiding issues with empty or undefined values.


594-596: LGTM! Appropriate deprecation notice.

The deprecation of subscribe in favor of the new Realtime service is clearly documented and aligns with the PR's introduction of the standalone Realtime class.

src/services/realtime.ts (7)

65-67: LGTM! Clean constructor.

The constructor properly stores the client reference for subsequent operations.


75-97: LGTM! Lifecycle callback registration.

The lifecycle hook methods (onError, onClose, onOpen) provide a clean API for consumers to register callbacks for connection events.


99-113: LGTM! Proper heartbeat management.

The heartbeat implementation correctly clears existing timers before starting new ones and validates the socket state before sending ping messages.


115-199: LGTM! Robust WebSocket connection management.

The createSocket method properly:

  • Validates required configuration (project ID)
  • Cleans up existing connections before creating new ones
  • Sets up all necessary event listeners with appropriate error handling
  • Returns a Promise for connection establishment
  • Implements reconnection logic with exponential backoff

Note: The static analysis warning about message origin validation is a false positive—WebSocket messages arrive over a direct TCP connection from the specified endpoint and don't have an origin property like window.postMessage events.


247-325: LGTM! Well-designed subscribe API.

The subscribe method provides excellent developer experience with:

  • Multiple overloads supporting single/multiple channels and typed payloads
  • Debouncing to batch rapid subscriptions and avoid connection churn
  • Clean unsubscribe via the returned close() method
  • Proper async/await usage for connection management

363-388: LGTM! Proper connection authentication.

The handleResponseConnected method correctly implements the authentication flow:

  • Prefers explicit session from config
  • Falls back to cookie-based session from localStorage
  • Only authenticates when session exists and user is not already authenticated
  • Includes error handling for localStorage parsing

This properly addresses the authentication concern raised in past review comments.


398-436: LGTM! Event dispatching logic is sound.

The event handling properly:

  • Validates required fields before processing
  • Filters events to only active channels
  • Dispatches to all subscriptions interested in the event's channels

Note: The timestamp type casting on line 407 is related to the type mismatch issue already flagged in the RealtimeResponseEvent type definition.


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.

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client.ts (1)

530-546: Wrap case body in braces to satisfy noSwitchDeclarations.
Biome flags declarations inside switch cases. Enclose the 'connected' case body in a block.

Apply:

-                    case 'connected':
-                        let session = this.config.session;
-                        if (!session) {
-                            const cookie = JSON.parse(window.localStorage.getItem('cookieFallback') ?? '{}');
-                            session = cookie?.[`a_session_${this.config.project}`];
-                        }
-
-                        const messageData = <RealtimeResponseConnected>message.data;
+                    case 'connected': {
+                        let session = this.config.session;
+                        if (!session) {
+                            const cookie = JSON.parse(window.localStorage.getItem('cookieFallback') ?? '{}');
+                            session = cookie?.[`a_session_${this.config.project}`];
+                        }
+
+                        const messageData = <RealtimeResponseConnected>message.data;
                         if (session && !messageData.user) {
                             this.realtime.socket?.send(JSON.stringify(<RealtimeRequest>{
                                 type: 'authentication',
                                 data: {
                                     session
                                 }
                             }));
                         }
-                        break;
+                        break;
+                    }
🧹 Nitpick comments (4)
CHANGELOG.md (1)

3-10: Add deprecation timeline and migration pointer.
Briefly note expected removal version for client.subscribe and link to a migration snippet using Realtime.subscribe(...) to reduce ambiguity.

src/index.ts (1)

19-20: Export subscription-related types at top-level.
Expose RealtimeSubscription (and optionally RealtimeCode) to avoid deep imports and improve DX.

Apply this minimal addition:

 export { Realtime } from './services/realtime';
+export type { RealtimeSubscription } from './services/realtime';
+export { RealtimeCode } from './services/realtime';
src/client.ts (1)

588-590: Clarify deprecation with link and timeline.
Add a link to the new Realtime usage and note the planned removal version to set expectations.

src/services/realtime.ts (1)

275-312: Simplify subscribe batching using microtasks.
Replace sleep(1)/depth tracking with a microtask batch to reduce timing fragility.

Example:

-        this.subCallDepth++;
-        await this.sleep(this.DEBOUNCE_MS);
-        if (this.subCallDepth === 1) {
-            await this.createSocket();
-        }
-        this.subCallDepth--;
+        if (!this._pendingConnect) {
+            this._pendingConnect = Promise.resolve().then(() => this.createSocket()).finally(() => { this._pendingConnect = undefined; });
+        }
+        await this._pendingConnect;

Add a private _pendingConnect?: Promise<void>;.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1181a97 and dbe7584.

📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • README.md (1 hunks)
  • package.json (1 hunks)
  • src/client.ts (3 hunks)
  • src/index.ts (1 hunks)
  • src/services/realtime.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/services/realtime.ts (2)
src/client.ts (3)
  • RealtimeResponseEvent (810-810)
  • Client (807-807)
  • AppwriteException (807-807)
src/index.ts (4)
  • RealtimeResponseEvent (20-20)
  • Realtime (19-19)
  • Client (8-8)
  • AppwriteException (8-8)
🪛 ast-grep (0.39.6)
src/services/realtime.ts

[warning] 143-150: Message event listener without origin validation detected. Always validate the event.origin property in message event handlers to prevent XSS attacks from malicious frames or windows.
Context: this.socket.addEventListener('message', (event: MessageEvent) => {
try {
const message = JSON.parse(event.data) as RealtimeResponse;
this.handleMessage(message);
} catch (error) {
console.error('Failed to parse message:', error);
}
})
Note: [CWE-346] Origin Validation Error [REFERENCES]
- https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
- https://cwe.mitre.org/data/definitions/346.html

(message-listener-origin-check)


[warning] 143-150: Message event listeners should validate the origin to prevent XSS attacks. Always check the event origin before processing the message.
Context: this.socket.addEventListener('message', (event: MessageEvent) => {
try {
const message = JSON.parse(event.data) as RealtimeResponse;
this.handleMessage(message);
} catch (error) {
console.error('Failed to parse message:', error);
}
})
Note: [CWE-346] Origin Validation Error [REFERENCES]
- https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

(event-origin-validation)

🪛 Biome (2.1.2)
src/client.ts

[error] 531-531: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 537-537: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (4)
README.md (1)

36-36: CDN version bump looks good.

package.json (1)

5-5: Version bump to 21.3.0 is consistent.

src/client.ts (1)

319-321: Header version update is correct.

src/services/realtime.ts (1)

3-10: Confirm intended public surface for types.
If consumers should type close() results, keep RealtimeSubscription exported and re-export it from src/index.ts (see related comment). Otherwise, make it internal to avoid API bloat.

@lohanidamodar lohanidamodar merged commit ef1e00b into main Oct 27, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants