Skip to content

Commit

Permalink
feat: ActionsType for channels
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Sep 22, 2023
1 parent 14697b9 commit 1104685
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

## master

- Add ActionsType to `Channel` class. ([@palkan][])

Now you can specify which actions can be _performed_ by the channel.

## 0.7.1 (2023-06-28)

- Add FallbackTransport and a new `@anycable/long-polling` package.
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Multiple reasons that forced us to implement an alternative client library for A

- [AnyCable Pro][pro] features support (e.g., binary formats).
- Multi-platform out-of-the-box (web, workers, React Native).
- TypeScript support.
- [TypeScript support](#typescript-support)
- Revisited client-side APIs.
- [Testability](#testing)
- Future [protocol extensions/modifications support](#extended-action-cable-protocol).
Expand Down Expand Up @@ -211,7 +211,13 @@ interface Events extends ChannelEvents<Message> {
typing: (msg: TypingMessage) => void
}

export class ChatChannel extends Channel<Params,Message,Events> {
// Which actions can be performed on this channel
interface Actions {
speak: (msg: ChatMessage) => void
typing: () => void
}

export class ChatChannel extends Channel<Params,Message,Events,Actions> {
static identifier = 'ChatChannel'

receive(message: Message) {
Expand All @@ -221,6 +227,14 @@ export class ChatChannel extends Channel<Params,Message,Events> {

super.receive(message)
}

sendMessage(msg: ChatMessage) {
return this.perform('speak', msg)
}

sendTyping() {
return this.perform('typing')
}
}
```

Expand All @@ -238,6 +252,14 @@ channel.on('typing', (msg: TypingMessage) => {}) //=> OK

channel.on('typing', (msg: string) => {}) //=> NOT OK: 'msg' type mismatch
channel.on('types', (msg: TypingMessage) => {}) //=> NOT OK: unknown event

channel.perform('speak', {type: 'message', username: 'John', userId: '42'}) //=> OK
channel.perform('speak', {body: 'hello!'}) //=> NOT OK: incorrect message type

channel.perform('typing') //=> OK
channel.perform('typing', {type: 'typing', username: 'John'}) //=> NOT OK: no payload expected

channel.perform('type') //=> NOT OK: unknown action
```

### Supported protocols
Expand Down
4 changes: 4 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## master

- Add ActionsType to `Channel` class. ([@palkan][])

Now you can specify which actions can be _performed_ by the channel.

## 0.7.6 (2023-08-17)

- Add `concurrentSubscribes: false` option to prevent sending multiple `subscribe` commands concurrently.
Expand Down
22 changes: 21 additions & 1 deletion packages/core/channel/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,34 @@ interface CustomEvents extends ChannelEvents<{ tupe: number }> {
// THROWS Type 'CustomEvents' does not satisfy the constraint 'ChannelEvents<{ type: string; }>'
export class TypedChannel extends Channel<{}, { type: string }, CustomEvents> {}

interface ChannelActions {
sendMessage: (data: { message: string }) => void
ping: () => void
}

export class AnotherTypedChannel extends Channel<
{},
{ tupe: number },
CustomEvents
CustomEvents,
ChannelActions
> {
trigger() {
this.emit('custom')
// THROWS Argument of type
this.emit('kustom')

// Should not throw
this.perform('sendMessage', { message: 'hello' })

this.perform('ping')

// THROWS Type 'number' is not assignable to type 'string'
this.perform('sendMessage', { message: 42 })

// THROWS Argument of type
this.perform('newMessage', { message: 'hello' })

// THROWS Expected 1 arguments, but got 2
this.perform('ping', { time: 42 })
}
}
19 changes: 17 additions & 2 deletions packages/core/channel/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ export interface ChannelEvents<T> {
message: (msg: T, meta?: MessageMeta) => void
}

/* eslint-disable @typescript-eslint/no-explicit-any */
export type ActionPayload = Record<string, any>

export interface ChannelActions {
[key: string]: (...args: ActionPayload[]) => void
}

export class Channel<
ParamsType extends ChannelParamsMap = {},
MessageType extends Message = Message,
EventsType extends ChannelEvents<MessageType> = ChannelEvents<MessageType>
EventsType extends ChannelEvents<MessageType> = ChannelEvents<MessageType>,
ActionsType = ChannelActions
> {
static readonly identifier: string

Expand All @@ -56,7 +64,14 @@ export class Channel<
attached(receiver: Receiver): boolean

disconnect(): void
perform(action: string, payload?: object): Promise<MessageType | void>
perform<A extends keyof ActionsType>(
action: A,
...payload: ActionsType extends ChannelActions
? [ActionPayload?]
: ActionsType[A] extends () => void
? []
: [Parameters<ActionsType[A]>[0]]
): Promise<MessageType | void>
send(payload: object): Promise<MessageType | void>
receive(msg: MessageType, meta?: MessageMeta): void

Expand Down

0 comments on commit 1104685

Please sign in to comment.