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
178 changes: 27 additions & 151 deletions src/commands/spaces/cursors/get-all.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { type CursorUpdate } from "@ably/spaces";
import { Args } from "@oclif/core";
import chalk from "chalk";

import { productApiFlags, clientIdFlag } from "../../../flags.js";
import { SpacesBaseCommand } from "../../../spaces-base-command.js";
import isTestMode from "../../../utils/test-mode.js";
import {
formatClientId,
formatCountLabel,
formatHeading,
formatIndex,
formatProgress,
formatSuccess,
formatResource,
formatClientId,
formatSuccess,
formatWarning,
} from "../../../utils/output.js";

interface CursorPosition {
x: number;
y: number;
}

interface CursorUpdate {
clientId?: string;
connectionId?: string;
data?: Record<string, unknown>;
position: CursorPosition;
}
import {
formatCursorBlock,
formatCursorOutput,
} from "../../../utils/spaces-output.js";

export default class SpacesCursorsGetAll extends SpacesBaseCommand {
static override args = {
Expand Down Expand Up @@ -76,16 +73,7 @@ export default class SpacesCursorsGetAll extends SpacesBaseCommand {
// Check realtime client state
if (this.realtimeClient!.connection.state === "connected") {
clearTimeout(timeout);
if (this.shouldOutputJson(flags)) {
this.logJsonResult(
{
connectionId: this.realtimeClient!.connection.id,
spaceName,
status: "connected",
},
flags,
);
} else {
if (!this.shouldOutputJson(flags)) {
this.log(
formatSuccess(`Entered space: ${formatResource(spaceName)}.`),
);
Expand Down Expand Up @@ -194,34 +182,17 @@ export default class SpacesCursorsGetAll extends SpacesBaseCommand {
const allCursors = await this.space!.cursors.getAll();

// Add any cached cursors that we didn't see in live updates
if (Array.isArray(allCursors)) {
allCursors.forEach((cursor) => {
if (
cursor &&
cursor.connectionId &&
!cursorMap.has(cursor.connectionId)
) {
cursorMap.set(cursor.connectionId, cursor as CursorUpdate);
}
});
} else if (allCursors && typeof allCursors === "object") {
// Handle object return type
Object.values(allCursors).forEach((cursor) => {
if (
cursor &&
cursor.connectionId &&
!cursorMap.has(cursor.connectionId)
) {
cursorMap.set(cursor.connectionId, cursor as CursorUpdate);
}
});
for (const cursor of Object.values(allCursors)) {
if (cursor && !cursorMap.has(cursor.connectionId)) {
cursorMap.set(cursor.connectionId, cursor);
}
}
} catch {
// If getAll fails due to connection issues, use only the live updates we collected
if (!this.shouldOutputJson(flags)) {
this.log(
chalk.yellow(
"Warning: Could not fetch all cursors, showing only live updates",
formatWarning(
"Could not fetch all cursors, showing only live updates.",
),
);
}
Expand All @@ -232,132 +203,37 @@ export default class SpacesCursorsGetAll extends SpacesBaseCommand {
if (this.shouldOutputJson(flags)) {
this.logJsonResult(
{
cursors: cursors.map((cursor: CursorUpdate) => ({
clientId: cursor.clientId,
connectionId: cursor.connectionId,
data: cursor.data,
position: cursor.position,
})),
spaceName,
cursorUpdateReceived,
cursors: cursors.map((cursor: CursorUpdate) =>
formatCursorOutput(cursor),
),
},
flags,
);
} else {
if (!cursorUpdateReceived && cursors.length === 0) {
this.log(chalk.dim("─".repeat(60)));
this.log(
chalk.yellow(
formatWarning(
"No cursor updates are being sent in this space. Make sure other clients are actively setting cursor positions.",
),
);
return;
}

if (cursors.length === 0) {
this.log(chalk.dim("─".repeat(60)));
this.log(chalk.yellow("No active cursors found in space."));
this.log(formatWarning("No active cursors found in space."));
return;
}

// Show summary table
this.log(chalk.dim("─".repeat(60)));
this.log(
chalk.bold(
`\nCursor Summary - ${cursors.length} cursor${cursors.length === 1 ? "" : "s"} found:\n`,
),
`\n${formatHeading("Current cursors")} (${formatCountLabel(cursors.length, "cursor")}):\n`,
);

// Table header
const colWidths = { client: 20, x: 8, y: 8, connection: 20 };
this.log(
chalk.gray(
"┌" +
"─".repeat(colWidths.client + 2) +
"┬" +
"─".repeat(colWidths.x + 2) +
"┬" +
"─".repeat(colWidths.y + 2) +
"┬" +
"─".repeat(colWidths.connection + 2) +
"┐",
),
);
this.log(
chalk.gray("│ ") +
chalk.bold("Client ID".padEnd(colWidths.client)) +
chalk.gray(" │ ") +
chalk.bold("X".padEnd(colWidths.x)) +
chalk.gray(" │ ") +
chalk.bold("Y".padEnd(colWidths.y)) +
chalk.gray(" │ ") +
chalk.bold("connection".padEnd(colWidths.connection)) +
chalk.gray(" │"),
);
this.log(
chalk.gray(
"├" +
"─".repeat(colWidths.client + 2) +
"┼" +
"─".repeat(colWidths.x + 2) +
"┼" +
"─".repeat(colWidths.y + 2) +
"┼" +
"─".repeat(colWidths.connection + 2) +
"┤",
),
);

// Table rows
cursors.forEach((cursor: CursorUpdate) => {
const clientId = (cursor.clientId || "Unknown").slice(
0,
colWidths.client,
);
const x = cursor.position.x.toString().slice(0, colWidths.x);
const y = cursor.position.y.toString().slice(0, colWidths.y);
const connectionId = (cursor.connectionId || "Unknown").slice(
0,
colWidths.connection,
);

cursors.forEach((cursor: CursorUpdate, index: number) => {
this.log(
chalk.gray("│ ") +
formatClientId(clientId.padEnd(colWidths.client)) +
chalk.gray(" │ ") +
chalk.yellow(x.padEnd(colWidths.x)) +
chalk.gray(" │ ") +
chalk.yellow(y.padEnd(colWidths.y)) +
chalk.gray(" │ ") +
chalk.dim(connectionId.padEnd(colWidths.connection)) +
chalk.gray(" │"),
`${formatIndex(index + 1)} ${formatCursorBlock(cursor, { indent: " " })}`,
);
this.log("");
});

this.log(
chalk.gray(
"└" +
"─".repeat(colWidths.client + 2) +
"┴" +
"─".repeat(colWidths.x + 2) +
"┴" +
"─".repeat(colWidths.y + 2) +
"┴" +
"─".repeat(colWidths.connection + 2) +
"┘",
),
);

// Show additional data if any cursor has it
const cursorsWithData = cursors.filter((c) => c.data);
if (cursorsWithData.length > 0) {
this.log(`\n${chalk.bold("Additional Data:")}`);
cursorsWithData.forEach((cursor: CursorUpdate) => {
this.log(
` ${formatClientId(cursor.clientId || "Unknown")}: ${JSON.stringify(cursor.data)}`,
);
});
}
}
} catch (error) {
this.fail(error, flags, "cursorGetAll", { spaceName });
Expand Down
39 changes: 20 additions & 19 deletions src/commands/spaces/cursors/set.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { CursorData, CursorPosition } from "@ably/spaces";
import { Args, Flags } from "@oclif/core";
import chalk from "chalk";

import { errorMessage } from "../../../utils/errors.js";
import { productApiFlags, clientIdFlag, durationFlag } from "../../../flags.js";
import { SpacesBaseCommand } from "../../../spaces-base-command.js";
Expand All @@ -12,18 +11,6 @@ import {
formatLabel,
} from "../../../utils/output.js";

// Define cursor types based on Ably documentation
interface CursorPosition {
x: number;
y: number;
}

interface CursorData {
[key: string]: unknown;
}

// CursorUpdate interface no longer required in this file

export default class SpacesCursorsSet extends SpacesBaseCommand {
static override args = {
space: Args.string({
Expand Down Expand Up @@ -191,17 +178,31 @@ export default class SpacesCursorsSet extends SpacesBaseCommand {
if (this.shouldOutputJson(flags)) {
this.logJsonResult(
{
cursor: cursorForOutput,
spaceName,
cursors: [
{
clientId: this.realtimeClient!.auth.clientId,
connectionId: this.realtimeClient!.connection.id,
position: (
cursorForOutput as { position: { x: number; y: number } }
).position,
data:
(cursorForOutput as { data?: Record<string, unknown> })
.data ?? null,
},
],
},
flags,
);
} else {
this.log(
formatSuccess(
`Set cursor in space ${formatResource(spaceName)} with data: ${chalk.blue(JSON.stringify(cursorForOutput))}.`,
),
formatSuccess(`Set cursor in space ${formatResource(spaceName)}.`),
);
const lines: string[] = [];
lines.push(`${formatLabel("Position")} (${position.x}, ${position.y})`);
if (data) {
lines.push(`${formatLabel("Data")} ${JSON.stringify(data)}`);
}
this.log(lines.join("\n"));
}

// Decide how long to remain connected
Expand Down
38 changes: 16 additions & 22 deletions src/commands/spaces/cursors/subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
formatResource,
formatSuccess,
formatTimestamp,
formatClientId,
formatLabel,
} from "../../../utils/output.js";
import {
formatCursorBlock,
formatCursorOutput,
} from "../../../utils/spaces-output.js";

export default class SpacesCursorsSubscribe extends SpacesBaseCommand {
static override args = {
Expand Down Expand Up @@ -56,35 +58,27 @@ export default class SpacesCursorsSubscribe extends SpacesBaseCommand {
this.listener = (cursorUpdate: CursorUpdate) => {
try {
const timestamp = new Date().toISOString();
const eventData = {
member: {
clientId: cursorUpdate.clientId,
connectionId: cursorUpdate.connectionId,
},
position: cursorUpdate.position,
data: cursorUpdate.data,
spaceName,
timestamp,
eventType: "cursor_update",
};
this.logCliEvent(
flags,
"cursor",
"updateReceived",
"Cursor update received",
eventData,
{
clientId: cursorUpdate.clientId,
position: cursorUpdate.position,
timestamp,
},
);

if (this.shouldOutputJson(flags)) {
this.logJsonEvent(eventData, flags);
} else {
// Include data field in the output if present
const dataString = cursorUpdate.data
? ` data: ${JSON.stringify(cursorUpdate.data)}`
: "";
this.log(
`${formatTimestamp(timestamp)} ${formatClientId(cursorUpdate.clientId)} ${formatLabel("position")} ${JSON.stringify(cursorUpdate.position)}${dataString}`,
this.logJsonEvent(
{ cursor: formatCursorOutput(cursorUpdate) },
flags,
);
} else {
this.log(formatTimestamp(timestamp));
this.log(formatCursorBlock(cursorUpdate));
this.log("");
}
} catch (error) {
this.fail(error, flags, "cursorSubscribe", {
Expand Down
6 changes: 1 addition & 5 deletions src/commands/spaces/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,10 @@ export default class SpacesList extends SpacesBaseCommand {
if (this.shouldOutputJson(flags)) {
this.logJsonResult(
{
hasMore: spacesList.length > flags.limit,
shown: limitedSpaces.length,
spaces: limitedSpaces.map((space: SpaceItem) => ({
metrics: space.status?.occupancy?.metrics || {},
spaceName: space.spaceName,
metrics: space.status?.occupancy?.metrics || {},
})),
timestamp: new Date().toISOString(),
total: spacesList.length,
},
flags,
);
Expand Down
Loading
Loading