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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nostr-battle-room - React Hook
|
|
3
|
+
* React bindings for Arena
|
|
4
|
+
*/
|
|
5
|
+
import type { ArenaConfig, ArenaCallbacks, RoomState, OpponentBase } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Opponent state with game state
|
|
8
|
+
*/
|
|
9
|
+
export interface OpponentState<TGameState> extends OpponentBase {
|
|
10
|
+
gameState: TGameState | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Return type for useArena hook
|
|
14
|
+
*/
|
|
15
|
+
export interface UseArenaReturn<TGameState> {
|
|
16
|
+
roomState: RoomState;
|
|
17
|
+
opponent: OpponentState<TGameState> | null;
|
|
18
|
+
isConnected: boolean;
|
|
19
|
+
publicKey: string;
|
|
20
|
+
createRoom: (baseUrl?: string) => Promise<string>;
|
|
21
|
+
joinRoom: (roomId: string) => Promise<void>;
|
|
22
|
+
leaveRoom: () => void;
|
|
23
|
+
reconnect: () => Promise<boolean>;
|
|
24
|
+
sendState: (state: TGameState) => void;
|
|
25
|
+
sendGameOver: (reason: string, finalScore?: number) => void;
|
|
26
|
+
requestRematch: () => void;
|
|
27
|
+
acceptRematch: () => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* React hook for managing a battle room
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* interface MyGameState {
|
|
35
|
+
* score: number;
|
|
36
|
+
* position: { x: number; y: number };
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* function GameComponent() {
|
|
40
|
+
* const {
|
|
41
|
+
* roomState,
|
|
42
|
+
* opponent,
|
|
43
|
+
* createRoom,
|
|
44
|
+
* joinRoom,
|
|
45
|
+
* sendState,
|
|
46
|
+
* } = useArena<MyGameState>({
|
|
47
|
+
* gameId: 'my-game',
|
|
48
|
+
* onOpponentState: (state) => {
|
|
49
|
+
* console.log('Opponent moved:', state.position);
|
|
50
|
+
* },
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Create a room
|
|
54
|
+
* const handleCreate = async () => {
|
|
55
|
+
* const url = await createRoom();
|
|
56
|
+
* console.log('Share this URL:', url);
|
|
57
|
+
* };
|
|
58
|
+
*
|
|
59
|
+
* // Send game state
|
|
60
|
+
* const handleMove = (x: number, y: number) => {
|
|
61
|
+
* sendState({ score: 100, position: { x, y } });
|
|
62
|
+
* };
|
|
63
|
+
*
|
|
64
|
+
* return (
|
|
65
|
+
* <div>
|
|
66
|
+
* <p>Status: {roomState.status}</p>
|
|
67
|
+
* {opponent && <p>Opponent score: {opponent.gameState?.score}</p>}
|
|
68
|
+
* </div>
|
|
69
|
+
* );
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function useArena<TGameState = Record<string, unknown>>(config: ArenaConfig, callbacks?: ArenaCallbacks<TGameState>): UseArenaReturn<TGameState>;
|
|
74
|
+
//# sourceMappingURL=useArena.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArena.d.ts","sourceRoot":"","sources":["../../src/react/useArena.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGrF;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,UAAU,CAAE,SAAQ,YAAY;IAC7D,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,UAAU;IAExC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAC3C,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAGlB,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAGlC,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACvC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,WAAW,EACnB,SAAS,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,GACrC,cAAc,CAAC,UAAU,CAAC,CAuM5B"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nostr-battle-room - React Hook
|
|
3
|
+
* React bindings for Arena
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
6
|
+
import { Arena } from '../core/Arena';
|
|
7
|
+
import { INITIAL_ROOM_STATE } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* React hook for managing a battle room
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* interface MyGameState {
|
|
14
|
+
* score: number;
|
|
15
|
+
* position: { x: number; y: number };
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* function GameComponent() {
|
|
19
|
+
* const {
|
|
20
|
+
* roomState,
|
|
21
|
+
* opponent,
|
|
22
|
+
* createRoom,
|
|
23
|
+
* joinRoom,
|
|
24
|
+
* sendState,
|
|
25
|
+
* } = useArena<MyGameState>({
|
|
26
|
+
* gameId: 'my-game',
|
|
27
|
+
* onOpponentState: (state) => {
|
|
28
|
+
* console.log('Opponent moved:', state.position);
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Create a room
|
|
33
|
+
* const handleCreate = async () => {
|
|
34
|
+
* const url = await createRoom();
|
|
35
|
+
* console.log('Share this URL:', url);
|
|
36
|
+
* };
|
|
37
|
+
*
|
|
38
|
+
* // Send game state
|
|
39
|
+
* const handleMove = (x: number, y: number) => {
|
|
40
|
+
* sendState({ score: 100, position: { x, y } });
|
|
41
|
+
* };
|
|
42
|
+
*
|
|
43
|
+
* return (
|
|
44
|
+
* <div>
|
|
45
|
+
* <p>Status: {roomState.status}</p>
|
|
46
|
+
* {opponent && <p>Opponent score: {opponent.gameState?.score}</p>}
|
|
47
|
+
* </div>
|
|
48
|
+
* );
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function useArena(config, callbacks) {
|
|
53
|
+
const [roomState, setRoomState] = useState(INITIAL_ROOM_STATE);
|
|
54
|
+
const [opponent, setOpponent] = useState(null);
|
|
55
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
56
|
+
const roomRef = useRef(null);
|
|
57
|
+
const callbacksRef = useRef(callbacks);
|
|
58
|
+
// Keep callbacks ref updated
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
callbacksRef.current = callbacks;
|
|
61
|
+
}, [callbacks]);
|
|
62
|
+
// Initialize Arena
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const room = new Arena(config);
|
|
65
|
+
// Register callbacks that update React state
|
|
66
|
+
room.onOpponentJoin((publicKey) => {
|
|
67
|
+
setOpponent({
|
|
68
|
+
publicKey,
|
|
69
|
+
gameState: null,
|
|
70
|
+
isConnected: true,
|
|
71
|
+
lastHeartbeat: Date.now(),
|
|
72
|
+
rematchRequested: false,
|
|
73
|
+
});
|
|
74
|
+
setRoomState((prev) => ({ ...prev, status: 'ready' }));
|
|
75
|
+
callbacksRef.current?.onOpponentJoin?.(publicKey);
|
|
76
|
+
});
|
|
77
|
+
room.onOpponentState((state) => {
|
|
78
|
+
setOpponent((prev) => prev
|
|
79
|
+
? {
|
|
80
|
+
...prev,
|
|
81
|
+
gameState: state,
|
|
82
|
+
lastHeartbeat: Date.now(),
|
|
83
|
+
isConnected: true,
|
|
84
|
+
}
|
|
85
|
+
: null);
|
|
86
|
+
callbacksRef.current?.onOpponentState?.(state);
|
|
87
|
+
});
|
|
88
|
+
room.onOpponentDisconnect(() => {
|
|
89
|
+
setOpponent((prev) => (prev ? { ...prev, isConnected: false } : null));
|
|
90
|
+
callbacksRef.current?.onOpponentDisconnect?.();
|
|
91
|
+
});
|
|
92
|
+
room.onOpponentGameOver((reason, finalScore) => {
|
|
93
|
+
setRoomState((prev) => ({ ...prev, status: 'finished', rematchRequested: false }));
|
|
94
|
+
setOpponent((prev) => (prev ? { ...prev, rematchRequested: false } : null));
|
|
95
|
+
callbacksRef.current?.onOpponentGameOver?.(reason, finalScore);
|
|
96
|
+
});
|
|
97
|
+
room.onRematchRequested(() => {
|
|
98
|
+
setOpponent((prev) => (prev ? { ...prev, rematchRequested: true } : null));
|
|
99
|
+
callbacksRef.current?.onRematchRequested?.();
|
|
100
|
+
});
|
|
101
|
+
room.onRematchStart((newSeed) => {
|
|
102
|
+
setRoomState((prev) => ({
|
|
103
|
+
...prev,
|
|
104
|
+
seed: newSeed,
|
|
105
|
+
status: 'ready',
|
|
106
|
+
rematchRequested: false,
|
|
107
|
+
}));
|
|
108
|
+
setOpponent((prev) => (prev ? { ...prev, gameState: null, rematchRequested: false } : null));
|
|
109
|
+
callbacksRef.current?.onRematchStart?.(newSeed);
|
|
110
|
+
});
|
|
111
|
+
room.onError((error) => {
|
|
112
|
+
callbacksRef.current?.onError?.(error);
|
|
113
|
+
});
|
|
114
|
+
room.connect();
|
|
115
|
+
setIsConnected(true);
|
|
116
|
+
roomRef.current = room;
|
|
117
|
+
return () => {
|
|
118
|
+
room.disconnect();
|
|
119
|
+
roomRef.current = null;
|
|
120
|
+
};
|
|
121
|
+
// Note: config should be stable (memoized) by the caller
|
|
122
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
+
}, [config.gameId]);
|
|
124
|
+
// Create room
|
|
125
|
+
const createRoom = useCallback(async (baseUrl) => {
|
|
126
|
+
if (!roomRef.current)
|
|
127
|
+
throw new Error('Arena not initialized');
|
|
128
|
+
const url = await roomRef.current.create(baseUrl);
|
|
129
|
+
setRoomState(roomRef.current.roomState);
|
|
130
|
+
return url;
|
|
131
|
+
}, []);
|
|
132
|
+
// Join room
|
|
133
|
+
const joinRoom = useCallback(async (roomId) => {
|
|
134
|
+
if (!roomRef.current)
|
|
135
|
+
throw new Error('Arena not initialized');
|
|
136
|
+
try {
|
|
137
|
+
await roomRef.current.join(roomId);
|
|
138
|
+
setRoomState(roomRef.current.roomState);
|
|
139
|
+
const opp = roomRef.current.opponent;
|
|
140
|
+
if (opp) {
|
|
141
|
+
setOpponent({
|
|
142
|
+
publicKey: opp.publicKey,
|
|
143
|
+
gameState: opp.gameState ?? null,
|
|
144
|
+
isConnected: opp.isConnected,
|
|
145
|
+
lastHeartbeat: opp.lastHeartbeat,
|
|
146
|
+
rematchRequested: opp.rematchRequested,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// Reset state on join failure
|
|
152
|
+
setRoomState(INITIAL_ROOM_STATE);
|
|
153
|
+
setOpponent(null);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}, []);
|
|
157
|
+
// Leave room
|
|
158
|
+
const leaveRoom = useCallback(() => {
|
|
159
|
+
if (!roomRef.current)
|
|
160
|
+
return;
|
|
161
|
+
roomRef.current.leave();
|
|
162
|
+
setRoomState(INITIAL_ROOM_STATE);
|
|
163
|
+
setOpponent(null);
|
|
164
|
+
}, []);
|
|
165
|
+
// Reconnect
|
|
166
|
+
const reconnect = useCallback(async () => {
|
|
167
|
+
if (!roomRef.current)
|
|
168
|
+
return false;
|
|
169
|
+
const success = await roomRef.current.reconnect();
|
|
170
|
+
if (success) {
|
|
171
|
+
setRoomState(roomRef.current.roomState);
|
|
172
|
+
const opp = roomRef.current.opponent;
|
|
173
|
+
if (opp) {
|
|
174
|
+
setOpponent({
|
|
175
|
+
publicKey: opp.publicKey,
|
|
176
|
+
gameState: opp.gameState ?? null,
|
|
177
|
+
isConnected: opp.isConnected,
|
|
178
|
+
lastHeartbeat: opp.lastHeartbeat,
|
|
179
|
+
rematchRequested: opp.rematchRequested,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return success;
|
|
184
|
+
}, []);
|
|
185
|
+
// Send state
|
|
186
|
+
const sendState = useCallback((state) => {
|
|
187
|
+
roomRef.current?.sendState(state);
|
|
188
|
+
}, []);
|
|
189
|
+
// Send game over
|
|
190
|
+
const sendGameOver = useCallback((reason, finalScore) => {
|
|
191
|
+
roomRef.current?.sendGameOver(reason, finalScore);
|
|
192
|
+
setRoomState((prev) => ({ ...prev, status: 'finished' }));
|
|
193
|
+
}, []);
|
|
194
|
+
// Request rematch
|
|
195
|
+
const requestRematch = useCallback(() => {
|
|
196
|
+
if (!roomRef.current)
|
|
197
|
+
return;
|
|
198
|
+
roomRef.current.requestRematch();
|
|
199
|
+
setRoomState((prev) => ({ ...prev, rematchRequested: true }));
|
|
200
|
+
// If opponent already requested, trigger acceptRematch
|
|
201
|
+
if (opponent?.rematchRequested) {
|
|
202
|
+
roomRef.current.acceptRematch();
|
|
203
|
+
}
|
|
204
|
+
}, [opponent?.rematchRequested]);
|
|
205
|
+
// Accept rematch
|
|
206
|
+
const acceptRematch = useCallback(() => {
|
|
207
|
+
roomRef.current?.acceptRematch();
|
|
208
|
+
}, []);
|
|
209
|
+
return {
|
|
210
|
+
roomState,
|
|
211
|
+
opponent,
|
|
212
|
+
isConnected,
|
|
213
|
+
publicKey: roomRef.current?.publicKey ?? '',
|
|
214
|
+
createRoom,
|
|
215
|
+
joinRoom,
|
|
216
|
+
leaveRoom,
|
|
217
|
+
reconnect,
|
|
218
|
+
sendState,
|
|
219
|
+
sendGameOver,
|
|
220
|
+
requestRematch,
|
|
221
|
+
acceptRematch,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=useArena.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useArena.js","sourceRoot":"","sources":["../../src/react/useArena.ts"],"names":[],"mappings":"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"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nostr-battle-room - Retry Utilities
|
|
3
|
+
* Exponential backoff retry logic for network operations
|
|
4
|
+
*/
|
|
5
|
+
export interface RetryOptions {
|
|
6
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
7
|
+
maxAttempts?: number;
|
|
8
|
+
/** Initial delay in ms (default: 1000) */
|
|
9
|
+
initialDelay?: number;
|
|
10
|
+
/** Maximum delay in ms (default: 10000) */
|
|
11
|
+
maxDelay?: number;
|
|
12
|
+
/** Backoff multiplier (default: 2) */
|
|
13
|
+
backoffMultiplier?: number;
|
|
14
|
+
/** Called on each retry attempt */
|
|
15
|
+
onRetry?: (attempt: number, error: Error, delay: number) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute a function with exponential backoff retry
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const result = await withRetry(
|
|
23
|
+
* () => fetchData(),
|
|
24
|
+
* { maxAttempts: 3, initialDelay: 1000 }
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Create a promise that rejects after specified timeout
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const result = await Promise.race([
|
|
35
|
+
* fetchData(),
|
|
36
|
+
* timeout(5000, 'Fetch timed out')
|
|
37
|
+
* ]);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function timeout<T = never>(ms: number, message?: string): Promise<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Execute a function with timeout
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const result = await withTimeout(
|
|
47
|
+
* () => fetchData(),
|
|
48
|
+
* 5000,
|
|
49
|
+
* 'Fetch timed out'
|
|
50
|
+
* );
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function withTimeout<T>(fn: () => Promise<T>, ms: number, message?: string): Promise<T>;
|
|
54
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AAgBD;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CA2B/F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,SAAwB,GAAG,OAAO,CAAC,CAAC,CAAC,CAI1F;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,OAAO,SAAwB,GAC9B,OAAO,CAAC,CAAC,CAAC,CAEZ"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nostr-battle-room - Retry Utilities
|
|
3
|
+
* Exponential backoff retry logic for network operations
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
6
|
+
maxAttempts: 3,
|
|
7
|
+
initialDelay: 1000,
|
|
8
|
+
maxDelay: 10000,
|
|
9
|
+
backoffMultiplier: 2,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Sleep for specified milliseconds
|
|
13
|
+
*/
|
|
14
|
+
function sleep(ms) {
|
|
15
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute a function with exponential backoff retry
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const result = await withRetry(
|
|
23
|
+
* () => fetchData(),
|
|
24
|
+
* { maxAttempts: 3, initialDelay: 1000 }
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export async function withRetry(fn, options = {}) {
|
|
29
|
+
const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
|
30
|
+
let lastError = null;
|
|
31
|
+
let delay = opts.initialDelay;
|
|
32
|
+
for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
|
|
33
|
+
try {
|
|
34
|
+
return await fn();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
38
|
+
if (attempt === opts.maxAttempts) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
// Call onRetry callback if provided
|
|
42
|
+
opts.onRetry?.(attempt, lastError, delay);
|
|
43
|
+
// Wait before next retry
|
|
44
|
+
await sleep(delay);
|
|
45
|
+
// Increase delay with exponential backoff
|
|
46
|
+
delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelay);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw lastError;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a promise that rejects after specified timeout
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const result = await Promise.race([
|
|
57
|
+
* fetchData(),
|
|
58
|
+
* timeout(5000, 'Fetch timed out')
|
|
59
|
+
* ]);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function timeout(ms, message = 'Operation timed out') {
|
|
63
|
+
return new Promise((_, reject) => {
|
|
64
|
+
setTimeout(() => reject(new Error(message)), ms);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Execute a function with timeout
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const result = await withTimeout(
|
|
73
|
+
* () => fetchData(),
|
|
74
|
+
* 5000,
|
|
75
|
+
* 'Fetch timed out'
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export async function withTimeout(fn, ms, message = 'Operation timed out') {
|
|
80
|
+
return Promise.race([fn(), timeout(ms, message)]);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,MAAM,qBAAqB,GAA4C;IACrE,WAAW,EAAE,CAAC;IACd,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,KAAK;IACf,iBAAiB,EAAE,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,EAAoB,EAAE,UAAwB,EAAE;IACjF,MAAM,IAAI,GAAG,EAAE,GAAG,qBAAqB,EAAE,GAAG,OAAO,EAAE,CAAC;IACtD,IAAI,SAAS,GAAiB,IAAI,CAAC;IACnC,IAAI,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;IAE9B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAEtE,IAAI,OAAO,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjC,MAAM;YACR,CAAC;YAED,oCAAoC;YACpC,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAE1C,yBAAyB;YACzB,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YAEnB,0CAA0C;YAC1C,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,OAAO,CAAY,EAAU,EAAE,OAAO,GAAG,qBAAqB;IAC5E,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAoB,EACpB,EAAU,EACV,OAAO,GAAG,qBAAqB;IAE/B,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,CAAI,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nostr-battle-room - MockArena
|
|
3
|
+
* Testing utilities for battle room
|
|
4
|
+
*/
|
|
5
|
+
import type { RoomState, OpponentBase, ArenaCallbacks, ArenaEventName } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Map event names to callback keys
|
|
8
|
+
*/
|
|
9
|
+
type EventToCallback<TGameState> = {
|
|
10
|
+
opponentJoin: ArenaCallbacks<TGameState>['onOpponentJoin'];
|
|
11
|
+
opponentState: ArenaCallbacks<TGameState>['onOpponentState'];
|
|
12
|
+
opponentDisconnect: ArenaCallbacks<TGameState>['onOpponentDisconnect'];
|
|
13
|
+
opponentGameOver: ArenaCallbacks<TGameState>['onOpponentGameOver'];
|
|
14
|
+
rematchRequested: ArenaCallbacks<TGameState>['onRematchRequested'];
|
|
15
|
+
rematchStart: ArenaCallbacks<TGameState>['onRematchStart'];
|
|
16
|
+
error: ArenaCallbacks<TGameState>['onError'];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Mock opponent state
|
|
20
|
+
*/
|
|
21
|
+
interface MockOpponentState<TGameState> extends OpponentBase {
|
|
22
|
+
gameState: TGameState | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* MockArena - For testing without real Nostr connections
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const mock = new MockArena<MyGameState>({ gameId: 'test' });
|
|
30
|
+
*
|
|
31
|
+
* // Simulate opponent joining
|
|
32
|
+
* mock.simulateOpponentJoin('pubkey123');
|
|
33
|
+
*
|
|
34
|
+
* // Simulate opponent state update
|
|
35
|
+
* mock.simulateOpponentState({ score: 100 });
|
|
36
|
+
*
|
|
37
|
+
* // Simulate opponent disconnect
|
|
38
|
+
* mock.simulateOpponentDisconnect();
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare class MockArena<TGameState = Record<string, unknown>> {
|
|
42
|
+
private _roomState;
|
|
43
|
+
private _opponent;
|
|
44
|
+
private callbacks;
|
|
45
|
+
constructor(_config: {
|
|
46
|
+
gameId: string;
|
|
47
|
+
});
|
|
48
|
+
get roomState(): Readonly<RoomState>;
|
|
49
|
+
get opponent(): Readonly<MockOpponentState<TGameState>> | null;
|
|
50
|
+
get isConnected(): boolean;
|
|
51
|
+
get publicKey(): string;
|
|
52
|
+
on<K extends ArenaEventName>(event: K, callback: NonNullable<EventToCallback<TGameState>[K]>): this;
|
|
53
|
+
connect(): void;
|
|
54
|
+
disconnect(): void;
|
|
55
|
+
create(): Promise<string>;
|
|
56
|
+
join(roomId: string): Promise<void>;
|
|
57
|
+
leave(): void;
|
|
58
|
+
sendState(_state: TGameState): void;
|
|
59
|
+
sendGameOver(_reason: string, _finalScore?: number): void;
|
|
60
|
+
requestRematch(): void;
|
|
61
|
+
acceptRematch(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Simulate an opponent joining the room
|
|
64
|
+
*/
|
|
65
|
+
simulateOpponentJoin(publicKey?: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Simulate opponent state update
|
|
68
|
+
*/
|
|
69
|
+
simulateOpponentState(state: TGameState): void;
|
|
70
|
+
/**
|
|
71
|
+
* Simulate opponent disconnection
|
|
72
|
+
*/
|
|
73
|
+
simulateOpponentDisconnect(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Simulate opponent game over
|
|
76
|
+
*/
|
|
77
|
+
simulateOpponentGameOver(reason: string, finalScore?: number): void;
|
|
78
|
+
/**
|
|
79
|
+
* Simulate opponent rematch request
|
|
80
|
+
*/
|
|
81
|
+
simulateRematchRequested(): void;
|
|
82
|
+
}
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=MockArena.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockArena.d.ts","sourceRoot":"","sources":["../../src/testing/MockArena.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAGxF;;GAEG;AACH,KAAK,eAAe,CAAC,UAAU,IAAI;IACjC,YAAY,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC3D,aAAa,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC7D,kBAAkB,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,sBAAsB,CAAC,CAAC;IACvE,gBAAgB,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,CAAC;IACnE,gBAAgB,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,CAAC;IACnE,YAAY,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC3D,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC;CAC9C,CAAC;AAEF;;GAEG;AACH,UAAU,iBAAiB,CAAC,UAAU,CAAE,SAAQ,YAAY;IAC1D,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzD,OAAO,CAAC,UAAU,CAAwC;IAC1D,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,SAAS,CAAkC;gBAEvC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;IAIvC,IAAI,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,CAEnC;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAE7D;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,EAAE,CAAC,CAAC,SAAS,cAAc,EACzB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GACpD,IAAI;IAOP,OAAO,IAAI,IAAI;IAIf,UAAU,IAAI,IAAI;IAIZ,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAYzB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC,KAAK,IAAI,IAAI;IAKb,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAInC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAIzD,cAAc,IAAI,IAAI;IAItB,aAAa,IAAI,IAAI;IAkBrB;;OAEG;IACH,oBAAoB,CAAC,SAAS,GAAE,MAA0B,GAAG,IAAI;IAYjE;;OAEG;IACH,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAY9C;;OAEG;IACH,0BAA0B,IAAI,IAAI;IAOlC;;OAEG;IACH,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAKnE;;OAEG;IACH,wBAAwB,IAAI,IAAI;CAMjC"}
|