nostr-arena 0.1.1
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/LICENSE +21 -0
- package/README.md +188 -0
- package/dist/__tests__/callbacks.test.d.ts +28 -0
- package/dist/__tests__/callbacks.test.d.ts.map +1 -0
- package/dist/__tests__/callbacks.test.js +140 -0
- package/dist/__tests__/callbacks.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +18 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +43 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/error-handling.test.d.ts +14 -0
- package/dist/__tests__/error-handling.test.d.ts.map +1 -0
- package/dist/__tests__/error-handling.test.js +199 -0
- package/dist/__tests__/error-handling.test.js.map +1 -0
- package/dist/__tests__/protocol.test.d.ts +7 -0
- package/dist/__tests__/protocol.test.d.ts.map +1 -0
- package/dist/__tests__/protocol.test.js +257 -0
- package/dist/__tests__/protocol.test.js.map +1 -0
- package/dist/__tests__/state-flow.test.d.ts +37 -0
- package/dist/__tests__/state-flow.test.d.ts.map +1 -0
- package/dist/__tests__/state-flow.test.js +160 -0
- package/dist/__tests__/state-flow.test.js.map +1 -0
- package/dist/__tests__/timing.test.d.ts +20 -0
- package/dist/__tests__/timing.test.d.ts.map +1 -0
- package/dist/__tests__/timing.test.js +73 -0
- package/dist/__tests__/timing.test.js.map +1 -0
- package/dist/core/Arena.d.ts +164 -0
- package/dist/core/Arena.d.ts.map +1 -0
- package/dist/core/Arena.js +634 -0
- package/dist/core/Arena.js.map +1 -0
- package/dist/core/NostrClient.d.ts +108 -0
- package/dist/core/NostrClient.d.ts.map +1 -0
- package/dist/core/NostrClient.js +225 -0
- package/dist/core/NostrClient.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +36 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +98 -0
- package/dist/proxy.js.map +1 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +6 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useArena.d.ts +74 -0
- package/dist/react/useArena.d.ts.map +1 -0
- package/dist/react/useArena.js +224 -0
- package/dist/react/useArena.js.map +1 -0
- package/dist/retry.d.ts +54 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +82 -0
- package/dist/retry.js.map +1 -0
- package/dist/testing/MockArena.d.ts +84 -0
- package/dist/testing/MockArena.d.ts.map +1 -0
- package/dist/testing/MockArena.js +156 -0
- package/dist/testing/MockArena.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types.d.ts +183 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +55 -0
- package/dist/types.js.map +1 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 kako-jun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# nostr-arena
|
|
2
|
+
|
|
3
|
+
Nostr-based real-time battle room for multiplayer games. No server required.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **P2P Matchmaking**: Create and join rooms via shareable URLs
|
|
8
|
+
- **Real-time State Sync**: Send game state to opponents with automatic throttling
|
|
9
|
+
- **Connection Health**: Heartbeat and disconnect detection
|
|
10
|
+
- **Reconnection**: Automatic reconnection from localStorage
|
|
11
|
+
- **Rematch**: Built-in rematch flow
|
|
12
|
+
- **Framework Agnostic**: Core classes work anywhere, React hooks included
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install nostr-arena nostr-tools
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### React
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { useArena } from 'nostr-arena/react';
|
|
26
|
+
|
|
27
|
+
interface MyGameState {
|
|
28
|
+
score: number;
|
|
29
|
+
position: { x: number; y: number };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function Game() {
|
|
33
|
+
const { roomState, opponent, createRoom, joinRoom, sendState, leaveRoom } =
|
|
34
|
+
useArena<MyGameState>({
|
|
35
|
+
gameId: 'my-game',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const handleCreate = async () => {
|
|
39
|
+
const url = await createRoom();
|
|
40
|
+
// Share this URL with opponent
|
|
41
|
+
navigator.clipboard.writeText(url);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleMove = (x: number, y: number) => {
|
|
45
|
+
sendState({ score: 100, position: { x, y } });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div>
|
|
50
|
+
<p>Status: {roomState.status}</p>
|
|
51
|
+
{roomState.status === 'idle' && <button onClick={handleCreate}>Create Room</button>}
|
|
52
|
+
{opponent && <p>Opponent score: {opponent.gameState?.score ?? 0}</p>}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Vanilla JavaScript
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Arena } from 'nostr-arena';
|
|
62
|
+
|
|
63
|
+
interface MyGameState {
|
|
64
|
+
score: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const room = new Arena<MyGameState>({
|
|
68
|
+
gameId: 'my-game',
|
|
69
|
+
relays: ['wss://relay.damus.io'],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Register event callbacks (chainable)
|
|
73
|
+
room
|
|
74
|
+
.onOpponentJoin((pubkey) => {
|
|
75
|
+
console.log('Opponent joined:', pubkey);
|
|
76
|
+
})
|
|
77
|
+
.onOpponentState((state) => {
|
|
78
|
+
console.log('Opponent score:', state.score);
|
|
79
|
+
})
|
|
80
|
+
.onOpponentDisconnect(() => {
|
|
81
|
+
console.log('Opponent disconnected');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Create a room
|
|
85
|
+
room.connect();
|
|
86
|
+
const url = await room.create();
|
|
87
|
+
console.log('Share this URL:', url);
|
|
88
|
+
|
|
89
|
+
// Send state updates
|
|
90
|
+
room.sendState({ score: 100 });
|
|
91
|
+
|
|
92
|
+
// Game over
|
|
93
|
+
room.sendGameOver('win', 500);
|
|
94
|
+
|
|
95
|
+
// Cleanup
|
|
96
|
+
room.disconnect();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API
|
|
100
|
+
|
|
101
|
+
### ArenaConfig
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface ArenaConfig {
|
|
105
|
+
gameId: string; // Required: unique game identifier
|
|
106
|
+
relays?: string[]; // Nostr relay URLs (default: public relays)
|
|
107
|
+
roomExpiry?: number; // Room expiration in ms (default: 600000 = 10 min)
|
|
108
|
+
heartbeatInterval?: number; // Heartbeat interval in ms (default: 3000)
|
|
109
|
+
disconnectThreshold?: number; // Disconnect threshold in ms (default: 10000)
|
|
110
|
+
stateThrottle?: number; // State update throttle in ms (default: 100)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Room States
|
|
115
|
+
|
|
116
|
+
| Status | Description |
|
|
117
|
+
| -------- | ------------------------------------- |
|
|
118
|
+
| idle | No room active |
|
|
119
|
+
| creating | Creating a new room |
|
|
120
|
+
| waiting | Waiting for opponent to join |
|
|
121
|
+
| joining | Joining an existing room |
|
|
122
|
+
| ready | Both players connected, ready to play |
|
|
123
|
+
| playing | Game in progress |
|
|
124
|
+
| finished | Game ended |
|
|
125
|
+
|
|
126
|
+
### Events (Callbacks)
|
|
127
|
+
|
|
128
|
+
| Event | Parameters | Description |
|
|
129
|
+
| ------------------ | -------------------------------- | -------------------------- |
|
|
130
|
+
| opponentJoin | (publicKey: string) | Opponent joined the room |
|
|
131
|
+
| opponentState | (state: TGameState) | Opponent sent state update |
|
|
132
|
+
| opponentDisconnect | () | Opponent disconnected |
|
|
133
|
+
| opponentGameOver | (reason: string, score?: number) | Opponent game over |
|
|
134
|
+
| rematchRequested | () | Opponent requested rematch |
|
|
135
|
+
| rematchStart | (newSeed: number) | Rematch starting |
|
|
136
|
+
| error | (error: Error) | Error occurred |
|
|
137
|
+
|
|
138
|
+
## Node.js / Proxy Support
|
|
139
|
+
|
|
140
|
+
For Node.js environments or when you need proxy support, call `configureProxy()` before creating any rooms:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { configureProxy, Arena } from 'nostr-arena';
|
|
144
|
+
|
|
145
|
+
// Call once at startup
|
|
146
|
+
configureProxy();
|
|
147
|
+
|
|
148
|
+
// Now create rooms as usual
|
|
149
|
+
const room = new Arena({ gameId: 'my-game' });
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This function:
|
|
153
|
+
|
|
154
|
+
- Configures the `ws` package for Node.js WebSocket support
|
|
155
|
+
- Reads proxy URL from environment variables: `HTTPS_PROXY`, `HTTP_PROXY`, or `ALL_PROXY`
|
|
156
|
+
- No-op in browser environments (browsers handle proxies at OS level)
|
|
157
|
+
|
|
158
|
+
**Required packages for Node.js:**
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm install ws # Required for Node.js
|
|
162
|
+
npm install https-proxy-agent # Required for proxy support
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Testing
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { MockArena } from 'nostr-arena/testing';
|
|
169
|
+
|
|
170
|
+
const mock = new MockArena<MyGameState>({ gameId: 'test' });
|
|
171
|
+
|
|
172
|
+
// Simulate opponent actions
|
|
173
|
+
mock.simulateOpponentJoin('pubkey123');
|
|
174
|
+
mock.simulateOpponentState({ score: 100 });
|
|
175
|
+
mock.simulateOpponentDisconnect();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## How It Works
|
|
179
|
+
|
|
180
|
+
1. **Room Creation**: Host publishes a replaceable event (kind 30078) with room info
|
|
181
|
+
2. **Joining**: Guest fetches room event, sends join notification (kind 25000)
|
|
182
|
+
3. **State Sync**: Players send ephemeral events (kind 25000) with game state
|
|
183
|
+
4. **Heartbeat**: Periodic heartbeat events detect disconnections
|
|
184
|
+
5. **Cleanup**: Ephemeral events are not stored by relays (no garbage)
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callbacks Tests
|
|
3
|
+
*
|
|
4
|
+
* Source: README.md - Events (Callbacks) table
|
|
5
|
+
*
|
|
6
|
+
* | Event | Parameters | Description |
|
|
7
|
+
* | ------------------ | -------------------------------- | -------------------------- |
|
|
8
|
+
* | opponentJoin | (publicKey: string) | Opponent joined the room |
|
|
9
|
+
* | opponentState | (state: TGameState) | Opponent sent state update |
|
|
10
|
+
* | opponentDisconnect | () | Opponent disconnected |
|
|
11
|
+
* | opponentGameOver | (reason: string, score?: number) | Opponent game over |
|
|
12
|
+
* | rematchRequested | () | Opponent requested rematch |
|
|
13
|
+
* | rematchStart | (newSeed: number) | Rematch starting |
|
|
14
|
+
* | error | (error: Error) | Error occurred |
|
|
15
|
+
*
|
|
16
|
+
* Source: architecture.md - Arena API
|
|
17
|
+
*
|
|
18
|
+
* All callback methods return `this` for chaining:
|
|
19
|
+
* - onOpponentJoin(callback): this
|
|
20
|
+
* - onOpponentState(callback): this
|
|
21
|
+
* - onOpponentDisconnect(callback): this
|
|
22
|
+
* - onOpponentGameOver(callback): this
|
|
23
|
+
* - onRematchRequested(callback): this
|
|
24
|
+
* - onRematchStart(callback): this
|
|
25
|
+
* - onError(callback): this
|
|
26
|
+
*/
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=callbacks.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callbacks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/callbacks.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callbacks Tests
|
|
3
|
+
*
|
|
4
|
+
* Source: README.md - Events (Callbacks) table
|
|
5
|
+
*
|
|
6
|
+
* | Event | Parameters | Description |
|
|
7
|
+
* | ------------------ | -------------------------------- | -------------------------- |
|
|
8
|
+
* | opponentJoin | (publicKey: string) | Opponent joined the room |
|
|
9
|
+
* | opponentState | (state: TGameState) | Opponent sent state update |
|
|
10
|
+
* | opponentDisconnect | () | Opponent disconnected |
|
|
11
|
+
* | opponentGameOver | (reason: string, score?: number) | Opponent game over |
|
|
12
|
+
* | rematchRequested | () | Opponent requested rematch |
|
|
13
|
+
* | rematchStart | (newSeed: number) | Rematch starting |
|
|
14
|
+
* | error | (error: Error) | Error occurred |
|
|
15
|
+
*
|
|
16
|
+
* Source: architecture.md - Arena API
|
|
17
|
+
*
|
|
18
|
+
* All callback methods return `this` for chaining:
|
|
19
|
+
* - onOpponentJoin(callback): this
|
|
20
|
+
* - onOpponentState(callback): this
|
|
21
|
+
* - onOpponentDisconnect(callback): this
|
|
22
|
+
* - onOpponentGameOver(callback): this
|
|
23
|
+
* - onRematchRequested(callback): this
|
|
24
|
+
* - onRematchStart(callback): this
|
|
25
|
+
* - onError(callback): this
|
|
26
|
+
*/
|
|
27
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
28
|
+
import { MockArena } from '../testing/MockArena';
|
|
29
|
+
describe('Callbacks (README.md, architecture.md)', () => {
|
|
30
|
+
let room;
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
room = new MockArena({ gameId: 'test-game' });
|
|
33
|
+
});
|
|
34
|
+
describe('opponentJoin: (publicKey: string)', () => {
|
|
35
|
+
it('should be called when opponent joins', async () => {
|
|
36
|
+
let receivedPubkey = null;
|
|
37
|
+
room.on('opponentJoin', (pubkey) => {
|
|
38
|
+
receivedPubkey = pubkey;
|
|
39
|
+
});
|
|
40
|
+
await room.create();
|
|
41
|
+
room.simulateOpponentJoin('opponent-abc123');
|
|
42
|
+
expect(receivedPubkey).toBe('opponent-abc123');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('opponentState: (state: TGameState)', () => {
|
|
46
|
+
it('should be called with game state when opponent sends state', async () => {
|
|
47
|
+
let receivedState = null;
|
|
48
|
+
room.on('opponentState', (state) => {
|
|
49
|
+
receivedState = state;
|
|
50
|
+
});
|
|
51
|
+
await room.create();
|
|
52
|
+
room.simulateOpponentJoin('opponent');
|
|
53
|
+
room.simulateOpponentState({ score: 250 });
|
|
54
|
+
expect(receivedState).toEqual({ score: 250 });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('opponentDisconnect: ()', () => {
|
|
58
|
+
it('should be called when opponent disconnects', async () => {
|
|
59
|
+
let disconnectCalled = false;
|
|
60
|
+
room.on('opponentDisconnect', () => {
|
|
61
|
+
disconnectCalled = true;
|
|
62
|
+
});
|
|
63
|
+
await room.create();
|
|
64
|
+
room.simulateOpponentJoin('opponent');
|
|
65
|
+
room.simulateOpponentDisconnect();
|
|
66
|
+
expect(disconnectCalled).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('opponentGameOver: (reason: string, score?: number)', () => {
|
|
70
|
+
it('should be called with reason when opponent ends game', async () => {
|
|
71
|
+
let receivedReason = null;
|
|
72
|
+
room.on('opponentGameOver', (reason) => {
|
|
73
|
+
receivedReason = reason;
|
|
74
|
+
});
|
|
75
|
+
await room.create();
|
|
76
|
+
room.simulateOpponentJoin('opponent');
|
|
77
|
+
room.simulateOpponentGameOver('surrender');
|
|
78
|
+
expect(receivedReason).toBe('surrender');
|
|
79
|
+
});
|
|
80
|
+
it('should receive optional score parameter', async () => {
|
|
81
|
+
let receivedScore;
|
|
82
|
+
room.on('opponentGameOver', (_reason, score) => {
|
|
83
|
+
receivedScore = score;
|
|
84
|
+
});
|
|
85
|
+
await room.create();
|
|
86
|
+
room.simulateOpponentJoin('opponent');
|
|
87
|
+
room.simulateOpponentGameOver('win', 1000);
|
|
88
|
+
expect(receivedScore).toBe(1000);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('rematchRequested: ()', () => {
|
|
92
|
+
it('should be called when opponent requests rematch', async () => {
|
|
93
|
+
let rematchRequested = false;
|
|
94
|
+
room.on('rematchRequested', () => {
|
|
95
|
+
rematchRequested = true;
|
|
96
|
+
});
|
|
97
|
+
await room.create();
|
|
98
|
+
room.simulateOpponentJoin('opponent');
|
|
99
|
+
room.sendGameOver('win');
|
|
100
|
+
room.simulateRematchRequested();
|
|
101
|
+
expect(rematchRequested).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('rematchStart: (newSeed: number)', () => {
|
|
105
|
+
it('should be called with new seed when rematch starts', async () => {
|
|
106
|
+
let receivedSeed = null;
|
|
107
|
+
room.on('rematchStart', (seed) => {
|
|
108
|
+
receivedSeed = seed;
|
|
109
|
+
});
|
|
110
|
+
await room.create();
|
|
111
|
+
room.simulateOpponentJoin('opponent');
|
|
112
|
+
room.sendGameOver('win');
|
|
113
|
+
room.simulateRematchRequested();
|
|
114
|
+
room.acceptRematch();
|
|
115
|
+
expect(receivedSeed).not.toBeNull();
|
|
116
|
+
expect(typeof receivedSeed).toBe('number');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('Chaining (architecture.md)', () => {
|
|
120
|
+
/**
|
|
121
|
+
* Source: architecture.md - Arena API
|
|
122
|
+
* All callback methods return `this` for chaining
|
|
123
|
+
*/
|
|
124
|
+
it('on() should return this for chaining', () => {
|
|
125
|
+
const result = room.on('opponentJoin', () => { });
|
|
126
|
+
expect(result).toBe(room);
|
|
127
|
+
});
|
|
128
|
+
it('multiple callbacks can be chained', () => {
|
|
129
|
+
const result = room
|
|
130
|
+
.on('opponentJoin', () => { })
|
|
131
|
+
.on('opponentState', () => { })
|
|
132
|
+
.on('opponentDisconnect', () => { })
|
|
133
|
+
.on('opponentGameOver', () => { })
|
|
134
|
+
.on('rematchRequested', () => { })
|
|
135
|
+
.on('rematchStart', () => { });
|
|
136
|
+
expect(result).toBe(room);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=callbacks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callbacks.test.js","sourceRoot":"","sources":["../../src/__tests__/callbacks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArenaConfig Tests
|
|
3
|
+
*
|
|
4
|
+
* Source: README.md - ArenaConfig section
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* interface ArenaConfig {
|
|
8
|
+
* gameId: string; // Required: unique game identifier
|
|
9
|
+
* relays?: string[]; // Nostr relay URLs (default: public relays)
|
|
10
|
+
* roomExpiry?: number; // Room expiration in ms (default: 600000 = 10 min)
|
|
11
|
+
* heartbeatInterval?: number; // Heartbeat interval in ms (default: 3000)
|
|
12
|
+
* disconnectThreshold?: number; // Disconnect threshold in ms (default: 10000)
|
|
13
|
+
* stateThrottle?: number; // State update throttle in ms (default: 100)
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=config.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArenaConfig Tests
|
|
3
|
+
*
|
|
4
|
+
* Source: README.md - ArenaConfig section
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* interface ArenaConfig {
|
|
8
|
+
* gameId: string; // Required: unique game identifier
|
|
9
|
+
* relays?: string[]; // Nostr relay URLs (default: public relays)
|
|
10
|
+
* roomExpiry?: number; // Room expiration in ms (default: 600000 = 10 min)
|
|
11
|
+
* heartbeatInterval?: number; // Heartbeat interval in ms (default: 3000)
|
|
12
|
+
* disconnectThreshold?: number; // Disconnect threshold in ms (default: 10000)
|
|
13
|
+
* stateThrottle?: number; // State update throttle in ms (default: 100)
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { describe, it, expect } from 'vitest';
|
|
18
|
+
import { DEFAULT_CONFIG } from '../types';
|
|
19
|
+
describe('ArenaConfig defaults (README.md)', () => {
|
|
20
|
+
// "relays?: string[] // Nostr relay URLs (default: public relays)"
|
|
21
|
+
it('relays should default to public relays', () => {
|
|
22
|
+
expect(DEFAULT_CONFIG.relays).toBeDefined();
|
|
23
|
+
expect(Array.isArray(DEFAULT_CONFIG.relays)).toBe(true);
|
|
24
|
+
expect(DEFAULT_CONFIG.relays.length).toBeGreaterThan(0);
|
|
25
|
+
});
|
|
26
|
+
// "roomExpiry?: number // Room expiration in ms (default: 600000 = 10 min)"
|
|
27
|
+
it('roomExpiry should default to 600000ms (10 min)', () => {
|
|
28
|
+
expect(DEFAULT_CONFIG.roomExpiry).toBe(600000);
|
|
29
|
+
});
|
|
30
|
+
// "heartbeatInterval?: number // Heartbeat interval in ms (default: 3000)"
|
|
31
|
+
it('heartbeatInterval should default to 3000ms', () => {
|
|
32
|
+
expect(DEFAULT_CONFIG.heartbeatInterval).toBe(3000);
|
|
33
|
+
});
|
|
34
|
+
// "disconnectThreshold?: number // Disconnect threshold in ms (default: 10000)"
|
|
35
|
+
it('disconnectThreshold should default to 10000ms', () => {
|
|
36
|
+
expect(DEFAULT_CONFIG.disconnectThreshold).toBe(10000);
|
|
37
|
+
});
|
|
38
|
+
// "stateThrottle?: number // State update throttle in ms (default: 100)"
|
|
39
|
+
it('stateThrottle should default to 100ms', () => {
|
|
40
|
+
expect(DEFAULT_CONFIG.stateThrottle).toBe(100);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,mEAAmE;IACnE,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,gFAAgF;IAChF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Tests
|
|
3
|
+
*
|
|
4
|
+
* Source: architecture.md - Error Handling section
|
|
5
|
+
*
|
|
6
|
+
* Errors are reported via `onError` callback:
|
|
7
|
+
*
|
|
8
|
+
* Common errors:
|
|
9
|
+
* - `"Room not found"` - Room doesn't exist or expired
|
|
10
|
+
* - `"Room has expired"` - Room older than 10 minutes
|
|
11
|
+
* - `"NostrClient not connected"` - Called method before connect()
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=error-handling.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/error-handling.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|