Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

language: en-US

reviews:
# Review draft PRs so we get early feedback
drafts: true
Comment on lines +5 to +7
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misplaced drafts property at the reviews level.

According to the CodeRabbit configuration schema, the drafts property belongs under reviews.auto_review.drafts, not directly under reviews. The schema specifies "additionalProperties": false for the reviews object, so this property will be ignored.

Since you already have drafts: true correctly placed under auto_review (line 21), you can safely remove line 7.

Proposed fix
 reviews:
-  # Review draft PRs so we get early feedback
-  drafts: true
-
   # Enable high-level summary of changes
   high_level_summary: true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reviews:
# Review draft PRs so we get early feedback
drafts: true
reviews:
# Enable high-level summary of changes
high_level_summary: true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.coderabbit.yaml around lines 5 - 7, The reviews block contains a misplaced
property: remove the top-level drafts entry under reviews (the stray "drafts:
true" currently directly beneath the reviews key) and rely on the existing
drafts setting under reviews.auto_review (auto_review.drafts) instead;
specifically delete the reviews-level "drafts" property so only
reviews.auto_review.drafts remains.


# Enable high-level summary of changes
high_level_summary: true

# Add a poem... just kidding, disable it
poem: false

# Collapse walkthrough to keep PR comments clean
collapse_walkthrough: true

# Auto-review on every push
auto_review:
enabled: true
drafts: true

# Path-based review instructions
path_instructions:
- path: "src/core/**"
instructions: |
This is the core SDK module. Pay close attention to:
- Backward compatibility of public API changes
- Proper TypeScript typing (strict mode is enabled)
- No platform-specific code (Node.js, Web, React Native specifics belong in their respective platform directories)
- Thread safety considerations for shared state
- path: "src/core/endpoints/**"
instructions: |
These are REST API endpoint implementations. Review for:
- Correct request/response type definitions
- Proper error handling and status code mapping
- Consistent parameter validation
- Adherence to PubNub REST API contracts
- path: "src/core/types/**"
instructions: |
TypeScript type definitions. Ensure:
- Types are precise and not overly permissive (avoid `any`)
- Exported types maintain backward compatibility
- Proper use of generics and utility types
- path: "src/event-engine/**"
instructions: |
State-machine-based subscription management. Review for:
- Correct state transitions and edge cases
- No leaked subscriptions or event listeners
- Proper cleanup on state exit
- path: "src/entities/**"
instructions: |
High-level subscription API (Channel, ChannelGroup, etc.). Review for:
- Proper event handler lifecycle management
- Memory leak prevention (listener cleanup)
- path: "src/transport/**"
instructions: |
Platform-specific HTTP transport implementations. Review for:
- Proper timeout and cancellation handling
- Correct header management
- Error propagation consistency across platforms
- path: "src/node/**"
instructions: "Node.js platform implementation. Ensure no browser/DOM APIs are used."
- path: "src/web/**"
instructions: "Browser platform implementation. Ensure no Node.js-specific APIs (fs, crypto, etc.) are used."
- path: "src/react_native/**"
instructions: "React Native platform implementation. Verify compatibility with RN runtime."
- path: "test/**"
instructions: |
Test files. Review for:
- Adequate coverage of edge cases
- Proper use of mocks/stubs (Sinon + Nock)
- No flaky patterns (hardcoded timeouts, race conditions)
- Tests that actually assert meaningful behavior

# Tools configuration
tools:
# Enable GitHub checks integration
github-checks:
enabled: true
timeout_ms: 120000

chat:
auto_reply: true

# Path filters - ignore generated/vendored/build output
path_filters:
# Build outputs
- "!dist/**"
- "!lib/**"
- "!upload/**"

# Dependencies
- "!node_modules/**"

# Unrelated working directories (not part of the SDK source)
- "!dataSync/**"
- "!new-dataSync/**"
- "!demo-app/**"
- "!loan_quote/**"
- "!docs/**"

# Lock files - too noisy, low review value
- "!package-lock.json"

# Config/metadata that rarely needs review
- "!.pubnub.yml"
- "!.vscode/**"

# Generated type bundles
- "!lib/types/**"
6 changes: 5 additions & 1 deletion src/core/components/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@ export abstract class AbstractRequest<ResponseType, ServiceResponse extends obje
if (headers) request.headers = headers;

// Attach body (if required).
if (request.method === TransportMethod.POST || request.method === TransportMethod.PATCH) {
if (
request.method === TransportMethod.POST ||
request.method === TransportMethod.PATCH ||
request.method === TransportMethod.PUT
) {
const [body, formData] = [this.body, this.formData];
if (formData) request.formData = formData;
if (body) request.body = body;
Expand Down
4 changes: 4 additions & 0 deletions src/core/types/transport-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export enum TransportMethod {
* Request will be sent using `PATCH` method.
*/
PATCH = 'PATCH',
/**
* Request will be sent using `PUT` method.
*/
PUT = 'PUT',
Comment on lines +22 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check signature method in middleware.ts for PUT handling

rg -n -A 15 'signature\(req: TransportRequest\)' src/transport/

Repository: pubnub/javascript

Length of output: 1212


Signature calculation must include PUT request bodies to prevent authentication failures.

The PUT enum addition is correct, but the signature middleware at src/transport/middleware.ts:69-80 only includes request body in signature calculation for POST and PATCH methods. PUT requests with bodies will have their body excluded from the signature input, causing authentication validation failures.

Update the condition to include TransportMethod.PUT:

if (method === TransportMethod.POST || method === TransportMethod.PATCH || method === TransportMethod.PUT) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/types/transport-request.ts` around lines 22 - 25, The signature
middleware currently only includes request bodies for POST and PATCH when
computing the signature, causing PUT bodies to be omitted; update the
method-check in the signature calculation (the conditional that compares method
to TransportMethod.POST / TransportMethod.PATCH) to also include
TransportMethod.PUT so that requests where method === TransportMethod.PUT
include the body in the signature input.

/**
* Request will be sent using `DELETE` method.
*/
Expand Down
52 changes: 52 additions & 0 deletions src/errors/pubnub-api-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,29 @@ export class PubNubAPIError extends Error {
) {
errorData = errorResponse;
status = errorResponse.status;
} else if (
'errors' in errorResponse &&
Array.isArray(errorResponse.errors) &&
errorResponse.errors.length > 0
) {
// Handle DataSync-style structured error responses:
// { errors: [{ errorCode: "SYN-0008", message: "...", path: "/id" }] }
errorData = errorResponse;

const errors = errorResponse.errors as Array<{
errorCode?: string;
message?: string;
path?: string;
}>;

message = errors
.map((e) => {
const parts: string[] = [];
if (e.errorCode) parts.push(e.errorCode);
if (e.message) parts.push(e.message);
return parts.join(': ');
})
.join('; ');
} else errorData = errorResponse;
Comment on lines +146 to 169
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle edge case where error items lack both errorCode and message.

If all items in the errors array have only path (no errorCode or message), the resulting message would be empty strings joined by '; ', producing unhelpful output like "; " or an empty string.

Consider filtering out empty results:

Proposed fix
                 message = errors
                   .map((e) => {
                     const parts: string[] = [];
                     if (e.errorCode) parts.push(e.errorCode);
                     if (e.message) parts.push(e.message);
                     return parts.join(': ');
                   })
+                  .filter((s) => s.length > 0)
                   .join('; ');
+
+                if (!message) message = 'Unknown error';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/errors/pubnub-api-error.ts` around lines 146 - 169, When building the
message from the DataSync-style errors (the block that reads
errorResponse.errors and sets message), skip error items that have neither
errorCode nor message so you don't produce empty tokens; specifically, update
the logic around errors (the const errors = ... and the subsequent message =
errors.map(...).join(...)) to either filter out mapped parts that are empty or
filter out error objects where both e.errorCode and e.message are falsy before
mapping, and then join the remaining non-empty strings into message while
leaving errorData assignment unchanged.


if ('error' in errorResponse && errorResponse.error instanceof Error) errorData = errorResponse.error;
Expand Down Expand Up @@ -229,6 +252,35 @@ export class PubNubAPIError extends Error {
};
}

/**
* Format a user-facing error message for this API error.
*
* When the error contains structured details extracted from the service response
* (e.g., DataSync `errors` array), those details are included in the message.
* Otherwise, falls back to a generic description.
*
* @param operation - Request operation during which error happened.
*
* @returns Formatted error message string.
*/
public toFormattedMessage(operation: RequestOperation): string {
const fallback = 'REST API request processing error, check status for details';

// When errorData contains a structured `errors` array, `this.message` was already
// constructed from it in `createFromServiceResponse` — prefer it over the generic fallback.
if (
this.errorData &&
typeof this.errorData === 'object' &&
!('name' in this.errorData && 'message' in this.errorData && 'stack' in this.errorData) &&
'errors' in this.errorData &&
Array.isArray((this.errorData as Record<string, unknown>).errors)
) {
return `${operation}: ${this.message}`;
}

return fallback;
}

/**
* Convert API error object to PubNub client error object.
*
Expand Down
Loading