filepizza-client 2.0.1 → 2.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/src/peerjs.ts ADDED
@@ -0,0 +1,147 @@
1
+ // src/peerjs.ts
2
+ import Peer, { PeerOptions } from 'peerjs'
3
+ import { PeerJSSignalingServer } from './types'
4
+
5
+ const DEFAULT_ICE_SERVERS: RTCIceServer[] = [
6
+ { urls: 'stun:stun.l.google.com:19302' },
7
+ ]
8
+
9
+ export type PeerJSConfigOptions = {
10
+ filePizzaServerUrl: string
11
+ iceServers?: RTCIceServer[]
12
+ peerJSSignalingServer?: PeerJSSignalingServer
13
+ discoverPeerJSSignalingServer?: boolean
14
+ }
15
+
16
+ type IceEndpointResponse = {
17
+ host?: string
18
+ path?: string
19
+ port?: number
20
+ secure?: boolean
21
+ servers?: string[]
22
+ iceServers?: RTCIceServer[]
23
+ }
24
+
25
+ function normalizeBaseUrl(url: string): string {
26
+ return url.replace(/\/+$/, '')
27
+ }
28
+
29
+ function parsePeerJSServerUrl(serverUrlString: string): PeerJSSignalingServer {
30
+ const serverUrl = new URL(serverUrlString)
31
+ const secure = serverUrl.protocol === 'https:'
32
+ return {
33
+ host: serverUrl.hostname,
34
+ port: serverUrl.port
35
+ ? Number.parseInt(serverUrl.port, 10)
36
+ : secure
37
+ ? 443
38
+ : 80,
39
+ path: serverUrl.pathname,
40
+ secure,
41
+ }
42
+ }
43
+
44
+ const iceCache = new Map<string, Promise<IceEndpointResponse>>()
45
+
46
+ function fetchIceEndpoint(
47
+ filePizzaServerUrl: string,
48
+ ): Promise<IceEndpointResponse> {
49
+ const baseUrl = normalizeBaseUrl(filePizzaServerUrl)
50
+ const cached = iceCache.get(baseUrl)
51
+ if (cached) {
52
+ return cached
53
+ }
54
+
55
+ const promise = (async () => {
56
+ const response = await fetch(`${baseUrl}/api/ice`, { method: 'POST' })
57
+ if (!response.ok) {
58
+ throw new Error(`Failed to fetch /api/ice: ${response.status}`)
59
+ }
60
+ return (await response.json()) as IceEndpointResponse
61
+ })()
62
+
63
+ promise.catch(() => iceCache.delete(baseUrl))
64
+ iceCache.set(baseUrl, promise)
65
+ return promise
66
+ }
67
+
68
+ export async function getIceServers(
69
+ filePizzaServerUrl: string,
70
+ ): Promise<RTCIceServer[]> {
71
+ try {
72
+ const data = await fetchIceEndpoint(filePizzaServerUrl)
73
+ return data.iceServers || DEFAULT_ICE_SERVERS
74
+ } catch (error) {
75
+ console.error('Error getting ICE servers:', error)
76
+ return DEFAULT_ICE_SERVERS
77
+ }
78
+ }
79
+
80
+ export async function discoverPeerJSSignalingServer(
81
+ filePizzaServerUrl: string,
82
+ ): Promise<PeerJSSignalingServer | undefined> {
83
+ try {
84
+ const data = await fetchIceEndpoint(filePizzaServerUrl)
85
+
86
+ if (data.host) {
87
+ return {
88
+ host: data.host,
89
+ path: data.path,
90
+ port: data.port,
91
+ secure: data.secure,
92
+ }
93
+ }
94
+
95
+ const firstServer = data.servers?.[0]
96
+ if (!firstServer) {
97
+ return undefined
98
+ }
99
+ return parsePeerJSServerUrl(firstServer)
100
+ } catch (error) {
101
+ console.error('Error discovering PeerJS signaling server:', error)
102
+ return undefined
103
+ }
104
+ }
105
+
106
+ export async function buildPeerOptions({
107
+ filePizzaServerUrl,
108
+ iceServers,
109
+ peerJSSignalingServer,
110
+ discoverPeerJSSignalingServer: shouldDiscover = true,
111
+ }: PeerJSConfigOptions): Promise<PeerOptions> {
112
+ const resolvedIceServers =
113
+ iceServers || (await getIceServers(filePizzaServerUrl))
114
+
115
+ const discoveredPeerJSSignalingServer =
116
+ peerJSSignalingServer || shouldDiscover
117
+ ? peerJSSignalingServer ||
118
+ (await discoverPeerJSSignalingServer(filePizzaServerUrl))
119
+ : undefined
120
+
121
+ return {
122
+ ...discoveredPeerJSSignalingServer,
123
+ config: {
124
+ iceServers: resolvedIceServers,
125
+ },
126
+ debug: 2,
127
+ }
128
+ }
129
+
130
+ export async function createPeer(options: PeerJSConfigOptions): Promise<Peer> {
131
+ const peerOptions = await buildPeerOptions(options)
132
+ const peer = new Peer(peerOptions)
133
+
134
+ if (peer.id) {
135
+ return peer
136
+ }
137
+
138
+ await new Promise<void>((resolve) => {
139
+ const onOpen = () => {
140
+ peer.off('open', onOpen)
141
+ resolve()
142
+ }
143
+ peer.on('open', onOpen)
144
+ })
145
+
146
+ return peer
147
+ }
package/src/types.ts CHANGED
@@ -1,10 +1,17 @@
1
- // src/core/types.ts
1
+ // src/types.ts
2
+ import type { PeerOptions } from 'peerjs'
3
+
2
4
  export interface EventEmitter {
3
- on(event: string, listener: (...args: any[]) => void): this;
4
- off(event: string, listener: (...args: any[]) => void): this;
5
- emit(event: string, ...args: any[]): boolean;
5
+ on(event: string, listener: (...args: any[]) => void): this
6
+ off(event: string, listener: (...args: any[]) => void): this
7
+ emit(event: string, ...args: any[]): boolean
6
8
  }
