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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/dist/__tests__/callbacks.test.d.ts +28 -0
  4. package/dist/__tests__/callbacks.test.d.ts.map +1 -0
  5. package/dist/__tests__/callbacks.test.js +140 -0
  6. package/dist/__tests__/callbacks.test.js.map +1 -0
  7. package/dist/__tests__/config.test.d.ts +18 -0
  8. package/dist/__tests__/config.test.d.ts.map +1 -0
  9. package/dist/__tests__/config.test.js +43 -0
  10. package/dist/__tests__/config.test.js.map +1 -0
  11. package/dist/__tests__/error-handling.test.d.ts +14 -0
  12. package/dist/__tests__/error-handling.test.d.ts.map +1 -0
  13. package/dist/__tests__/error-handling.test.js +199 -0
  14. package/dist/__tests__/error-handling.test.js.map +1 -0
  15. package/dist/__tests__/protocol.test.d.ts +7 -0
  16. package/dist/__tests__/protocol.test.d.ts.map +1 -0
  17. package/dist/__tests__/protocol.test.js +257 -0
  18. package/dist/__tests__/protocol.test.js.map +1 -0
  19. package/dist/__tests__/state-flow.test.d.ts +37 -0
  20. package/dist/__tests__/state-flow.test.d.ts.map +1 -0
  21. package/dist/__tests__/state-flow.test.js +160 -0
  22. package/dist/__tests__/state-flow.test.js.map +1 -0
  23. package/dist/__tests__/timing.test.d.ts +20 -0
  24. package/dist/__tests__/timing.test.d.ts.map +1 -0
  25. package/dist/__tests__/timing.test.js +73 -0
  26. package/dist/__tests__/timing.test.js.map +1 -0
  27. package/dist/core/Arena.d.ts +164 -0
  28. package/dist/core/Arena.d.ts.map +1 -0
  29. package/dist/core/Arena.js +634 -0
  30. package/dist/core/Arena.js.map +1 -0
  31. package/dist/core/NostrClient.d.ts +108 -0
  32. package/dist/core/NostrClient.d.ts.map +1 -0
  33. package/dist/core/NostrClient.js +225 -0
  34. package/dist/core/NostrClient.js.map +1 -0
  35. package/dist/core/index.d.ts +8 -0
  36. package/dist/core/index.d.ts.map +1 -0
  37. package/dist/core/index.js +7 -0
  38. package/dist/core/index.js.map +1 -0
  39. package/dist/index.d.ts +35 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +37 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/proxy.d.ts +36 -0
  44. package/dist/proxy.d.ts.map +1 -0
  45. package/dist/proxy.js +98 -0
  46. package/dist/proxy.js.map +1 -0
  47. package/dist/react/index.d.ts +7 -0
  48. package/dist/react/index.d.ts.map +1 -0
  49. package/dist/react/index.js +6 -0
  50. package/dist/react/index.js.map +1 -0
  51. package/dist/react/useArena.d.ts +74 -0
  52. package/dist/react/useArena.d.ts.map +1 -0
  53. package/dist/react/useArena.js +224 -0
  54. package/dist/react/useArena.js.map +1 -0
  55. package/dist/retry.d.ts +54 -0
  56. package/dist/retry.d.ts.map +1 -0
  57. package/dist/retry.js +82 -0
  58. package/dist/retry.js.map +1 -0
  59. package/dist/testing/MockArena.d.ts +84 -0
  60. package/dist/testing/MockArena.d.ts.map +1 -0
  61. package/dist/testing/MockArena.js +156 -0
  62. package/dist/testing/MockArena.js.map +1 -0
  63. package/dist/testing/index.d.ts +6 -0
  64. package/dist/testing/index.d.ts.map +1 -0
  65. package/dist/testing/index.js +6 -0
  66. package/dist/testing/index.js.map +1 -0
  67. package/dist/types.d.ts +183 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +55 -0
  70. package/dist/types.js.map +1 -0
  71. package/package.json +84 -0
