Skip to content
Open
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
36 changes: 36 additions & 0 deletions packages/actor-core/src/actor/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ import type { SaveStateOptions } from "./instance";
import { Actions } from "./config";
import { ActorContext } from "./context";

/**
* Options for the `_broadcast` method.
*/
interface BroadcastOptions {
/**
* The connection IDs to be excluded from the broadcast.
*/
exclude?: ConnId[];
/**
* Excludes the current connection from the broadcast.
*/
excludeSelf?: boolean;
}

/**
* Context for a remote procedure call.
*
Expand Down Expand Up @@ -50,6 +64,28 @@ export class ActionContext<S, CP, CS, V> {
this.#actorContext.broadcast(name, ...args);
}

/**
* Broadcasts an event to all connected clients with options.
*/
broadcastWithOptions<Args extends Array<unknown>>(opts: BroadcastOptions, name: string, ...args: Args) {
const exclude = opts.exclude ?? [];

if (opts.excludeSelf) {
exclude.push(this.conn.id);
}
Comment on lines +71 to +75
Copy link

Choose a reason for hiding this comment

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

Potential Issue: This code mutates the exclude array that was passed in through opts. If the caller reuses this array elsewhere, they might encounter unexpected behavior. Consider creating a copy of the array instead:

const exclude = [...(opts.exclude ?? [])];

This ensures the original array remains unchanged while still allowing the function to add the current connection ID when excludeSelf is true.

Suggested change
const exclude = opts.exclude ?? [];
if (opts.excludeSelf) {
exclude.push(this.conn.id);
}
const exclude = [...(opts.exclude ?? [])];
if (opts.excludeSelf) {
exclude.push(this.conn.id);
}

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.


// @ts-ignore - Access protected method
this.#actorContext.broadcastWithOptions({ exclude }, name, ...args);
return;
}

/**
* Alias for `broadcastWithOptions`
*/
broadcastWith<Args extends Array<unknown>>(opts: BroadcastOptions, name: string, ...args: Args) {
return this.broadcastWithOptions(opts, name, ...args);
}

/**
* Gets the logger instance.
*/
Expand Down
14 changes: 13 additions & 1 deletion packages/actor-core/src/actor/context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Logger } from "@/common/log";
import { Actions } from "./config";
import { ActorInstance, SaveStateOptions } from "./instance";
import { ActorInstance, BroadcastInstanceOptions, SaveStateOptions } from "./instance";
import { Conn, ConnId } from "./connection";
import { ActorTags } from "@/common/utils";
import { Schedule } from "./schedule";
Expand Down Expand Up @@ -41,6 +41,18 @@ export class ActorContext<S, CP, CS, V> {
return;
}

/**
* Broadcasts an event to all connected clients with options.
* @param opts - Options for the broadcast.
* @param name - The name of the event.
* @param args - The arguments to send with the event.
*/
broadcastWithOptions<Args extends Array<unknown>>(opts: BroadcastInstanceOptions, name: string, ...args: Args) {
// @ts-ignore - Access protected method
this.#actor._broadcastWithOptions(opts, name, ...args);
return;
}

/**
* Gets the logger instance.
*/
Expand Down
26 changes: 26 additions & 0 deletions packages/actor-core/src/actor/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ export interface SaveStateOptions {
immediate?: boolean;
}

/**
* Options for the `_broadcastWithOptions` method.
*/
export interface BroadcastInstanceOptions {
/**
* The connection IDs to be excluded from the broadcast.
*/
exclude?: string[];
Copy link

Choose a reason for hiding this comment

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

The type for exclude should be ConnId[] rather than string[] to maintain consistency with the BroadcastOptions interface defined in action.ts. This ensures type safety across the codebase and prevents potential issues when passing connection IDs between these interfaces.

Suggested change
exclude?: string[];
exclude?: ConnId[];

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

}

/** Actor type alias with all `any` types. Used for `extends` in classes referencing this actor. */
// biome-ignore lint/suspicious/noExplicitAny: Needs to be used in `extends`
export type AnyActorInstance = ActorInstance<any, any, any, any>;
Expand Down Expand Up @@ -1013,6 +1023,16 @@ export class ActorInstance<S, CP, CS, V> {
* @param args - The arguments to send with the event.
*/
_broadcast<Args extends Array<unknown>>(name: string, ...args: Args) {
return this._broadcastWithOptions({}, name, ...args);
}

/**
* Broadcasts an event to all connected clients with options.
* @param opts - Options for the broadcast.
* @param name - The name of the event.
* @param args - The arguments to send with the event.
*/
_broadcastWithOptions<Args extends Array<unknown>>(opts: BroadcastInstanceOptions, name: string, ...args: Args) {
this.#assertReady();

// Send to all connected clients
Expand All @@ -1028,8 +1048,14 @@ export class ActorInstance<S, CP, CS, V> {
},
});

const excludeList = opts.exclude ?? [];

// Send message to clients
for (const connection of subscriptions) {
if (excludeList.includes(connection.id)) {
continue;
}

connection._sendMessage(toClientSerializer);
}
}
Expand Down
Loading