7
9
 
10
+ export type PeerJSSignalingServer = Pick<
11
+ PeerOptions,
12
+ 'host' | 'port' | 'path' | 'secure' | 'key' | 'token' | 'pingInterval'
13
+ >
14
+
8
15
  /**
9
16
  * Connection status
10
17
  */
@@ -18,66 +25,68 @@ export enum ConnectionStatus {
18
25
  Authenticating = 'AUTHENTICATING',
19
26
  InvalidPassword = 'INVALID_PASSWORD',
20
27
  Closed = 'CLOSED',
21
- Error = 'ERROR'
28
+ Error = 'ERROR',
22
29
  }
23
30
 
24
31
  /**
25
32
  * File information
26
33
  */
27
34
  export interface FileInfo {
28
- fileName: string;
29
- size: number;
30
- type: string;
35
+ fileName: string
36
+ size: number
37
+ type: string
31
38
  }
32
39
 
33
40
  /**
34
41
  * Interface for a completed file ready to download
35
42
  */
36
43
  export interface CompletedFile extends FileInfo {
37
- data: Uint8Array;
38
- downloadUrl?: string;
44
+ data: Uint8Array
45
+ downloadUrl?: string
39
46
  }
40
47
 
41
48
  /**
42
49
  * Progress information
43
50
  */
44
51
  export interface ProgressInfo {
45
- fileIndex: number;
46
- fileName: string;
47
- totalFiles: number;
48
- currentFileProgress: number;
49
- overallProgress: number;
50
- bytesTransferred: number;
51
- totalBytes: number;
52
+ fileIndex: number
53
+ fileName: string
54
+ totalFiles: number
55
+ currentFileProgress: number
56
+ overallProgress: number
57
+ bytesTransferred: number
58
+ totalBytes: number
52
59
  }
53
60
 
54
61
  /**
55
62
  * Connection information
56
63
  */
57
64
  export interface ConnectionInfo {
58
- id: string;
59
- status: ConnectionStatus;
60
- browserName?: string;
61
- browserVersion?: string;
62
- osName?: string;
63
- osVersion?: string;
64
- mobileVendor?: string;
65
- mobileModel?: string;
65
+ id: string
66
+ status: ConnectionStatus
67
+ browserName?: string
68
+ browserVersion?: string
69
+ osName?: string
70
+ osVersion?: string
71
+ mobileVendor?: string
72
+ mobileModel?: string
66
73
  }
67
74
 
68
75
  /**
69
- * Message types for peer-to-peer communication
76
+ * Message types for peer-to-peer communication.
77
+ *
78
+ * This mirrors the server frontend protocol in filepizza-server/src/messages.ts.
70
79
  */
71
80
  export enum MessageType {
72
81
  RequestInfo = 'RequestInfo',
73
82
  Info = 'Info',
74
83
  Start = 'Start',
75
84
  Chunk = 'Chunk',
85
+ ChunkAck = 'ChunkAck',
76
86
  Pause = 'Pause',
77
- Resume = 'Resume',
78
87
  Done = 'Done',
79
88
  Error = 'Error',
80
89
  PasswordRequired = 'PasswordRequired',
81
90
  UsePassword = 'UsePassword',
82
91
  Report = 'Report',
83
- }
92
+ }