Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR introduces four new space management commands ( Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/commands/spaces/get.ts (2)
128-158: Consider extracting member formatting to avoid duplication withformatMemberBlock.The inline formatting here closely mirrors
formatMemberBlockfromsrc/utils/spaces-output.ts(context snippet 1). While the input types differ (MemberInfovsSpaceMember), the output structure is identical.You could either:
- Create a shared interface that both types satisfy for formatting purposes, or
- Accept this duplication given the REST-based approach in this command
This is a minor refactor opportunity that can be deferred.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/get.ts` around lines 128 - 158, The member-printing loop duplicates the output of formatMemberBlock; refactor by creating a small adapter that converts the REST MemberInfo shape used in the get command to the shape expected by formatMemberBlock (or introduce a shared interface both MemberInfo and SpaceMember implement), then replace the inline logging inside the loop with a single call to formatMemberBlock(memberAdapter) to centralize formatting and remove duplication; reference the loop in the get command, the types MemberInfo and SpaceMember, and the existing formatMemberBlock utility when making the change.
17-17: ExtractSPACE_CHANNEL_TAGto a shared constant file.This constant is defined in three files:
src/commands/spaces/get.ts,src/commands/spaces/occupancy/get.ts, andsrc/commands/spaces/occupancy/subscribe.ts. Extract it tosrc/utils/spaces-constants.tsto prevent divergence if the suffix changes.♻️ Suggested refactor
Create a shared constant file:
// src/utils/spaces-constants.ts export const SPACE_CHANNEL_TAG = "::$space";Then import it in all three files:
-const SPACE_CHANNEL_TAG = "::$space"; +import { SPACE_CHANNEL_TAG } from "../../utils/spaces-constants.js";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/spaces/get.ts` at line 17, Extract the repeated constant SPACE_CHANNEL_TAG into a single shared module (e.g., export const SPACE_CHANNEL_TAG = "::$space" from a new spaces-constants module) and replace the local declarations in each file with an import from that module; update the three places where SPACE_CHANNEL_TAG is currently defined to import the shared constant so the suffix is maintained in one location.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/commands/spaces/members/get-all.ts`:
- Around line 35-38: Remove clientIdFlag from the static flags definition on the
get-all command: update the static override flags object (the "static override
flags" declaration in the get-all command class) to only spread productApiFlags
and remove ...clientIdFlag; also search the same class for any references to
clientIdFlag and delete or adapt them so the command remains a read-only query
using only productApiFlags.
In `@src/commands/spaces/subscribe.ts`:
- Around line 42-43: The listener stored in the property "listener" is
subscribed via this.space!.subscribe("update", this.listener) but never removed;
override or extend the class's finally() method (the one in SpacesBaseCommand or
the command class that sets this.listener) to, if this.space and this.listener
are non-null, call this.space.unsubscribe("update", this.listener), then set
this.listener = null to allow GC; ensure the unsubscribe call is guarded (check
this.space and this.listener) and placed before calling super.finally() if
applicable.
---
Nitpick comments:
In `@src/commands/spaces/get.ts`:
- Around line 128-158: The member-printing loop duplicates the output of
formatMemberBlock; refactor by creating a small adapter that converts the REST
MemberInfo shape used in the get command to the shape expected by
formatMemberBlock (or introduce a shared interface both MemberInfo and
SpaceMember implement), then replace the inline logging inside the loop with a
single call to formatMemberBlock(memberAdapter) to centralize formatting and
remove duplication; reference the loop in the get command, the types MemberInfo
and SpaceMember, and the existing formatMemberBlock utility when making the
change.
- Line 17: Extract the repeated constant SPACE_CHANNEL_TAG into a single shared
module (e.g., export const SPACE_CHANNEL_TAG = "::$space" from a new
spaces-constants module) and replace the local declarations in each file with an
import from that module; update the three places where SPACE_CHANNEL_TAG is
currently defined to import the shared constant so the suffix is maintained in
one location.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: fabbcfdc-be62-4728-9cfb-dd91c60baabe
📒 Files selected for processing (12)
README.mdsrc/commands/spaces/create.tssrc/commands/spaces/get.tssrc/commands/spaces/index.tssrc/commands/spaces/members/get-all.tssrc/commands/spaces/subscribe.tssrc/utils/spaces-output.tstest/helpers/mock-ably-spaces.tstest/unit/commands/spaces/create.test.tstest/unit/commands/spaces/get.test.tstest/unit/commands/spaces/members/get-all.test.tstest/unit/commands/spaces/subscribe.test.ts
👮 Files not reviewed due to content moderation or server errors (8)
- test/helpers/mock-ably-spaces.ts
- src/commands/spaces/index.ts
- src/utils/spaces-output.ts
- README.md
- test/unit/commands/spaces/subscribe.test.ts
- test/unit/commands/spaces/create.test.ts
- src/commands/spaces/create.ts
- test/unit/commands/spaces/members/get-all.test.ts
| static override flags = { | ||
| ...productApiFlags, | ||
| ...clientIdFlag, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if other read-only spaces commands (like occupancy get) include clientIdFlag
rg -A5 'static override flags' src/commands/spaces/occupancy/get.ts src/commands/spaces/get.tsRepository: ably/ably-cli
Length of output: 744
Remove clientIdFlag from the flags definition.
Read-only query commands like spaces get and spaces occupancy get use only productApiFlags. Since get-all is a read-only query that doesn't perform an operation requiring client identity control, clientIdFlag should be removed to keep the flag surface minimal and consistent with the existing pattern.
static override flags = {
...productApiFlags,
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/commands/spaces/members/get-all.ts` around lines 35 - 38, Remove
clientIdFlag from the static flags definition on the get-all command: update the
static override flags object (the "static override flags" declaration in the
get-all command class) to only spread productApiFlags and remove
...clientIdFlag; also search the same class for any references to clientIdFlag
and delete or adapt them so the command remains a read-only query using only
productApiFlags.
| private listener: ((spaceState: { members: SpaceMember[] }) => void) | null = | ||
| null; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify if space.unsubscribe is called anywhere in SpacesBaseCommand or related cleanup
rg -n 'space\.unsubscribe' src/Repository: ably/ably-cli
Length of output: 39
🏁 Script executed:
cat -n src/commands/spaces/subscribe.tsRepository: ably/ably-cli
Length of output: 3840
🏁 Script executed:
cat -n src/spaces-base-command.tsRepository: ably/ably-cli
Length of output: 16879
🏁 Script executed:
rg -n "finally" src/commands/spaces/subscribe.tsRepository: ably/ably-cli
Length of output: 39
Listener is stored but never unsubscribed—resource leak in cleanup.
The this.listener reference is registered at line 96 via this.space!.subscribe("update", this.listener), but SpacesBaseCommand.finally() does not call this.space.unsubscribe("update", ...). The listener will persist after the command exits, preventing garbage collection and causing memory leaks during long-running CLI sessions.
Override finally() to clean up the space "update" listener:
🛠️ Proposed fix
+ async finally(error: Error | undefined): Promise<void> {
+ if (this.space && this.listener) {
+ try {
+ this.space.unsubscribe("update", this.listener);
+ } catch (e) {
+ this.debug(`Failed to unsubscribe from space update: ${e}`);
+ }
+ }
+ await super.finally(error);
+ }📝 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.
| private listener: ((spaceState: { members: SpaceMember[] }) => void) | null = | |
| null; | |
| private listener: ((spaceState: { members: SpaceMember[] }) => void) | null = | |
| null; | |
| async finally(error: Error | undefined): Promise<void> { | |
| if (this.space && this.listener) { | |
| try { | |
| this.space.unsubscribe("update", this.listener); | |
| } catch (e) { | |
| this.debug(`Failed to unsubscribe from space update: ${e}`); | |
| } | |
| } | |
| await super.finally(error); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/commands/spaces/subscribe.ts` around lines 42 - 43, The listener stored
in the property "listener" is subscribed via this.space!.subscribe("update",
this.listener) but never removed; override or extend the class's finally()
method (the one in SpacesBaseCommand or the command class that sets
this.listener) to, if this.space and this.listener are non-null, call
this.space.unsubscribe("update", this.listener), then set this.listener = null
to allow GC; ensure the unsubscribe call is guarded (check this.space and
this.listener) and placed before calling super.finally() if applicable.
There was a problem hiding this comment.
Pull request overview
Adds missing Ably Spaces CLI commands to create/inspect/subscribe to spaces and to retrieve all space members, along with supporting mocks, output tweaks, tests, and README docs.
Changes:
- Introduces new commands:
spaces create,spaces get,spaces subscribe, andspaces members get-all. - Extends the Spaces test mocks and adds unit tests covering JSON and non-JSON output plus error cases.
- Updates human-readable member output to separate “Last Event” from its timestamp, and documents the new commands in the README.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/commands/spaces/subscribe.test.ts | Adds unit coverage for spaces:subscribe output, JSON mode, and error handling. |
| test/unit/commands/spaces/members/get-all.test.ts | Adds unit coverage for spaces:members:get-all including NDJSON envelopes and non-JSON display. |
| test/unit/commands/spaces/get.test.ts | Adds unit coverage for REST-backed spaces:get behavior and parsing. |
| test/unit/commands/spaces/create.test.ts | Adds unit coverage for spaces:create including JSON envelope and failure path. |
| test/helpers/mock-ably-spaces.ts | Extends Spaces SDK mocks with space-level subscribe/unsubscribe/state support. |
| src/utils/spaces-output.ts | Adjusts member block formatting to output event type and timestamp on separate lines. |
| src/commands/spaces/subscribe.ts | Implements spaces subscribe command to stream space update events. |
| src/commands/spaces/members/get-all.ts | Implements spaces members get-all command to retrieve all members without entering. |
| src/commands/spaces/index.ts | Updates topic examples to include new spaces subcommands. |
| src/commands/spaces/get.ts | Implements spaces get via REST presence query against the ::$space channel. |
| src/commands/spaces/create.ts | Implements spaces create command behavior and JSON/human output. |
| README.md | Documents the new spaces commands and adds them to the command index. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!this.shouldOutputJson(flags)) { | ||
| this.log( | ||
| formatSuccess(`Subscribed to space: ${formatResource(spaceName)}.`), | ||
| ); | ||
| this.log(formatListening("Listening for space updates.")); | ||
| } |
| static override description = "Create a new space"; | ||
|
|
||
| static override examples = [ | ||
| "$ ably spaces create my-space", | ||
| "$ ably spaces create my-space --json", | ||
| "$ ably spaces create my-space --client-id my-client", | ||
| ]; | ||
|
|
||
| static override flags = { | ||
| ...productApiFlags, | ||
| ...clientIdFlag, | ||
| }; | ||
|
|
||
| async run(): Promise<void> { | ||
| const { args, flags } = await this.parse(SpacesCreate); | ||
| const spaceName = args.space_name; | ||
|
|
||
| try { | ||
| if (!this.shouldOutputJson(flags)) { | ||
| this.log(formatProgress(`Creating space ${formatResource(spaceName)}`)); | ||
| } | ||
|
|
||
| await this.initializeSpace(flags, spaceName, { | ||
| enterSpace: false, | ||
| setupConnectionLogging: false, | ||
| }); | ||
|
|
||
| if (this.shouldOutputJson(flags)) { | ||
| this.logJsonResult({ space: { name: spaceName } }, flags); | ||
| } else { | ||
| this.log(formatSuccess(`Space ${formatResource(spaceName)} created.`)); | ||
| } |
| ## `ably spaces create SPACE_NAME` | ||
|
|
||
| Create a new space | ||
|
|
||
| ``` | ||
| USAGE | ||
| $ ably spaces create SPACE_NAME [-v] [--json | --pretty-json] [--client-id <value>] | ||
|
|
||
| ARGUMENTS | ||
| SPACE_NAME Name of the space to create | ||
|
|
||
| FLAGS | ||
| -v, --verbose Output verbose logs | ||
| --client-id=<value> Overrides any default client ID when using API authentication. Use "none" to explicitly set | ||
| no client ID. Not applicable when using token authentication. | ||
| --json Output in JSON format | ||
| --pretty-json Output in colorized JSON format | ||
|
|
||
| DESCRIPTION | ||
| Create a new space | ||
|
|
Added following missing commands
Summary by CodeRabbit
New Features
ably spaces create SPACE_NAMEcommand to create new spacesably spaces get SPACE_NAMEcommand to view space state and membersably spaces members get-all SPACE_NAMEcommand to retrieve all members in a spaceably spaces subscribe SPACE_NAMEcommand to monitor real-time space updatesDocumentation