@@ -0,0 +1,199 @@
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
+ import { describe, it, expect } from 'vitest';
14
+ import { withRetry, withTimeout, timeout } from '../retry';
15
+ describe('Error Handling (architecture.md)', () => {
16
+ describe('Error Messages', () => {
17
+ /**
18
+ * Source: architecture.md - Error Handling section
19
+ */
20
+ it('"Room not found" error should exist', () => {
21
+ const error = new Error('Room not found');
22
+ expect(error.message).toBe('Room not found');
23
+ });
24
+ it('"Room has expired" error should exist', () => {
25
+ const error = new Error('Room has expired');
26
+ expect(error.message).toBe('Room has expired');
27
+ });
28
+ it('"NostrClient not connected" error should exist', () => {
29
+ const error = new Error('NostrClient not connected. Call connect() first.');
30
+ expect(error.message).toContain('NostrClient not connected');
31
+ });
32
+ it('"Invalid room data" error should exist', () => {
33
+ const error = new Error('Invalid room data');
34
+ expect(error.message).toBe('Invalid room data');
35
+ });
36
+ it('"Invalid room data: missing required fields" error should exist', () => {
37
+ const error = new Error('Invalid room data: missing required fields');
38
+ expect(error.message).toContain('missing required fields');
39
+ });
40
+ });
41
+ describe('Timeout Errors', () => {
42
+ it('"Join operation timed out" error should exist', () => {
43
+ const error = new Error('Join operation timed out');
44
+ expect(error.message).toBe('Join operation timed out');
45
+ });
46
+ it('"Create operation timed out" error should exist', () => {
47
+ const error = new Error('Create operation timed out');
48
+ expect(error.message).toBe('Create operation timed out');
49
+ });
50
+ it('"Reconnect operation timed out" error should exist', () => {
51
+ const error = new Error('Reconnect operation timed out');
52
+ expect(error.message).toBe('Reconnect operation timed out');
53
+ });
54
+ });
55
+ describe('Storage Error Resilience', () => {
56
+ it('storage errors should not crash the application', () => {
57
+ // Storage operations are wrapped in try-catch
58
+ // and should fail gracefully
59
+ const mockStorage = {
60
+ getItem: () => {
61
+ throw new Error('QuotaExceededError');
62
+ },
63
+ setItem: () => {
64
+ throw new Error('QuotaExceededError');
65
+ },
66
+ removeItem: () => {
67
+ throw new Error('QuotaExceededError');
68
+ },
69
+ };
70
+ // Just verifying the pattern exists - actual integration tested elsewhere
71
+ expect(() => {
72
+ try {
73
+ mockStorage.setItem();
74
+ }
75
+ catch {
76
+ // Should be caught and handled
77
+ }
78
+ }).not.toThrow();
79
+ });
80
+ });
81
+ describe('Relay Connection Errors', () => {
82
+ it('"No relay response" error should exist for timeout', () => {
83
+ const error = new Error('No relay response: timeout');
84
+ expect(error.message).toContain('No relay response');
85
+ });
86
+ it('"No relay response" error should exist for connection failure', () => {
87
+ const error = new Error('No relay response: connection refused');
88
+ expect(error.message).toContain('No relay response');
89
+ });
90
+ it('"Failed to connect to relays" error should exist', () => {
91
+ const error = new Error('Failed to connect to relays: No relay response: timeout');
92
+ expect(error.message).toContain('Failed to connect to relays');
93
+ });
94
+ });
95
+ });
96
+ describe('Retry Utilities', () => {
97
+ describe('withRetry', () => {
98
+ it('should succeed on first attempt if operation succeeds', async () => {
99
+ let attempts = 0;
100
+ const result = await withRetry(async () => {
101
+ attempts++;
102
+ return 'success';
103
+ });
104
+ expect(result).toBe('success');
105
+ expect(attempts).toBe(1);
106
+ });
107
+ it('should retry on failure', async () => {
108
+ let attempts = 0;
109
+ const result = await withRetry(async () => {
110
+ attempts++;
111
+ if (attempts < 3) {
112
+ throw new Error('Temporary failure');
113
+ }
114
+ return 'success';
115
+ }, { maxAttempts: 3, initialDelay: 10 });
116
+ expect(result).toBe('success');
117
+ expect(attempts).toBe(3);
118
+ });
119
+ it('should throw after max attempts exceeded', async () => {
120
+ let attempts = 0;
121
+ await expect(withRetry(async () => {
122
+ attempts++;
123
+ throw new Error('Permanent failure');
124
+ }, { maxAttempts: 3, initialDelay: 10 })).rejects.toThrow('Permanent failure');
125
+ expect(attempts).toBe(3);
126
+ });
127
+ it('should call onRetry callback on each retry', async () => {
128
+ const retries = [];
129
+ let attempts = 0;
130
+ await withRetry(async () => {
131
+ attempts++;
132
+ if (attempts < 3) {
133
+ throw new Error('Temporary failure');
134
+ }
135
+ return 'success';
136
+ }, {
137
+ maxAttempts: 3,
138
+ initialDelay: 10,
139
+ onRetry: (attempt) => {
140
+ retries.push(attempt);
141
+ },
142
+ });
143
+ expect(retries).toEqual([1, 2]);
144
+ });
145
+ it('should apply exponential backoff', async () => {
146
+ const delays = [];
147
+ await expect(withRetry(async () => {
148
+ throw new Error('Always fails');
149
+ }, {
150
+ maxAttempts: 3,
151
+ initialDelay: 100,
152
+ backoffMultiplier: 2,
153
+ maxDelay: 1000,
154
+ onRetry: (_attempt, _error, delay) => {
155
+ delays.push(delay);
156
+ },
157
+ })).rejects.toThrow('Always fails');
158
+ // First retry: 100ms, Second retry: 200ms (100 * 2)
159
+ expect(delays).toEqual([100, 200]);
160
+ });
161
+ it('should respect maxDelay', async () => {
162
+ const delays = [];
163
+ await expect(withRetry(async () => {
164
+ throw new Error('Always fails');
165
+ }, {
166
+ maxAttempts: 4,
167
+ initialDelay: 100,
168
+ backoffMultiplier: 10,
169
+ maxDelay: 150,
170
+ onRetry: (_attempt, _error, delay) => {
171
+ delays.push(delay);
172
+ },
173
+ })).rejects.toThrow('Always fails');
174
+ // All delays should be capped at maxDelay (150)
175
+ expect(delays).toEqual([100, 150, 150]);
176
+ });
177
+ });
178
+ describe('withTimeout', () => {
179
+ it('should resolve if operation completes before timeout', async () => {
180
+ const result = await withTimeout(async () => {
181
+ await new Promise((r) => setTimeout(r, 10));
182
+ return 'success';
183
+ }, 1000);
184
+ expect(result).toBe('success');
185
+ });
186
+ it('should reject with timeout error if operation takes too long', async () => {
187
+ await expect(withTimeout(async () => {
188
+ await new Promise((r) => setTimeout(r, 1000));
189
+ return 'success';
190
+ }, 50, 'Custom timeout message')).rejects.toThrow('Custom timeout message');
191
+ });
192
+ });
193
+ describe('timeout', () => {
194
+ it('should reject after specified time', async () => {
195
+ await expect(timeout(50, 'Timed out')).rejects.toThrow('Timed out');
196
+ });
197
+ });
198
+ });
199
+ //# sourceMappingURL=error-handling.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handling.test.js","sourceRoot":"","sources":["../../src/__tests__/error-handling.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Protocol Tests
3
+ *
4
+ * Source: protocol.md
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=protocol.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Protocol Tests
3
+ *
4
+ * Source: protocol.md
5
+ */
6
+ import { describe, it, expect } from 'vitest';
7
+ import { NOSTR_KINDS, createRoomTag, DEFAULT_CONFIG } from '../types';
8
+ describe('Event Kinds (protocol.md)', () => {
9
+ /**
10
+ * Source: protocol.md - Event Kinds table
11
+ *
12
+ * | Kind | Type | Stored | Purpose |
13
+ * | ----- | ----------- | ------ | --------------------- |
14
+ * | 30078 | Replaceable | Yes | Room metadata |
15
+ * | 25000 | Ephemeral | No | Game state, heartbeat |
16
+ */
17
+ it('room metadata should use kind 30078 (Replaceable)', () => {
18
+ expect(NOSTR_KINDS.ROOM).toBe(30078);
19
+ });
20
+ it('game state/heartbeat should use kind 25000 (Ephemeral)', () => {
21
+ expect(NOSTR_KINDS.EPHEMERAL).toBe(25000);
22
+ });
23
+ });
24
+ describe('Tag Format (protocol.md)', () => {
25
+ /**
26
+ * Source: protocol.md - Tag Format section
27
+ *
28
+ * Room tag format: `{gameId}-room-{roomId}`
29
+ *
30
+ * Examples:
31
+ * - `sasso-room-abc123`
32
+ * - `tetris-room-xyz789`
33
+ */
34
+ it('should create tag in format {gameId}-room-{roomId}', () => {
35
+ const tag = createRoomTag('sasso', 'abc123');
36
+ expect(tag).toBe('sasso-room-abc123');
37
+ });
38
+ it('should work with different gameIds (example: tetris)', () => {
39
+ const tag = createRoomTag('tetris', 'xyz789');
40
+ expect(tag).toBe('tetris-room-xyz789');
41
+ });
42
+ });
43
+ describe('Default Relays (protocol.md)', () => {
44
+ /**
45
+ * Source: protocol.md - Default Relays section
46
+ *
47
+ * ```typescript
48
+ * const DEFAULT_RELAYS = ['wss://relay.damus.io', 'wss://nos.lol', 'wss://relay.nostr.band'];
49
+ * ```
50
+ */
51
+ it('should include wss://relay.damus.io', () => {
52
+ expect(DEFAULT_CONFIG.relays).toContain('wss://relay.damus.io');
53
+ });
54
+ it('should include wss://nos.lol', () => {
55
+ expect(DEFAULT_CONFIG.relays).toContain('wss://nos.lol');
56
+ });
57
+ it('should include wss://relay.nostr.band', () => {
58
+ expect(DEFAULT_CONFIG.relays).toContain('wss://relay.nostr.band');
59
+ });
60
+ });
61
+ describe('Event Content Structures (protocol.md)', () => {
62
+ /**
63
+ * These tests verify the expected structure of event content
64
+ * as documented in protocol.md Event Formats section.
65
+ *
66
+ * Note: These are structural tests, not implementation tests.
67
+ * They verify the shape of data matches the protocol spec.
68
+ */
69
+ describe('Room Creation Event (kind 30078)', () => {
70
+ /**
71
+ * Source: protocol.md
72
+ *
73
+ * ```json
74
+ * {
75
+ * "kind": 30078,
76
+ * "tags": [
77
+ * ["d", "{gameId}-room-{roomId}"],
78
+ * ["t", "{gameId}"]
79
+ * ],
80
+ * "content": {
81
+ * "type": "room",
82
+ * "status": "waiting",
83
+ * "seed": 123456789,
84
+ * "hostPubkey": "<hex-pubkey>"
85
+ * }
86
+ * }
87
+ * ```
88
+ */
89
+ it('content should have type "room"', () => {
90
+ const content = { type: 'room', status: 'waiting', seed: 123, hostPubkey: 'abc' };
91
+ expect(content.type).toBe('room');
92
+ });
93
+ it('content should have status "waiting"', () => {
94
+ const content = { type: 'room', status: 'waiting', seed: 123, hostPubkey: 'abc' };
95
+ expect(content.status).toBe('waiting');
96
+ });
97
+ it('content should have numeric seed', () => {
98
+ const content = { type: 'room', status: 'waiting', seed: 123456789, hostPubkey: 'abc' };
99
+ expect(typeof content.seed).toBe('number');
100
+ });
101
+ it('content should have hostPubkey', () => {
102
+ const content = { type: 'room', status: 'waiting', seed: 123, hostPubkey: 'abc123' };
103
+ expect(content.hostPubkey).toBe('abc123');
104
+ });
105
+ });
106
+ describe('Join Event (kind 25000)', () => {
107
+ /**
108
+ * Source: protocol.md
109
+ *
110
+ * ```json
111
+ * {
112
+ * "kind": 25000,
113
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
114
+ * "content": {
115
+ * "type": "join",
116
+ * "playerPubkey": "<hex-pubkey>"
117
+ * }
118
+ * }
119
+ * ```
120
+ */
121
+ it('content should have type "join"', () => {
122
+ const content = { type: 'join', playerPubkey: 'player123' };
123
+ expect(content.type).toBe('join');
124
+ });
125
+ it('content should have playerPubkey', () => {
126
+ const content = { type: 'join', playerPubkey: 'player123' };
127
+ expect(content.playerPubkey).toBe('player123');
128
+ });
129
+ });
130
+ describe('State Update Event (kind 25000)', () => {
131
+ /**
132
+ * Source: protocol.md
133
+ *
134
+ * ```json
135
+ * {
136
+ * "kind": 25000,
137
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
138
+ * "content": {
139
+ * "type": "state",
140
+ * "gameState": {
141
+ * // Your game's state object
142
+ * }
143
+ * }
144
+ * }
145
+ * ```
146
+ */
147
+ it('content should have type "state"', () => {
148
+ const content = { type: 'state', gameState: {} };
149
+ expect(content.type).toBe('state');
150
+ });
151
+ it('content should wrap game state in gameState property', () => {
152
+ const content = { type: 'state', gameState: { score: 100, level: 5 } };
153
+ expect(content.gameState).toEqual({ score: 100, level: 5 });
154
+ });
155
+ });
156
+ describe('Heartbeat Event (kind 25000)', () => {
157
+ /**
158
+ * Source: protocol.md
159
+ *
160
+ * ```json
161
+ * {
162
+ * "kind": 25000,
163
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
164
+ * "content": {
165
+ * "type": "heartbeat",
166
+ * "timestamp": 1704000000000
167
+ * }
168
+ * }
169
+ * ```
170
+ */
171
+ it('content should have type "heartbeat"', () => {
172
+ const content = { type: 'heartbeat', timestamp: Date.now() };
173
+ expect(content.type).toBe('heartbeat');
174
+ });
175
+ it('content should have numeric timestamp', () => {
176
+ const content = { type: 'heartbeat', timestamp: 1704000000000 };
177
+ expect(typeof content.timestamp).toBe('number');
178
+ });
179
+ });
180
+ describe('Game Over Event (kind 25000)', () => {
181
+ /**
182
+ * Source: protocol.md
183
+ *
184
+ * ```json
185
+ * {
186
+ * "kind": 25000,
187
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
188
+ * "content": {
189
+ * "type": "gameover",
190
+ * "reason": "win|lose|disconnect|surrender",
191
+ * "finalScore": 500,
192
+ * "winner": "<hex-pubkey>"
193
+ * }
194
+ * }
195
+ * ```
196
+ */
197
+ it('content should have type "gameover"', () => {
198
+ const content = { type: 'gameover', reason: 'win' };
199
+ expect(content.type).toBe('gameover');
200
+ });
201
+ it('reason should be one of: win, lose, disconnect, surrender', () => {
202
+ const validReasons = ['win', 'lose', 'disconnect', 'surrender'];
203
+ for (const reason of validReasons) {
204
+ const content = { type: 'gameover', reason };
205
+ expect(validReasons).toContain(content.reason);
206
+ }
207
+ });
208
+ });
209
+ describe('Rematch Request Event (kind 25000)', () => {
210
+ /**
211
+ * Source: protocol.md
212
+ *
213
+ * ```json
214
+ * {
215
+ * "kind": 25000,
216
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
217
+ * "content": {
218
+ * "type": "rematch",
219
+ * "action": "request"
220
+ * }
221
+ * }
222
+ * ```
223
+ */
224
+ it('content should have type "rematch" and action "request"', () => {
225
+ const content = { type: 'rematch', action: 'request' };
226
+ expect(content.type).toBe('rematch');
227
+ expect(content.action).toBe('request');
228
+ });
229
+ });
230
+ describe('Rematch Accept Event (kind 25000)', () => {
231
+ /**
232
+ * Source: protocol.md
233
+ *
234
+ * ```json
235
+ * {
236
+ * "kind": 25000,
237
+ * "tags": [["d", "{gameId}-room-{roomId}"]],
238
+ * "content": {
239
+ * "type": "rematch",
240
+ * "action": "accept",
241
+ * "newSeed": 987654321
242
+ * }
243
+ * }
244
+ * ```
245
+ */
246
+ it('content should have type "rematch" and action "accept"', () => {
247
+ const content = { type: 'rematch', action: 'accept', newSeed: 123 };
248
+ expect(content.type).toBe('rematch');
249
+ expect(content.action).toBe('accept');
250
+ });
251
+ it('content should have numeric newSeed', () => {
252
+ const content = { type: 'rematch', action: 'accept', newSeed: 987654321 };
253
+ expect(typeof content.newSeed).toBe('number');
254
+ });
255
+ });
256
+ });
257
+ //# sourceMappingURL=protocol.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * State Flow Tests
3
+ *
4
+ * Source: architecture.md - State Flow diagram
5
+ *
6
+ * ```
7
+ * ┌─────────┐ create() ┌─────────┐ opponent ┌─────────┐
8
+ * │ idle │────────────▶│ waiting │────joins───▶│ ready │
9
+ * └─────────┘ └─────────┘ └────┬────┘
10
+ * ▲ │
11
+ * │ game starts
12
+ * │ │
13
+ * │ ┌──────────┐ ┌───▼─────┐
14
+ * │◀────leave()──────│ finished │◀──gameover──│ playing │
15
+ * │ └────┬─────┘ └─────────┘
16
+ * │ │
17
+ * │ rematch
18
+ * │ │
19
+ * │ ┌────▼────┐
20
+ * └──────────────────│ ready │
21
+ * └─────────┘
22
+ * ```
23
+ *
24
+ * Source: README.md - Room States table
25
+ *
26
+ * | Status | Description |
27
+ * | -------- | ------------------------------------- |
28
+ * | idle | No room active |
29
+ * | creating | Creating a new room |
30
+ * | waiting | Waiting for opponent to join |
31
+ * | joining | Joining an existing room |
32
+ * | ready | Both players connected, ready to play |
33
+ * | playing | Game in progress |
34
+ * | finished | Game ended |
35
+ */
36
+ export {};
37
+ //# sourceMappingURL=state-flow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-flow.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/state-flow.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}