@tailuge/messaging 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,391 @@
1
+ # Multi-User Messaging Library Specification
2
+
3
+ ## Overview
4
+
5
+ This document outlines the requirements and contract for a unified messaging library designed to handle presence and real-time synchronization. The library uses the existing `NchanClient` as a transport layer (WebSockets for subscribe, POST for publish) and provides a semantic, stateful API for turn-based multi-user applications.
6
+
7
+ ## Goals
8
+
9
+ - **Zero WebSocket Dependencies**: The consumer project should not interact with WebSockets directly.
10
+ - **Unified Client**: A single entry point for both global presence (lobby) and specific table messaging.
11
+ - **Stateful Presence**: Internal management of online users, including heartbeats and stale user pruning.
12
+ - **Semantic API**: Interaction through high-level methods rather than raw channel/URL manipulation.
13
+ - **Platform Agnostic**: Compatible with both Browser and Node.js environments.
14
+ - **Transport Reuse**: Leverages existing `NchanClient` from `src/lobby/nchanclient.ts` as the underlying transport.
15
+
16
+ ---
17
+
18
+ ## Core API Contract
19
+
20
+ ### `MessagingClient`
21
+
22
+ The main class exposed by the library. Uses `NchanClient` internally for all transport operations.
23
+
24
+ ```typescript
25
+ interface MessagingClient {
26
+ /**
27
+ * Initialize and start the client.
28
+ * Handles initial connection and automatic reconnection logic.
29
+ * In browser environments, attaches lifecycle event listeners.
30
+ */
31
+ start(): void;
32
+
33
+ /**
34
+ * Stop all connections, timers (heartbeats), and clean up resources.
35
+ */
36
+ stop(): Promise<void>;
37
+
38
+ /**
39
+ * Joins the global lobby to broadcast presence and see other online users.
40
+ */
41
+ joinLobby(user: PresenceMessage, options?: LobbyOptions): Promise<Lobby>;
42
+
43
+ /**
44
+ * Joins a specific table (game room) for 2-player/spectator communication.
45
+ */
46
+ joinTable<T = any>(tableId: string, userId: string): Promise<Table<T>>;
47
+ }
48
+ ```
49
+
50
+ ### `Lobby`
51
+
52
+ Represents the global presence state and matchmaking.
53
+
54
+ ```typescript
55
+ interface Lobby {
56
+ /**
57
+ * Stream of the current online users.
58
+ * Emits the full list whenever users join, leave, or time out.
59
+ * The list must be sorted alphabetically by `userName` (case-insensitive).
60
+ */
61
+ onUsersChange(callback: (users: PresenceMessage[]) => void): void;
62
+
63
+ /**
64
+ * Update the current user's presence information (e.g., change name).
65
+ */
66
+ updatePresence(update: Partial<PresenceMessage>): Promise<void>;
67
+
68
+ /**
69
+ * Challenge another user to a game.
70
+ * Returns the ID of the table created for the challenge.
71
+ */
72
+ challenge(userId: string, ruleType: string): Promise<string>;
73
+
74
+ /**
75
+ * Accept an incoming challenge.
76
+ * Returns the Table instance for the accepted game.
77
+ */
78
+ acceptChallenge(userId: string, ruleType: string, tableId: string): Promise<Table>;
79
+
80
+ /**
81
+ * Decline an incoming challenge.
82
+ */
83
+ declineChallenge(userId: string, ruleType: string): Promise<void>;
84
+
85
+ /**
86
+ * Cancel an outgoing challenge.
87
+ */
88
+ cancelChallenge(userId: string, ruleType: string): Promise<void>;
89
+
90
+ /**
91
+ * Subscribe to incoming challenges directed at the current user.
92
+ */
93
+ onChallenge(callback: (challenge: ChallengeMessage) => void): void;
94
+
95
+ /**
96
+ * Leave the lobby.
97
+ */
98
+ leave(): Promise<void>;
99
+ }
100
+ ```
101
+
102
+ ### `Table`
103
+
104
+ Represents a specific communication channel for a 2-player/spectator scenario at a table.
105
+
106
+ ```typescript
107
+ interface Table<T = any> {
108
+ /**
109
+ * Broadcast an event to all participants at the table.
110
+ */
111
+ publish(type: string, data: T): Promise<void>;
112
+
113
+ /**
114
+ * Subscribe to events published by other participants.
115
+ */
116
+ onMessage(callback: (event: TableMessage<T>) => void): void;
117
+
118
+ /**
119
+ * Subscribe to changes in the spectator list.
120
+ */
121
+ onSpectatorChange(callback: (spectators: PresenceMessage[]) => void): void;
122
+
123
+ /**
124
+ * Leave the table.
125
+ */
126
+ leave(): Promise<void>;
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Data Models
133
+
134
+ ### `_meta` (Server-Enriched Metadata)
135
+
136
+ All messages published through the transport layer are automatically enriched by the server with metadata from HTTP headers and connection info. This `_meta` object is **added by the server** and should be used by clients as the absolute source of truth for timing (`ts`) and origin.
137
+
138
+ ```typescript
139
+ interface Meta {
140
+ ts: string; // ISO timestamp of the request (Source of Truth for time)
141
+ locale: string; // Accept-Language header (use for flag rendering)
142
+ ua: string; // User-Agent header
143
+ ip: string; // Client remote address
144
+ origin: string; // Origin header value
145
+ host: string; // Host header value
146
+ path: string; // Request URI path
147
+ method: string; // HTTP method (always POST for publish)
148
+ country: string; // Country code from IP (e.g., "US", "GB", "XX")
149
+ }
150
+ ```
151
+
152
+ **Note**: The client should NOT include `locale` or `ua` in published messages — the server adds these automatically from HTTP headers. This ensures reliable, tamper-resistant metadata for UI features like flag rendering.
153
+
154
+ ### `PresenceMessage`
155
+
156
+ Information about a user in the lobby. The `locale` and `ua` fields are **not** set by the client — they are provided by the server via `_meta`.
157
+
158
+ ```typescript
159
+ interface PresenceMessage {
160
+ messageType: "presence";
161
+ type: "join" | "heartbeat" | "leave";
162
+ userId: string;
163
+ userName: string;
164
+ ruleType?: string;
165
+ opponentId?: string | null;
166
+ seek?: Seek;
167
+ lastSeen?: number; // Managed internally for pruning (derived from _meta.ts)
168
+ _meta?: Meta; // Server-enriched metadata (received messages only)
169
+
170
+ // Current game state:
171
+ // - If present: user is playing or spectating at that table (available for spectating)
172
+ // - If absent: user is available for new games
173
+ tableId?: string;
174
+ }
175
+ ```
176
+
177
+ ### `ChallengeMessage`
178
+
179
+ Represents a peer-to-peer challenge request.
180
+
181
+ ```typescript
182
+ interface ChallengeMessage {
183
+ messageType: "challenge";
184
+ type: "offer" | "accept" | "decline" | "cancel";
185
+ challengerId: string;
186
+ challengerName: string;
187
+ recipientId: string;
188
+ ruleType: string;
189
+ tableId?: string; // Optional: table created by challenger
190
+ _meta?: Meta; // Server-enriched metadata (received messages only)
191
+ }
192
+ ```
193
+
194
+ ### `TableInfo` (UNDER REVIEW)
195
+
196
+ Lobby-level information about an active game table.
197
+
198
+ ```typescript
199
+ interface TableInfo {
200
+ tableId: string;
201
+ ruleType: string;
202
+ players: { id: string; name: string }[];
203
+ spectatorCount: number;
204
+ status: "waiting" | "playing" | "finished";
205
+ createdAt: number;
206
+ }
207
+ ```
208
+
209
+ ### `TableMessage`
210
+
211
+ A generic structure for table/game events. Replaces raw payloads with a structured, meta-aware event.
212
+
213
+ ```typescript
214
+ interface TableMessage<T = any> {
215
+ type: string;
216
+ senderId: string;
217
+ data: T; // Application-specific payload
218
+ _meta?: Meta; // Server-enriched metadata (received messages only)
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Derived State & Logic
225
+
226
+ Clients can derive additional state from the presence data provided by the `Lobby`.
227
+
228
+ ### 1. Active Games List
229
+
230
+ #### `activeGames(users: PresenceMessage[]): ActiveGame[]`
231
+
232
+ Filters users with a `tableId` and returns one entry per unique table.
233
+
234
+ ```typescript
235
+ interface ActiveGame {
236
+ tableId: string;
237
+ players: { id: string; name: string }[];
238
+ ruleType?: string;
239
+ }
240
+ ```
241
+
242
+ **Note**: Spectator vs player distinction is not available in presence data - all users with `tableId` are included as "players".
243
+
244
+ ### 2. Predicates
245
+
246
+ Exported helper functions for consumer use.
247
+
248
+ #### `canChallenge(target: PresenceMessage, currentUserId: string): boolean`
249
+ Returns true if:
250
+ - `target.userId !== currentUserId` (not self)
251
+ - `!target.tableId` (not already at a table)
252
+ - `!target.seek` (not seeking a game)
253
+
254
+ #### `canSpectate(target: PresenceMessage, currentTableId?: string): boolean`
255
+ Returns true if:
256
+ - `target.tableId` exists (is at a table)
257
+ - `target.tableId !== currentTableId` (not already at this table)
258
+
259
+ ---
260
+
261
+ ## Internal Requirements
262
+
263
+ ### 1. Presence Management
264
+
265
+ - **Heartbeat**: The library must automatically send periodic "heartbeat" messages (e.g., every 60 seconds) to the lobby while active.
266
+ - **Pruning**: The library must maintain an internal map of users and automatically remove users who haven't sent a heartbeat within a specific TTL (e.g., 90 seconds).
267
+ - **Unload Handling**: In browser environments, the library should attempt to send a "leave" message on `beforeunload` or `pagehide`.
268
+
269
+ ### 2. Transport & Reconnection
270
+
271
+ - **Transport Layer**: Uses `NchanClient` from `src/lobby/nchanclient.ts` as the underlying transport. The WebSocket/POST abstraction is handled internally. `NchanClient` remains transport-agnostic and platform-neutral (Browser + Node.js).
272
+ - **Resilience**:
273
+ - Automatic exponential backoff for reconnection on WebSocket failure.
274
+ - Transparently handle transition between online/offline states.
275
+ - **Concurrency**: Ensure multiple `Table` instances can coexist if needed (though typically one game at a time).
276
+
277
+ ### 3. Message Retention
278
+
279
+ The Nchan server retains presence messages for a configurable duration (default: 90 seconds, 2000 messages). This ensures:
280
+ - Late subscribers receive buffered presence messages from existing users
281
+ - Clients reconnecting can see active users without waiting for the next heartbeat
282
+ - The system is resilient to brief network interruptions
283
+
284
+ **Note**: This is handled by the Nchan server configuration (`nchan_message_timeout` and `nchan_message_buffer_length`).
285
+
286
+ ### 3. Page Visibility & Browser Lifecycle
287
+
288
+ Page visibility handling (`pagehide`, `pageshow`, `visibilitychange`) is the responsibility of `MessagingClient` (application layer), **not** `NchanClient` (transport layer). This keeps the transport layer platform-agnostic.
289
+
290
+ `MessagingClient` should:
291
+
292
+ - Listen for `pagehide` to close connections and send a "leave" presence message
293
+ - Listen for `pageshow` (with `event.persisted`) to restore connections from bfcache
294
+ - Track `document.hidden` state to pause/resume heartbeats
295
+
296
+ ```typescript
297
+ // Example pattern for MessagingClient
298
+ // Note: MessagingClient does NOT have direct socket access.
299
+ // It calls stop() on subscriptions returned by NchanClient, or exposes its own stop() method.
300
+
301
+ private handlePageHide = (): void => {
302
+ this.stop(); // Stops all subscriptions and sends "leave" presence
303
+ };
304
+
305
+ private handlePageShow = (event: PageTransitionEvent): void => {
306
+ if (event.persisted) {
307
+ this.start().then(() => this.joinLobby(this.currentUser));
308
+ }
309
+ };
310
+ ```
311
+
312
+ ### 3. State Synchronization
313
+
314
+ - The library should ensure that when a user joins a lobby, they receive the current "state of the world" or quickly populate it via incoming heartbeats.
315
+ - For tables, it should provide a reliable pipe for sequence-sensitive events (optionally implementing sequence numbering if required by the transport).
316
+
317
+ ---
318
+
319
+ ## Accepted Concerns
320
+
321
+ ### 1. Race Conditions in Matchmaking
322
+
323
+ ### 2. Presence Scaling ($O(N^2)$)
324
+
325
+ ### 3. State Reconstruction
326
+
327
+ Since the transport retains messages server-side, a new client joining the lobby will receive buffered presence messages from existing users.
328
+
329
+ ---
330
+
331
+ ## Usage Example (Conceptual)
332
+
333
+ ```typescript
334
+ const client = new MessagingClient({
335
+ baseUrl: "billiards-network.onrender.com",
336
+ });
337
+
338
+ await client.start();
339
+
340
+ // Lobby interaction
341
+ const lobby = await client.joinLobby({
342
+ messageType: "presence",
343
+ type: "join",
344
+ userId: "user-123",
345
+ userName: "Alice",
346
+ });
347
+
348
+ lobby.onUsersChange((users) => {
349
+ console.log("Online users:", users.length);
350
+ });
351
+
352
+ lobby.onChallenge((challenge) => {
353
+ if (confirm(`Accept challenge from ${challenge.challengerName}?`)) {
354
+ lobby.acceptChallenge(challenge.challengerId, challenge.ruleType, challenge.tableId);
355
+ }
356
+ });
357
+
358
+ // Table interaction with a generic move type
359
+ interface Move { x: number; y: number }
360
+ const table = await client.joinTable<Move>("table-xyz", "user-123");
361
+
362
+ table.onMessage((msg) => {
363
+ if (msg.type === "MOVE") {
364
+ // msg.data is typed as Move
365
+ applyMove(msg.data);
366
+ console.log("Move received at:", msg._meta?.ts);
367
+ }
368
+ });
369
+
370
+ table.publish("MOVE", { x: 10, y: 20 });
371
+ ```
372
+
373
+ ---
374
+
375
+ ## Future Considerations
376
+
377
+ ### 1. `beforeunload` Handler
378
+
379
+ The current implementation uses `pagehide` to handle browser page dismissal. An alternative approach is to use `beforeunload` to send a synchronous (or near-synchronous) "leave" presence message before the page unloads. This provides a fallback for browsers where `pagehide` may not fire reliably.
380
+
381
+ Trade-offs:
382
+ - `beforeunload` is synchronous and may delay page dismissal
383
+ - Not supported in all browsers (e.g., Safari iOS)
384
+ - `pagehide` is the modern recommended approach, but may not send network requests in some cases
385
+
386
+ ### 2. `joinTable` Behavior
387
+
388
+ The current `joinTable` method automatically updates the user's presence to include `tableId` (marking them as "at a table"). Future iterations could:
389
+ - Make this behavior optional via a configuration flag
390
+ - Allow spectating without affecting the user's availability status
391
+ - Provide separate methods: `joinTableAsPlayer()` vs. `joinTableAsSpectator()`
package/README.md CHANGED
@@ -77,5 +77,19 @@ npm run docker:build
77
77
 
78
78
  - **Linting**: `npm run lint`
79
79
  - **Formatting**: `npm run format`
80
+
81
+ ## Publishing
82
+
83
+ To release a new version to npm:
84
+
85
+ ```bash
86
+ npm run release # Builds and bumps version
87
+ npm login # Login to npm (if needed)
88
+ npm publish # Publish to npm registry
89
+ ```
90
+
91
+ ## Additional Documentation
92
+
80
93
  - **Specification**: See [MESSAGING_SPEC.md](./MESSAGING_SPEC.md) for the API contract and data models.
94
+ - **Usage Guide**: See [SKILL.md](./SKILL.md) for a quick reference guide.
81
95
  - **Architectural Overview**: See [AGENTS.md](./AGENTS.md) for the design patterns used.
package/SKILL.md ADDED
@@ -0,0 +1,192 @@
1
+ # @tailuge/messaging Agent Skill
2
+
3
+ Use this skill when integrating the `@tailuge/messaging` library into a new project or codebase.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @tailuge/messaging
9
+ ```
10
+
11
+ ## Initialization
12
+
13
+ Create a `MessagingClient` instance with your Nchan server base URL:
14
+
15
+ ```typescript
16
+ import { MessagingClient } from '@tailuge/messaging';
17
+
18
+ const client = new MessagingClient({
19
+ baseUrl: "https://your-nchan-server.com",
20
+ });
21
+
22
+ await client.start();
23
+ ```
24
+
25
+ ## Getting Online User Count
26
+
27
+ Join the lobby and listen for user changes:
28
+
29
+ ```typescript
30
+ const lobby = await client.joinLobby({
31
+ messageType: "presence",
32
+ type: "join",
33
+ userId: "user-123",
34
+ userName: "Alice",
35
+ });
36
+
37
+ lobby.onUsersChange((users) => {
38
+ console.log("Online users:", users.length);
39
+ });
40
+ ```
41
+
42
+ ## Getting User List
43
+
44
+ The same `onUsersChange` callback provides the full user list:
45
+
46
+ ```typescript
47
+ lobby.onUsersChange((users) => {
48
+ // Users are sorted alphabetically by userName
49
+ users.forEach(user => {
50
+ console.log(`${user.userName} (${user.userId})`);
51
+ if (user.tableId) {
52
+ console.log(` Playing at table: ${user.tableId}`);
53
+ }
54
+ });
55
+ });
56
+ ```
57
+
58
+ ## Sending a Challenge
59
+
60
+ To challenge another user to a game:
61
+
62
+ ```typescript
63
+ // challenge(userId, ruleType) returns the table ID
64
+ const tableId = await lobby.challenge(targetUserId, "billiards");
65
+ console.log(`Challenge sent, table created: ${tableId}`);
66
+ ```
67
+
68
+ ## Receiving Challenges
69
+
70
+ Listen for incoming challenges:
71
+
72
+ ```typescript
73
+ lobby.onChallenge((challenge) => {
74
+ console.log(`${challenge.challengerName} challenged you to ${challenge.ruleType}`);
75
+
76
+ if (challenge.type === "offer") {
77
+ // Accept the challenge
78
+ lobby.acceptChallenge(challenge.challengerId, challenge.ruleType, challenge.tableId);
79
+
80
+ // Or decline
81
+ // lobby.declineChallenge(challenge.challengerId, challenge.ruleType);
82
+ }
83
+ });
84
+ ```
85
+
86
+ ## Joining a Table
87
+
88
+ Connect to a specific game table:
89
+
90
+ ```typescript
91
+ interface Move { x: number; y: number }
92
+ const table = await client.joinTable<Move>("table-xyz", "user-123");
93
+
94
+ table.onMessage((msg) => {
95
+ if (msg.type === "MOVE") {
96
+ const move = msg.data as Move;
97
+ console.log(`Move received at: ${msg._meta?.ts}`);
98
+ }
99
+ });
100
+
101
+ // Send a move
102
+ await table.publish("MOVE", { x: 10, y: 20 });
103
+ ```
104
+
105
+ ## Spectator Updates
106
+
107
+ Listen for spectator changes at a table:
108
+
109
+ ```typescript
110
+ table.onSpectatorChange((spectators) => {
111
+ console.log(`Spectators: ${spectators.length}`);
112
+ });
113
+ ```
114
+
115
+ ## Cleanup
116
+
117
+ When done, stop the client:
118
+
119
+ ```typescript
120
+ await client.stop();
121
+ ```
122
+
123
+ ## Key Interfaces
124
+
125
+ ### PresenceMessage
126
+ ```typescript
127
+ interface PresenceMessage {
128
+ messageType: "presence";
129
+ type: "join" | "heartbeat" | "leave";
130
+ userId: string;
131
+ userName: string;
132
+ ruleType?: string;
133
+ tableId?: string;
134
+ // ... other fields
135
+ }
136
+ ```
137
+
138
+ ### Meta
139
+ ```typescript
140
+ interface Meta {
141
+ ts: string; // ISO timestamp from server
142
+ locale: string; // Accept-Language header
143
+ ua: string; // User-Agent header
144
+ ip: string; // Client remote address
145
+ origin: string; // Origin header value
146
+ host: string; // Host header value
147
+ path: string; // Request URI path
148
+ method: string; // HTTP method
149
+ country: string; // Country code from IP (e.g., "US", "GB", "XX")
150
+ }
151
+ ```
152
+
153
+ ### ChallengeMessage
154
+ ```typescript
155
+ interface ChallengeMessage {
156
+ messageType: "challenge";
157
+ type: "offer" | "accept" | "decline" | "cancel";
158
+ challengerId: string;
159
+ challengerName: string;
160
+ recipientId: string;
161
+ ruleType: string;
162
+ tableId?: string;
163
+ }
164
+ ```
165
+
166
+ ### TableMessage<T>
167
+ ```typescript
168
+ interface TableMessage<T = any> {
169
+ type: string;
170
+ senderId: string;
171
+ data: T;
172
+ _meta?: Meta;
173
+ }
174
+ ```
175
+
176
+ ## Helper Predicates
177
+
178
+ The library exports helper functions:
179
+
180
+ ```typescript
181
+ import { canChallenge, canSpectate } from '@tailuge/messaging';
182
+
183
+ // Check if you can challenge a user
184
+ if (canChallenge(targetUser, currentUserId)) {
185
+ lobby.challenge(targetUser.userId, "billiards");
186
+ }
187
+
188
+ // Check if you can spectate a user
189
+ if (canSpectate(targetUser, currentTableId)) {
190
+ const table = await client.joinTable(targetUser.tableId, currentUserId);
191
+ }
192
+ ```
package/dist/lobby.js CHANGED
@@ -65,6 +65,7 @@ export class Lobby {
65
65
  console.error("Failed to send heartbeat:", _e);
66
66
  }
67
67
  }, this.heartbeatInterval);
68
+ this.heartbeatTimer.unref?.();
68
69
  }
69
70
  stopHeartbeat() {
70
71
  if (this.heartbeatTimer) {
@@ -91,6 +92,7 @@ export class Lobby {
91
92
  this.notifyListeners();
92
93
  }
93
94
  }, this.pruneInterval);
95
+ this.pruneTimer.unref?.();
94
96
  }
95
97
  stopPruning() {
96
98
  if (this.pruneTimer) {
@@ -237,7 +239,7 @@ export class Lobby {
237
239
  this.listeners.forEach((cb) => cb(list));
238
240
  }
239
241
  getUsersList() {
240
- return Array.from(this.users.values());
242
+ return Array.from(this.users.values()).sort((a, b) => a.userName.localeCompare(b.userName));
241
243
  }
242
244
  }
243
245
  //# sourceMappingURL=lobby.js.map
package/dist/lobby.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lobby.js","sourceRoot":"","sources":["../src/lobby.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lobby.js","sourceRoot":"","sources":["../src/lobby.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@ export declare class MessagingClient {
18
18
  * Initializes the client and ensures connection readiness.
19
19
  * In browser environments, attaches lifecycle event listeners.
20
20
  */
21
- start(): Promise<void>;
21
+ start(): void;
22
22
  /**
23
23
  * Stops all active connections and cleans up.
24
24
  */
@@ -18,7 +18,7 @@ export class MessagingClient {
18
18
  * Initializes the client and ensures connection readiness.
19
19
  * In browser environments, attaches lifecycle event listeners.
20
20
  */
21
- async start() {
21
+ start() {
22
22
  if (typeof window !== "undefined") {
23
23
  window.addEventListener("pagehide", this.handlePageHide);
24
24
  window.addEventListener("pageshow", this.handlePageShow);
@@ -43,10 +43,8 @@ export class MessagingClient {
43
43
  await Promise.all(lobbies.map((lobby) => lobby.leave(options)));
44
44
  const tables = [...this.activeTables];
45
45
  this.activeTables = [];
46
- // Use for loop to await each table leave
47
- for (const table of tables) {
48
- await table.leave(options);
49
- }
46
+ // Tables can be left in parallel since each operates independently
47
+ await Promise.all(tables.map((table) => table.leave(options)));
50
48
  }
51
49
  finally {
52
50
  this.isStopping = false;
@@ -70,20 +68,19 @@ export class MessagingClient {
70
68
  * Joins a specific table for communication.
71
69
  */
72
70
  async joinTable(tableId, userId) {
73
- let table = this.activeTables.find((t) => t.tableId === tableId);
74
- if (!table) {
75
- const lobby = this.activeLobbies.find((l) => l.currentUser.userId === userId);
76
- if (!lobby) {
77
- throw new Error(`Cannot join table: No active lobby found for user ${userId}`);
78
- }
79
- table = new Table(this.nchan, tableId, userId, lobby);
80
- await table.join();
81
- this.activeTables.push(table);
82
- await lobby.updatePresence({ tableId });
71
+ const existingTable = this.activeTables.find((t) => t.tableId === tableId);
72
+ if (existingTable) {
73
+ await existingTable.join();
74
+ return existingTable;
83
75
  }
84
- else {
85
- await table.join();
76
+ const lobby = this.activeLobbies.find((l) => l.currentUser.userId === userId);
77
+ if (!lobby) {
78
+ throw new Error(`Cannot join table: No active lobby found for user ${userId}`);
86
79
  }
80
+ const table = new Table(this.nchan, tableId, userId, lobby);
81
+ await table.join();
82
+ this.activeTables.push(table);
83
+ await lobby.updatePresence({ tableId });
87
84
  return table;
88
85
  }
89
86
  handlePageHide = () => {
@@ -91,10 +88,15 @@ export class MessagingClient {
91
88
  // Non-blocking call because pagehide might terminate the process
92
89
  this.stop({ isTeardown: true });
93
90
  };
94
- handlePageShow = (event) => {
91
+ handlePageShow = async (event) => {
95
92
  // If returning via bfcache, restore connections
96
93
  if (event.persisted && this.lastLobbyConfig) {
97
- this.joinLobby(this.lastLobbyConfig.user, this.lastLobbyConfig.options);
94
+ try {
95
+ await this.joinLobby(this.lastLobbyConfig.user, this.lastLobbyConfig.options);
96
+ }
97
+ catch (e) {
98
+ console.error("Failed to restore lobby on pageshow:", e);
99
+ }
98
100
  }
99
101
  };
100
102
  handleVisibilityChange = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"messagingclient.js","sourceRoot":"","sources":["../src/messagingclient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messagingclient.js","sourceRoot":"","sources":["../src/messagingclient.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,29 @@
1
+ const PATHS = {
2
+ PRESENCE_PUBLISH: "/publish/status/lobby",
3
+ PRESENCE_SUBSCRIBE: "/subscribe/status/lobby",
4
+ TABLE_PUBLISH: (tableId) => `/publish/table/${tableId}`,
5
+ TABLE_SUBSCRIBE: (tableId) => `/subscribe/table/${tableId}`,
6
+ };
1
7
  export class NchanClient {
2
8
  server;
3
9
  constructor(server) {
4
- // Ensure server string doesn't end with a slash and starts with protocol if missing
10
+ // Ensure server string doesn't end with a slash
5
11
  this.server = server.replace(/\/$/, "");
6
- if (!this.server.startsWith("http")) {
7
- this.server = `http://${this.server}`;
12
+ // If no protocol is provided, determine it based on the environment
13
+ if (!this.server.includes("://")) {
14
+ if (typeof window !== "undefined") {
15
+ // Use current page protocol if available
16
+ const protocol = window.location.protocol; // "http:" or "https:"
17
+ this.server = `${protocol}//${this.server}`;
18
+ }
19
+ else {
20
+ // Fallback for non-browser environments
21
+ this.server = `http://${this.server}`;
22
+ }
8
23
  }
9
24
  }
