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
37 changes: 14 additions & 23 deletions sdk/webpubsub-chat-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import { ChatClient } from '@azure/web-pubsub-chat-client';
// Get client access URL from your server
const url = await fetch('/negotiate?userId=alice').then(r => r.json()).then(d => d.url);

// Option 1: Start with an existing WebPubSubClient
const wpsClient = new WebPubSubClient(url);
const client = await ChatClient.start(wpsClient);
// Start directly with a client access URL...
const client = await ChatClient.start(url);

// Option 2: Start directly with URL
// const client = await ChatClient.start(url);
// ...or with a credential (a callback that returns a client access URL):
// const client = await ChatClient.start({ getClientAccessUrl: async () => url });

console.log(`Started as: ${client.userId}`);

Expand Down Expand Up @@ -63,28 +62,24 @@ await client.stop();
#### Constructor

```typescript
new ChatClient(wpsClient: WebPubSubClient)
new ChatClient(credential: WebPubSubClientCredential)
```

`ChatClient` always wraps a pre-constructed `WebPubSubClient` and owns its lifecycle: `start()` starts the transport, `stop()` stops it.

To construct from a client-access URL or `WebPubSubClientCredential`, use the static `ChatClient.start(...)` factory below — it builds the underlying `WebPubSubClient` and starts the chat client atomically, so callers never observe a half-constructed instance.
`ChatClient` is constructed from a `WebPubSubClientCredential`. It builds and owns the underlying transport: `start()` connects and authenticates, `stop()` disconnects. The instance is constructed but not started — call `start()`, or use the static `ChatClient.start(...)` factory below (which also accepts a plain client-access URL) to construct-and-start in one step.

#### Static Methods

| Method | Description |
|--------|-------------|
| `ChatClient.start(clientAccessUrl, webPubSubClientOptions?, options?)` | Construct from a URL and start (`webPubSubClientOptions?: WebPubSubClientOptions`, `options?: StartOptions`) |
| `ChatClient.start(credential, webPubSubClientOptions?, options?)` | Construct from a credential and start (same option shape as above) |
| `ChatClient.start(wpsClient, options?)` | Start a pre-constructed `WebPubSubClient` (`options?: StartOptions`) |
| `ChatClient.start(clientAccessUrl, options?)` | Construct from a client-access URL and start (`options?: StartOptions`) |
| `ChatClient.start(credential, options?)` | Construct from a `WebPubSubClientCredential` and start (`options?: StartOptions`) |

#### Properties

| Property | Type | Description |
|----------|------|-------------|
| `userId` | `string` | Current user's ID (throws if not started). Read-only — set internally on `start()`. |
| `rooms` | `RoomInfo[]` | Snapshot of currently joined rooms (not live-updated) |
| `connection` | `WebPubSubClient` | Underlying WebPubSub connection owned by this chat client |

#### Methods

Expand All @@ -93,10 +88,10 @@ To construct from a client-access URL or `WebPubSubClientCredential`, use the st
| `start(options?)` | Connect and authenticate. Idempotent; concurrent calls share one in-flight promise. After `stop()` the client can be started again. Accepts `{ abortSignal }`. |
| `stop()` | Disconnect and reset client state. Returns `Promise<void>`. |
| `createRoom(title, members, options?)` | Create a new room with initial members. The current user is automatically added to the members list. Options: `{ roomId?, abortSignal? }` — supply `roomId` to choose an explicit id, otherwise the service assigns one. |
| `getRoomDetail(roomId, options?)` | Get room info. Options: `{ withMembers?, abortSignal? }` — set `withMembers: true` to populate the members list (extra round-trip). |
| `getRoomDetail(roomId, options?)` | Get the detailed view of a room (`RoomDetail`). Options: `{ withMembers?, abortSignal? }` — pass `withMembers: true` to populate the `members` list (omitted otherwise). |
| `addUserToRoom(roomId, userId, options?)` | Add user to room (admin operation) |
| `removeUserFromRoom(roomId, userId, options?)` | Remove user from room (admin operation) |
| `sendToRoom(roomId, message, options?)` | Send text message to room, returns message ID |
| `sendToRoom(roomId, message, options?)` | Send text message to room, returns a `SendMessageResult` (`{ messageId }`) |
| `listRoomMessages(roomId, options?)` | Paged async iterator over room message history (auto-paginates). Use `for await` to stream every message, or `.byPage({ maxPageSize })` to load up to `maxPageSize` messages at a time. `options = { startId?, endId?, maxPageSize?, abortSignal? }` |
| `getUserProfile(userId, options?)` | Get user profile |

Expand Down Expand Up @@ -166,14 +161,10 @@ client.off('message', onMsg);
| `member-joined` | `OnMemberJoinedArgs` | Another user joined a room this client is in. |
| `member-left` | `OnMemberLeftArgs` | Another user left a room this client is in. |

Connection-lifecycle events (`connected`, `disconnected`, `stopped`) live
on the underlying `WebPubSubClient`. Subscribe through `client.connection`:

```ts
client.connection.on('connected', (e) => console.log('connected', e));
client.connection.on('disconnected', (e) => console.log('disconnected', e));
client.connection.on('stopped', () => console.log('stopped'));
```
The underlying connection is managed internally and is not exposed. Use the
chat-level `started` / `stopped` events for lifecycle notifications;
lower-level connection events (`connected`, `disconnected`) are not
currently surfaced.

## Examples

Expand Down
18 changes: 6 additions & 12 deletions sdk/webpubsub-chat-client/examples/quickstart/client.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ChatClient } from '@azure/web-pubsub-chat-client';
import { WebPubSubClient } from '@azure/web-pubsub-client';

const SERVER_URL = process.env.SERVER_URL || 'http://localhost:3000';

Expand All @@ -25,25 +24,20 @@ function setupListeners(client) {
client.onRoomLeft((event) => {
console.log(`[${client.userId}] left room ${event.roomId}`);
});
// underlying connection lifecycle listeners (on the wpsClient)
client.connection.on("stopped", () => {
console.log(`connection used by ${client.userId} stopped`);
});
client.connection.on("disconnected", () => {
console.log(`connection used by ${client.userId} disconnected`);
// chat lifecycle listener
client.on("stopped", () => {
console.log(`chat client for ${client.userId} stopped`);
});
}

async function main() {
// Create chat clients for Alice, Bob, and Mike

// Option 1: create a chat client with a existing WebPubSubClient
const url1 = await getClientAccessUrl('alice');
const webPubSubClient = new WebPubSubClient(url1);
const alice = await ChatClient.start(webPubSubClient);
// Option 1: create a chat client with a credential (a callback returning a client access URL)
const alice = await ChatClient.start({ getClientAccessUrl: () => getClientAccessUrl('alice') });
console.log(`Alice started as: ${alice.userId}`);

// Option 2: create a chat client directly with client access URL
// Option 2: create a chat client directly with a client access URL
const url2 = await getClientAccessUrl('bob'), url3 = await getClientAccessUrl('mike');
const bob = await ChatClient.start(url2);
const mike = await ChatClient.start(url3);
Expand Down
Loading
Loading