@tailuge/messaging 1.2.0 → 1.3.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.
- package/SKILL.md +37 -116
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: tailuge-messaging
|
|
3
|
+
description: |
|
|
4
|
+
Integration guide for @tailuge/messaging library. Use when building applications with:
|
|
5
|
+
- Real-time presence/lobby systems
|
|
6
|
+
- User matchmaking via challenges
|
|
7
|
+
- Game table communication with players and spectators
|
|
8
|
+
- Nchan-powered transport layer
|
|
9
|
+
---
|
|
2
10
|
|
|
3
|
-
|
|
11
|
+
# @tailuge/messaging
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
Quick integration guide. See [MESSAGING_SPEC.md](./MESSAGING_SPEC.md) for full API contract.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
6
16
|
|
|
7
17
|
```bash
|
|
8
18
|
npm install @tailuge/messaging
|
|
9
19
|
```
|
|
10
20
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
Create a `MessagingClient` instance with your Nchan server base URL:
|
|
21
|
+
## Quick Start
|
|
14
22
|
|
|
15
23
|
```typescript
|
|
16
24
|
import { MessagingClient } from '@tailuge/messaging';
|
|
17
25
|
|
|
18
|
-
const client = new MessagingClient({
|
|
19
|
-
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
await client.start();
|
|
26
|
+
const client = new MessagingClient({ baseUrl: "https://your-nchan-server.com" });
|
|
27
|
+
client.start();
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
Join the lobby and listen for user changes:
|
|
30
|
+
## Lobby & Presence
|
|
28
31
|
|
|
29
32
|
```typescript
|
|
30
33
|
const lobby = await client.joinLobby({
|
|
@@ -35,57 +38,27 @@ const lobby = await client.joinLobby({
|
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
lobby.onUsersChange((users) => {
|
|
38
|
-
console.log(
|
|
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
|
-
}
|
|
41
|
+
console.log(`Online: ${users.length}`);
|
|
42
|
+
users.forEach(u => {
|
|
43
|
+
const flag = countryToFlag(u._meta?.country);
|
|
44
|
+
console.log(`${flag} ${u.userName}`);
|
|
54
45
|
});
|
|
55
46
|
});
|
|
56
47
|
```
|
|
57
48
|
|
|
58
|
-
##
|
|
59
|
-
|
|
60
|
-
To challenge another user to a game:
|
|
49
|
+
## Challenge Opponent
|
|
61
50
|
|
|
62
51
|
```typescript
|
|
63
|
-
// challenge(userId, ruleType) returns the table ID
|
|
64
52
|
const tableId = await lobby.challenge(targetUserId, "billiards");
|
|
65
|
-
console.log(`Challenge sent, table created: ${tableId}`);
|
|
66
|
-
```
|
|
67
53
|
|
|
68
|
-
## Receiving Challenges
|
|
69
|
-
|
|
70
|
-
Listen for incoming challenges:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
54
|
lobby.onChallenge((challenge) => {
|
|
74
|
-
console.log(`${challenge.challengerName} challenged you to ${challenge.ruleType}`);
|
|
75
|
-
|
|
76
55
|
if (challenge.type === "offer") {
|
|
77
|
-
// Accept the challenge
|
|
78
56
|
lobby.acceptChallenge(challenge.challengerId, challenge.ruleType, challenge.tableId);
|
|
79
|
-
|
|
80
|
-
// Or decline
|
|
81
|
-
// lobby.declineChallenge(challenge.challengerId, challenge.ruleType);
|
|
82
57
|
}
|
|
83
58
|
});
|
|
84
59
|
```
|
|
85
60
|
|
|
86
|
-
##
|
|
87
|
-
|
|
88
|
-
Connect to a specific game table:
|
|
61
|
+
## Table Messaging
|
|
89
62
|
|
|
90
63
|
```typescript
|
|
91
64
|
interface Move { x: number; y: number }
|
|
@@ -93,18 +66,14 @@ const table = await client.joinTable<Move>("table-xyz", "user-123");
|
|
|
93
66
|
|
|
94
67
|
table.onMessage((msg) => {
|
|
95
68
|
if (msg.type === "MOVE") {
|
|
96
|
-
|
|
97
|
-
console.log(`Move received at: ${msg._meta?.ts}`);
|
|
69
|
+
console.log(`Move at: ${msg._meta?.ts}`);
|
|
98
70
|
}
|
|
99
71
|
});
|
|
100
72
|
|
|
101
|
-
// Send a move
|
|
102
73
|
await table.publish("MOVE", { x: 10, y: 20 });
|
|
103
74
|
```
|
|
104
75
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
Listen for spectator changes at a table:
|
|
76
|
+
## Spectators
|
|
108
77
|
|
|
109
78
|
```typescript
|
|
110
79
|
table.onSpectatorChange((spectators) => {
|
|
@@ -114,79 +83,31 @@ table.onSpectatorChange((spectators) => {
|
|
|
114
83
|
|
|
115
84
|
## Cleanup
|
|
116
85
|
|
|
117
|
-
When done, stop the client:
|
|
118
|
-
|
|
119
86
|
```typescript
|
|
120
87
|
await client.stop();
|
|
121
88
|
```
|
|
122
89
|
|
|
123
|
-
## Key
|
|
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
|
-
```
|
|
90
|
+
## Key Imports
|
|
165
91
|
|
|
166
|
-
### TableMessage<T>
|
|
167
92
|
```typescript
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
93
|
+
import {
|
|
94
|
+
MessagingClient,
|
|
95
|
+
canChallenge,
|
|
96
|
+
canSpectate,
|
|
97
|
+
activeGames,
|
|
98
|
+
} from '@tailuge/messaging';
|
|
174
99
|
```
|
|
175
100
|
|
|
176
|
-
##
|
|
177
|
-
|
|
178
|
-
The library exports helper functions:
|
|
101
|
+
## Predicates
|
|
179
102
|
|
|
180
103
|
```typescript
|
|
181
|
-
import { canChallenge, canSpectate } from '@tailuge/messaging';
|
|
182
|
-
|
|
183
|
-
// Check if you can challenge a user
|
|
184
104
|
if (canChallenge(targetUser, currentUserId)) {
|
|
185
|
-
lobby.challenge(targetUser.userId, "billiards");
|
|
105
|
+
await lobby.challenge(targetUser.userId, "billiards");
|
|
186
106
|
}
|
|
187
107
|
|
|
188
|
-
// Check if you can spectate a user
|
|
189
108
|
if (canSpectate(targetUser, currentTableId)) {
|
|
190
|
-
|
|
109
|
+
await client.joinTable(targetUser.tableId, currentUserId);
|
|
191
110
|
}
|
|
192
111
|
```
|
|
112
|
+
|
|
113
|
+
See [MESSAGING_SPEC.md](./MESSAGING_SPEC.md) for complete interface definitions.
|