10
25
  getWsUrl(path) {
26
+ // Replace http with ws, and https with wss
11
27
  return this.server.replace(/^http/, "ws") + path;
12
28
  }
13
29
  getHttpUrl(path) {
@@ -28,29 +44,29 @@ export class NchanClient {
28
44
  }
29
45
  // Publishing
30
46
  async publishPresence(message, options) {
31
- return this.publish("/publish/presence/lobby", {
47
+ return this.publish(PATHS.PRESENCE_PUBLISH, {
32
48
  ...message,
33
49
  messageType: "presence",
34
50
  }, options);
35
51
  }
36
52
  async publishChallenge(message, options) {
37
- return this.publish("/publish/presence/lobby", {
53
+ return this.publish(PATHS.PRESENCE_PUBLISH, {
38
54
  ...message,
39
55
  messageType: "challenge",
40
56
  }, options);
41
57
  }
42
58
  async publishTable(tableId, message, senderId, options) {
43
- return this.publish(`/publish/table/${tableId}`, {
59
+ return this.publish(PATHS.TABLE_PUBLISH(tableId), {
44
60
  ...message,
45
61
  senderId,
46
62
  }, options);
47
63
  }
48
64
  // Subscribing
49
65
  subscribePresence(onMessage) {
50
- return this.subscribe("/subscribe/presence/lobby", onMessage);
66
+ return this.subscribe(PATHS.PRESENCE_SUBSCRIBE, onMessage);
51
67
  }
52
68
  subscribeTable(tableId, onMessage) {
53
- return this.subscribe(`/subscribe/table/${tableId}`, onMessage);
69
+ return this.subscribe(PATHS.TABLE_SUBSCRIBE(tableId), onMessage);
54
70
  }
55
71
  subscribe(path, onMessage) {
56
72
  const url = this.getWsUrl(path);
@@ -83,6 +99,7 @@ export class NchanClient {
83
99
  const delay = Math.min(Math.pow(2, reconnectAttempts) * 1000, maxReconnectDelay);
84
100
  reconnectAttempts++;
85
101
  reconnectTimer = setTimeout(connect, delay);
102
+ reconnectTimer.unref?.();
86
103
  }
87
104
  };
88
105
  ws.onerror = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"nchanclient.js","sourceRoot":"","sources":["../src/nchanclient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nchanclient.js","sourceRoot":"","sources":["../src/nchanclient.ts"],"names":[],"mappings":"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"}
package/dist/table.d.ts CHANGED
@@ -3,6 +3,7 @@ import { TableMessage, PresenceMessage } from "./types";
3
3
  import { Lobby } from "./lobby";
4
4
  /**
5
5
  * Represents a specific communication channel for a 2-player/spectator scenario at a table.
6
+ * Uses `any` as default for internal storage flexibility; consumers should use `unknown` or specific types.
6
7
  */
7
8
  export declare class Table<T = any> {
8
9
  private nchan;
package/dist/table.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { parseMessage } from "./types";
2
2
  /**
3
3
  * Represents a specific communication channel for a 2-player/spectator scenario at a table.
4
+ * Uses `any` as default for internal storage flexibility; consumers should use `unknown` or specific types.
4
5
  */
5
6
  export class Table {
6
7
  nchan;
@@ -72,16 +73,16 @@ export class Table {
72
73
  * Leave the table and stop all subscriptions.
73
74
  */
74
75
  async leave(options = {}) {
75
- try {
76
- // Explicitly notify the opponent we are leaving
77
- await this.nchan.publishTable(this.tableId, { type: "SYSTEM_DISCONNECT", data: {} }, this.userId, { keepalive: options.isTeardown });
78
- if (!options.isTeardown) {
79
- // Small delay to ensure the message is dispatched before closing the socket
76
+ // Only send leave message on explicit user action, not on page hide/teardown
77
+ if (!options.isTeardown) {
78
+ try {
79
+ await this.nchan.publishTable(this.tableId, { type: "table:leave", data: {} }, this.userId);
80
+ // Small delay to ensure message is dispatched before closing the socket
80
81
  await new Promise((r) => setTimeout(r, 100));
81
82
  }
82
- }
83
- catch (e) {
84
- console.error("Error leaving table:", e);
83
+ catch (e) {
84
+ console.error("Error leaving table:", e);
85
+ }
85
86
  }
86
87
  // Clear lobby presence if we have one
87
88
  if (this.lobby) {
@@ -99,7 +100,7 @@ export class Table {
99
100
  if (!msg || !msg.type)
100
101
  return;
101
102
  // Handle system messages internally
102
- if (msg.type === "SYSTEM_DISCONNECT" && msg.senderId !== this.userId) {
103
+ if (msg.type === "table:leave" && msg.senderId !== this.userId) {
103
104
  this.notifyOpponentLeft();
104
105
  }
105
106
  // Notify message listeners
package/dist/table.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Server-enriched metadata added to all messages by Nchan.
3
3
  * This is the absolute source of truth for timing and origin.
4
4
  */
5
- export interface _Meta {
5
+ export interface Meta {
6
6
  ts: string;
7
7
  locale: string;
8
8
  ua: string;
@@ -11,6 +11,7 @@ export interface _Meta {
11
11
  host: string;
12
12
  path: string;
13
13
  method: string;
14
+ country: string;
14
15
  }
15
16
  /**
16
17
  * Seek object for table seeking in the lobby
@@ -31,7 +32,7 @@ export interface PresenceMessage {
31
32
  opponentId?: string | null;
32
33
  seek?: Seek;
33
34
  lastSeen?: number;
34
- _meta?: _Meta;
35
+ _meta?: Meta;
35
36
  tableId?: string;
36
37
  }
37
38
  /**
@@ -45,16 +46,16 @@ export interface ChallengeMessage {
45
46
  recipientId: string;
46
47
  ruleType: string;
47
48
  tableId?: string;
48
- _meta?: _Meta;
49
+ _meta?: Meta;
49
50
  }
50
51
  /**
51
52
  * Generic structure for table/game events
52
53
  */
53
- export interface TableMessage<T = any> {
54
+ export interface TableMessage<T = unknown> {
54
55
  type: string;
55
56
  senderId: string;
56
57
  data: T;
57
- _meta?: _Meta;
58
+ _meta?: Meta;
58
59
  }
59
60
  /**
60
61
  * Lobby-level information about an active game table
@@ -70,6 +71,17 @@ export interface TableInfo {
70
71
  status: "waiting" | "playing" | "finished";
71
72
  createdAt: number;
72
73
  }
74
+ /**
75
+ * Derived active game from presence list
76
+ */
77
+ export interface ActiveGame {
78
+ tableId: string;
79
+ players: {
80
+ id: string;
81
+ name: string;
82
+ }[];
83
+ ruleType?: string;
84
+ }
73
85
  /**
74
86
  * Union type for messages received via the lobby channel
75
87
  */
@@ -79,6 +91,21 @@ export type LobbyIncomingMessage = PresenceMessage | ChallengeMessage;
79
91
  */
80
92
  export declare function isPresenceMessage(msg: any): msg is PresenceMessage;
81
93
  export declare function isChallengeMessage(msg: any): msg is ChallengeMessage;
94
+ /**
95
+ * Predicate: can the current user challenge this target?
96
+ * Returns true if target is not self, not in a game, and not seeking
97
+ */
98
+ export declare function canChallenge(target: PresenceMessage, currentUserId: string): boolean;
99
+ /**
100
+ * Predicate: can the current user spectate this target's game?
101
+ * Returns true if target is at a table and it's not the current user's table
102
+ */
103
+ export declare function canSpectate(target: PresenceMessage, currentTableId?: string): boolean;
104
+ /**
105
+ * Filter: derive active games from presence list
106
+ * Returns unique games (one entry per tableId) with their players
107
+ */
108
+ export declare function activeGames(users: PresenceMessage[]): ActiveGame[];
82
109
  /**
83
110
  * Helper to parse incoming Nchan JSON strings
84
111
  */
package/dist/types.js CHANGED
@@ -7,6 +7,46 @@ export function isPresenceMessage(msg) {
7
7
  export function isChallengeMessage(msg) {
8
8
  return msg?.messageType === "challenge";
9
9
  }
10
+ /**
11
+ * Predicate: can the current user challenge this target?
12
+ * Returns true if target is not self, not in a game, and not seeking
13
+ */
14
+ export function canChallenge(target, currentUserId) {
15
+ return (target.userId !== currentUserId &&
16
+ !target.tableId &&
17
+ !target.seek);
18
+ }
19
+ /**
20
+ * Predicate: can the current user spectate this target's game?
21
+ * Returns true if target is at a table and it's not the current user's table
22
+ */
23
+ export function canSpectate(target, currentTableId) {
24
+ return (!!target.tableId &&
25
+ target.tableId !== currentTableId);
26
+ }
27
+ /**
28
+ * Filter: derive active games from presence list
29
+ * Returns unique games (one entry per tableId) with their players
30
+ */
31
+ export function activeGames(users) {
32
+ const gameMap = new Map();
33
+ for (const user of users) {
34
+ if (user.tableId) {
35
+ if (!gameMap.has(user.tableId)) {
36
+ gameMap.set(user.tableId, {
37
+ tableId: user.tableId,
38
+ players: [],
39
+ ruleType: user.ruleType,
40
+ });
41
+ }
42
+ gameMap.get(user.tableId).players.push({
43
+ id: user.userId,
44
+ name: user.userName,
45
+ });
46
+ }
47
+ }
48
+ return Array.from(gameMap.values());
49
+ }
10
50
  /**
11
51
  * Helper to parse incoming Nchan JSON strings
12
52
  */
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAiFA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAQ;IACxC,OAAO,GAAG,EAAE,WAAW,KAAK,UAAU,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAQ;IACzC,OAAO,GAAG,EAAE,WAAW,KAAK,WAAW,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAI,IAAY;IAC1C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA2FA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAQ;IACxC,OAAO,GAAG,EAAE,WAAW,KAAK,UAAU,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAQ;IACzC,OAAO,GAAG,EAAE,WAAW,KAAK,WAAW,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAuB,EAAE,aAAqB;IACzE,OAAO,CACL,MAAM,CAAC,MAAM,KAAK,aAAa;QAC/B,CAAC,MAAM,CAAC,OAAO;QACf,CAAC,MAAM,CAAC,IAAI,CACb,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAAuB,EAAE,cAAuB;IAC1E,OAAO,CACL,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,MAAM,CAAC,OAAO,KAAK,cAAc,CAClC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAwB;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;IAE9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE;oBACxB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,OAAO,EAAE,EAAE;oBACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;iBACxB,CAAC,CAAC;YACL,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAE,CAAC,OAAO,CAAC,IAAI,CAAC;gBACtC,EAAE,EAAE,IAAI,CAAC,MAAM;gBACf,IAAI,EAAE,IAAI,CAAC,QAAQ;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAI,IAAY;IAC1C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,CAAC,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailuge/messaging",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "A stateful messaging library for Nchan-powered real-time applications.",
6
6
  "main": "./dist/index.js",
@@ -12,10 +12,12 @@
12
12
  }
13
13
  },
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "MESSAGING_SPEC.md",
17
+ "SKILL.md"
16
18
  ],
17
19
  "scripts": {
18
- "test": "jest --config test/jest.config.cjs",
20
+ "test": "jest --config test/jest.config.cjs --verbose",
19
21
  "coverage": "jest --config test/jest.config.cjs --coverage",
20
22
  "playwright": "npx playwright test playwright/debug-connection.spec.ts",
21
23
  "test:debug": "npm run playwright",
@@ -23,13 +25,15 @@
23
25
  "prettify": "npx oxfmt src test",
24
26
  "build": "tsc --declaration",
25
27
  "release": "npm version minor --no-git-tag-version && npm run build",
26
- "build:all": "bash scripts/docker-nchan.sh build",
27
- "build:example": "npx esbuild example/src/client.ts --bundle --outfile=example/dist/client.js",
28
- "example": "npm run build:example && npx http-server example -p 3000",
28
+ "build:all": "npm run build && npm run build:example && npm run docker:start",
29
+ "build:example": "mkdir -p docker/html/example && npx esbuild example/src/client.ts --bundle --outfile=docker/html/example/client.js && cp example/*.html docker/html/example/",
30
+ "example": "npm run build:example && npx http-server docker/html/example -p 3000",
29
31
  "docker:start": "bash scripts/docker-nchan.sh",
30
32
  "docker:stop": "bash scripts/docker-nchan.sh stop",
31
33
  "docker:build": "npm run build:all",
32
- "deps": "npx npm-check"
34
+ "docker:push": "docker push tailuge/billiards-network:3.0.0 && bash scripts/trigger_render.sh",
35
+ "deps": "npx npm-check",
36
+ "docker:logs": "bash scripts/docker-nchan.sh logs"
33
37
  },
34
38
  "keywords": [
35
39
  "messaging",
@@ -44,10 +48,10 @@
44
48
  "@playwright/test": "1.58.2",
45
49
  "@types/jest": "30.0.0",
46
50
  "@types/ws": "^8.18.1",
47
- "esbuild": "^0.27.3",
51
+ "esbuild": "^0.27.4",
48
52
  "http-server": "^14.1.1",
49
53
  "jest": "30.3.0",
50
- "oxlint": "^1.52.0",
54
+ "oxlint": "^1.55.0",
51
55
  "testcontainers": "^11.12.0",
52
56
  "ts-jest": "^29.4.6",
53
57
  "typescript": "5.9.3",