@elefunc/send 0.1.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/LICENSE +21 -0
- package/README.md +57 -0
- package/package.json +52 -0
- package/patches/@rezi-ui%2Fcore@0.1.0-alpha.60.patch +227 -0
- package/patches/werift@0.22.9.patch +31 -0
- package/src/core/files.ts +79 -0
- package/src/core/paths.ts +19 -0
- package/src/core/protocol.ts +241 -0
- package/src/core/session.ts +1435 -0
- package/src/core/targeting.ts +39 -0
- package/src/index.ts +283 -0
- package/src/tui/app.ts +1442 -0
- package/src/tui/file-search-protocol.ts +48 -0
- package/src/tui/file-search.ts +282 -0
- package/src/tui/file-search.worker.ts +127 -0
- package/src/tui/rezi-checkbox-click.ts +63 -0
- package/src/types/bun-runtime.d.ts +5 -0
- package/src/types/bun-test.d.ts +9 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { RTCIceCandidateInit, RTCSessionDescriptionInit } from "werift"
|
|
2
|
+
|
|
3
|
+
export const SIGNAL_WS_URL = "wss://sig.efn.kr/ws"
|
|
4
|
+
export const BASE_ICE_SERVERS = [
|
|
5
|
+
{ urls: "stun:stun.cloudflare.com:3478" },
|
|
6
|
+
{ urls: "stun:stun.l.google.com:19302" },
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
export const CHUNK = 64 * 1024
|
|
10
|
+
export const BUFFER_HIGH = 1024 * 1024
|
|
11
|
+
export const FINAL_STATUSES = new Set(["complete", "rejected", "cancelled", "error"] as const)
|
|
12
|
+
export const SENDABLE_STATUSES = new Set(["queued", "offered", "accepted", "sending", "awaiting-done", "cancelling"] as const)
|
|
13
|
+
|
|
14
|
+
export type SocketState = "idle" | "connecting" | "open" | "closed" | "error"
|
|
15
|
+
export type Presence = "active" | "terminal"
|
|
16
|
+
export type Direction = "in" | "out"
|
|
17
|
+
export type TransferStatus =
|
|
18
|
+
| "pending"
|
|
19
|
+
| "queued"
|
|
20
|
+
| "offered"
|
|
21
|
+
| "accepted"
|
|
22
|
+
| "receiving"
|
|
23
|
+
| "sending"
|
|
24
|
+
| "awaiting-done"
|
|
25
|
+
| "cancelling"
|
|
26
|
+
| "complete"
|
|
27
|
+
| "rejected"
|
|
28
|
+
| "cancelled"
|
|
29
|
+
| "error"
|
|
30
|
+
|
|
31
|
+
export interface PeerProfile {
|
|
32
|
+
geo?: {
|
|
33
|
+
city?: string
|
|
34
|
+
region?: string
|
|
35
|
+
country?: string
|
|
36
|
+
timezone?: string
|
|
37
|
+
}
|
|
38
|
+
network?: {
|
|
39
|
+
colo?: string
|
|
40
|
+
asOrganization?: string
|
|
41
|
+
asn?: number
|
|
42
|
+
ip?: string
|
|
43
|
+
}
|
|
44
|
+
ua?: {
|
|
45
|
+
browser?: string
|
|
46
|
+
os?: string
|
|
47
|
+
device?: string
|
|
48
|
+
}
|
|
49
|
+
defaults?: {
|
|
50
|
+
autoAcceptIncoming?: boolean
|
|
51
|
+
autoSaveIncoming?: boolean
|
|
52
|
+
}
|
|
53
|
+
ready?: boolean
|
|
54
|
+
error?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SignalEnvelope {
|
|
58
|
+
room: string
|
|
59
|
+
from: string
|
|
60
|
+
to: string
|
|
61
|
+
at: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface HelloSignal extends SignalEnvelope {
|
|
65
|
+
kind: "hello"
|
|
66
|
+
name: string
|
|
67
|
+
turnAvailable: boolean
|
|
68
|
+
profile?: PeerProfile
|
|
69
|
+
rtcEpoch?: number
|
|
70
|
+
reply?: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface NameSignal extends SignalEnvelope {
|
|
74
|
+
kind: "name"
|
|
75
|
+
name: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ProfileSignal extends SignalEnvelope {
|
|
79
|
+
kind: "profile"
|
|
80
|
+
name?: string
|
|
81
|
+
turnAvailable?: boolean
|
|
82
|
+
profile?: PeerProfile
|
|
83
|
+
rtcEpoch?: number
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ByeSignal extends SignalEnvelope {
|
|
87
|
+
kind: "bye"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface DescriptionSignal extends SignalEnvelope {
|
|
91
|
+
kind: "description"
|
|
92
|
+
name?: string
|
|
93
|
+
turnAvailable?: boolean
|
|
94
|
+
profile?: PeerProfile
|
|
95
|
+
rtcEpoch?: number
|
|
96
|
+
description: RTCSessionDescriptionInit
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CandidateSignal extends SignalEnvelope {
|
|
100
|
+
kind: "candidate"
|
|
101
|
+
name?: string
|
|
102
|
+
turnAvailable?: boolean
|
|
103
|
+
profile?: PeerProfile
|
|
104
|
+
rtcEpoch?: number
|
|
105
|
+
candidate: RTCIceCandidateInit
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type SignalMessage =
|
|
109
|
+
| HelloSignal
|
|
110
|
+
| NameSignal
|
|
111
|
+
| ProfileSignal
|
|
112
|
+
| ByeSignal
|
|
113
|
+
| DescriptionSignal
|
|
114
|
+
| CandidateSignal
|
|
115
|
+
|
|
116
|
+
export interface DataEnvelope {
|
|
117
|
+
room: string
|
|
118
|
+
from: string
|
|
119
|
+
to: string
|
|
120
|
+
at: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface FileOfferMessage extends DataEnvelope {
|
|
124
|
+
kind: "file-offer"
|
|
125
|
+
transferId: string
|
|
126
|
+
name: string
|
|
127
|
+
size: number
|
|
128
|
+
type: string
|
|
129
|
+
lastModified: number
|
|
130
|
+
chunkSize: number
|
|
131
|
+
totalChunks: number
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface FileAcceptMessage extends DataEnvelope {
|
|
135
|
+
kind: "file-accept"
|
|
136
|
+
transferId: string
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface FileStartMessage extends DataEnvelope {
|
|
140
|
+
kind: "file-start"
|
|
141
|
+
transferId: string
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface FileEndMessage extends DataEnvelope {
|
|
145
|
+
kind: "file-end"
|
|
146
|
+
transferId: string
|
|
147
|
+
size: number
|
|
148
|
+
totalChunks: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface FileDoneMessage extends DataEnvelope {
|
|
152
|
+
kind: "file-done"
|
|
153
|
+
transferId: string
|
|
154
|
+
size: number
|
|
155
|
+
totalChunks: number
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface FileRejectMessage extends DataEnvelope {
|
|
159
|
+
kind: "file-reject"
|
|
160
|
+
transferId: string
|
|
161
|
+
reason: string
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface FileCancelMessage extends DataEnvelope {
|
|
165
|
+
kind: "file-cancel"
|
|
166
|
+
transferId: string
|
|
167
|
+
reason: string
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface FileErrorMessage extends DataEnvelope {
|
|
171
|
+
kind: "file-error"
|
|
172
|
+
transferId: string
|
|
173
|
+
reason: string
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type DataMessage =
|
|
177
|
+
| FileOfferMessage
|
|
178
|
+
| FileAcceptMessage
|
|
179
|
+
| FileStartMessage
|
|
180
|
+
| FileEndMessage
|
|
181
|
+
| FileDoneMessage
|
|
182
|
+
| FileRejectMessage
|
|
183
|
+
| FileCancelMessage
|
|
184
|
+
| FileErrorMessage
|
|
185
|
+
|
|
186
|
+
export interface LogEntry {
|
|
187
|
+
id: string
|
|
188
|
+
at: number
|
|
189
|
+
kind: string
|
|
190
|
+
level: "info" | "error"
|
|
191
|
+
payload: unknown
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const fallbackName = "user"
|
|
195
|
+
export const turnStateLabel = (hasTurn: boolean) => hasTurn ? "custom-turn" : "stun"
|
|
196
|
+
export const stamp = () => new Date().toISOString()
|
|
197
|
+
export const uid = (n = 8) => [...crypto.getRandomValues(new Uint8Array(n))].map(value => (value % 36).toString(36)).join("")
|
|
198
|
+
|
|
199
|
+
export const cleanText = (value: unknown, max = 72) => `${value ?? ""}`.trim().replace(/\s+/g, " ").slice(0, max)
|
|
200
|
+
export const cleanRoom = (value: unknown) => cleanText(value).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || uid(8)
|
|
201
|
+
export const cleanName = (value: unknown) => cleanText(value, 24).toLowerCase().replace(/[^a-z0-9]+/g, "").slice(0, 24) || fallbackName
|
|
202
|
+
export const cleanLocalId = (value: unknown) => cleanText(value, 24).toLowerCase().replace(/[^a-z0-9]+/g, "").slice(0, 24) || uid(8)
|
|
203
|
+
export const signalEpoch = (value: unknown) => Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : 0
|
|
204
|
+
|
|
205
|
+
export const buildCliProfile = (): PeerProfile => ({
|
|
206
|
+
ua: { browser: "send-cli", os: process.platform, device: "desktop" },
|
|
207
|
+
ready: true,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
export const peerDefaultsToken = (profile?: PeerProfile) => {
|
|
211
|
+
const autoAcceptIncoming = typeof profile?.defaults?.autoAcceptIncoming === "boolean" ? profile.defaults.autoAcceptIncoming : null
|
|
212
|
+
const autoSaveIncoming = typeof profile?.defaults?.autoSaveIncoming === "boolean" ? profile.defaults.autoSaveIncoming : null
|
|
213
|
+
return autoAcceptIncoming === null || autoSaveIncoming === null ? "??" : `${autoAcceptIncoming ? "A" : "a"}${autoSaveIncoming ? "S" : "s"}`
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const displayPeerName = (name: string, id: string) => `${cleanName(name)}-${id}`
|
|
217
|
+
export const clamp = (value: number) => Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0))
|
|
218
|
+
|
|
219
|
+
export const formatBytes = (value: number) => {
|
|
220
|
+
const size = Number(value) || 0
|
|
221
|
+
if (!size) return "0 B"
|
|
222
|
+
const units = ["B", "KB", "MB", "GB", "TB"]
|
|
223
|
+
const tier = Math.min(units.length - 1, Math.floor(Math.log(size) / Math.log(1024)))
|
|
224
|
+
const scaled = size / 1024 ** tier
|
|
225
|
+
return `${scaled >= 100 || tier === 0 ? scaled.toFixed(0) : scaled >= 10 ? scaled.toFixed(1) : scaled.toFixed(2)} ${units[tier]}`
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const formatRate = (value: number) => value > 0 ? `${formatBytes(value)}/s` : "0 B/s"
|
|
229
|
+
export const formatEta = (value: number) => !Number.isFinite(value) || value < 0 ? "—" : value < 1 ? "<1s" : value < 60 ? `${value.toFixed(0)}s` : `${Math.floor(value / 60)}m ${(value % 60).toFixed(0)}s`
|
|
230
|
+
|
|
231
|
+
export const formatDuration = (value: number) => {
|
|
232
|
+
const ms = Number(value) || 0
|
|
233
|
+
if (!Number.isFinite(ms) || ms <= 0) return "—"
|
|
234
|
+
if (ms < 1000) return "<1s"
|
|
235
|
+
const total = Math.round(ms / 1000)
|
|
236
|
+
if (total < 60) return `${total}s`
|
|
237
|
+
const hours = Math.floor(total / 3600)
|
|
238
|
+
const minutes = Math.floor((total % 3600) / 60)
|
|
239
|
+
const seconds = total % 60
|
|
240
|
+
return hours ? `${hours}h ${`${minutes}`.padStart(2, "0")}m` : `${minutes}m ${`${seconds}`.padStart(2, "0")}s`
|
|
241
|
+
}
|