edge-core-js 2.41.1 → 2.41.3
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/CHANGELOG.md +8 -0
- package/android/src/main/assets/edge-core-js/edge-core.js +1 -1
- package/android/src/main/assets/edge-core-js/index.html +1 -1
- package/android/src/main/java/app/edge/reactnative/core/EdgeCoreModule.java +31 -0
- package/android/src/main/java/app/edge/reactnative/core/EdgeCorePackage.java +1 -1
- package/android/src/main/java/app/edge/reactnative/core/EdgeCoreWebView.java +117 -65
- package/android/src/main/java/app/edge/reactnative/core/LocalContentWebViewClient.java +79 -0
- package/edge-core-js.podspec +3 -1
- package/ios/EdgeAssetsSchemeHandler.swift +175 -0
- package/ios/EdgeCoreModule.m +4 -0
- package/ios/EdgeCoreModule.swift +30 -0
- package/ios/EdgeCoreWebView.swift +32 -45
- package/lib/io/react-native/react-native-worker.js +3 -31
- package/lib/libs.d.js +12 -0
- package/lib/react-native.js +41 -4
- package/lib/util/nym.js +1 -8
- package/package.json +1 -1
- package/android/src/main/java/app/edge/reactnative/core/BundleHTTPServer.java +0 -318
- package/ios/BundleHTTPServer.swift +0 -394
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import Network
|
|
3
|
-
|
|
4
|
-
class BundleHTTPServer {
|
|
5
|
-
private var listener: NWListener?
|
|
6
|
-
private(set) var assignedPort: UInt16 = 0
|
|
7
|
-
private let queue = DispatchQueue(label: "com.edge.bundleserver")
|
|
8
|
-
|
|
9
|
-
enum ServerError: Error {
|
|
10
|
-
case portUnavailable
|
|
11
|
-
case bindFailed(Error)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
init() {}
|
|
15
|
-
|
|
16
|
-
/// Starts the HTTP server on an ephemeral port bound to loopback only (127.0.0.1).
|
|
17
|
-
/// This prevents other devices on the network from connecting to the server.
|
|
18
|
-
/// - Parameter completion: Called with the assigned port on success, or an error on failure.
|
|
19
|
-
/// This is called on the server's dispatch queue.
|
|
20
|
-
func start(completion: @escaping (Result<UInt16, Error>) -> Void) {
|
|
21
|
-
do {
|
|
22
|
-
// Configure TCP parameters to bind to loopback only (127.0.0.1)
|
|
23
|
-
// Port 0 tells the OS to assign an available ephemeral port
|
|
24
|
-
let params = NWParameters.tcp
|
|
25
|
-
params.requiredLocalEndpoint = NWEndpoint.hostPort(host: "127.0.0.1", port: 0)
|
|
26
|
-
|
|
27
|
-
listener = try NWListener(using: params)
|
|
28
|
-
|
|
29
|
-
var completionCalled = false
|
|
30
|
-
listener?.stateUpdateHandler = { [weak self] state in
|
|
31
|
-
switch state {
|
|
32
|
-
case .ready:
|
|
33
|
-
// Get the assigned ephemeral port from the listener
|
|
34
|
-
if let port = self?.listener?.port?.rawValue {
|
|
35
|
-
self?.assignedPort = port
|
|
36
|
-
print("BundleHttpServer ready on 127.0.0.1:\(port)")
|
|
37
|
-
if !completionCalled {
|
|
38
|
-
completionCalled = true
|
|
39
|
-
completion(.success(port))
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
print("BundleHttpServer failed: could not get assigned port")
|
|
43
|
-
if !completionCalled {
|
|
44
|
-
completionCalled = true
|
|
45
|
-
completion(.failure(ServerError.portUnavailable))
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
case .failed(let error):
|
|
49
|
-
print("BundleHttpServer failed with error: \(error)")
|
|
50
|
-
if !completionCalled {
|
|
51
|
-
completionCalled = true
|
|
52
|
-
completion(.failure(error))
|
|
53
|
-
}
|
|
54
|
-
case .cancelled:
|
|
55
|
-
print("BundleHttpServer was cancelled")
|
|
56
|
-
default:
|
|
57
|
-
break
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
listener?.newConnectionHandler = { [weak self] connection in
|
|
62
|
-
self?.handleConnection(connection)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
listener?.start(queue: queue)
|
|
66
|
-
} catch {
|
|
67
|
-
print("Failed to start HTTP server: \(error)")
|
|
68
|
-
completion(.failure(ServerError.bindFailed(error)))
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
func stop() {
|
|
73
|
-
listener?.cancel()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private func handleConnection(_ connection: NWConnection) {
|
|
77
|
-
connection.stateUpdateHandler = { state in
|
|
78
|
-
switch state {
|
|
79
|
-
case .ready:
|
|
80
|
-
self.receiveRequest(on: connection)
|
|
81
|
-
case .failed(let error):
|
|
82
|
-
print("Connection failed: \(error)")
|
|
83
|
-
connection.cancel()
|
|
84
|
-
case .cancelled:
|
|
85
|
-
break
|
|
86
|
-
default:
|
|
87
|
-
break
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
connection.start(queue: queue)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private func receiveRequest(on connection: NWConnection) {
|
|
95
|
-
connection.receive(minimumIncompleteLength: 1, maximumLength: 2048) { [weak self] content, _, isComplete, error in
|
|
96
|
-
guard let self = self else { return }
|
|
97
|
-
|
|
98
|
-
if let error = error {
|
|
99
|
-
print("Error receiving request: \(error)")
|
|
100
|
-
connection.cancel()
|
|
101
|
-
return
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
guard let content = content, !content.isEmpty else {
|
|
105
|
-
if isComplete {
|
|
106
|
-
connection.cancel()
|
|
107
|
-
}
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Parse the request
|
|
112
|
-
if let requestString = String(data: content, encoding: .utf8) {
|
|
113
|
-
let requestLines = requestString.components(separatedBy: "\r\n")
|
|
114
|
-
if let firstLine = requestLines.first {
|
|
115
|
-
let components = firstLine.components(separatedBy: " ")
|
|
116
|
-
if components.count >= 2 {
|
|
117
|
-
let method = components[0]
|
|
118
|
-
var path = components[1]
|
|
119
|
-
|
|
120
|
-
// Remove query parameters if any
|
|
121
|
-
if let queryStart = path.firstIndex(of: "?") {
|
|
122
|
-
path = String(path[..<queryStart])
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Require explicit file name - no auto-matching for root path
|
|
126
|
-
if path == "/" {
|
|
127
|
-
self.sendResponse(code: 404, body: "Not Found", connection: connection)
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Handle plugin bundle requests (e.g., /plugin/edge-currency-accountbased.bundle/edge-currency-accountbased.js)
|
|
132
|
-
if path.hasPrefix("/plugin/") {
|
|
133
|
-
let pluginPath = String(path.dropFirst("/plugin/".count))
|
|
134
|
-
self.servePluginFile(pluginPath, method: method, connection: connection)
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Remove leading slash for bundle resource lookup
|
|
139
|
-
let resourcePath = String(path.dropFirst())
|
|
140
|
-
|
|
141
|
-
self.serveFile(resourcePath, method: method, connection: connection)
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// If we got here, the request was invalid
|
|
148
|
-
self.sendResponse(code: 400, body: "Bad Request", connection: connection)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
private func serveFile(_ path: String, method: String, connection: NWConnection) {
|
|
153
|
-
// Only support GET requests
|
|
154
|
-
guard method == "GET" else {
|
|
155
|
-
sendResponse(code: 405, body: "Method Not Allowed", connection: connection)
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Require explicit file name - no auto-matching for empty paths
|
|
160
|
-
guard !path.isEmpty else {
|
|
161
|
-
sendResponse(code: 404, body: "Not Found", connection: connection)
|
|
162
|
-
return
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Parse filename and extension properly (handles multi-dot filenames like "some.file.js")
|
|
166
|
-
let nsPath = path as NSString
|
|
167
|
-
let fileExtension = nsPath.pathExtension
|
|
168
|
-
let filename = nsPath.deletingPathExtension
|
|
169
|
-
|
|
170
|
-
// Try to find the resource in the main bundle first
|
|
171
|
-
var url: URL?
|
|
172
|
-
var data: Data?
|
|
173
|
-
|
|
174
|
-
// Check if this is a request for a bundled file
|
|
175
|
-
if let bundleUrl = Bundle.main.url(forResource: "edge-core-js", withExtension: "bundle"),
|
|
176
|
-
let bundle = Bundle(url: bundleUrl) {
|
|
177
|
-
if !fileExtension.isEmpty {
|
|
178
|
-
url = bundle.url(forResource: filename, withExtension: fileExtension)
|
|
179
|
-
} else {
|
|
180
|
-
url = bundle.url(forResource: path, withExtension: nil)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// If not found in the bundle, check the main bundle directly
|
|
185
|
-
if url == nil {
|
|
186
|
-
if !fileExtension.isEmpty {
|
|
187
|
-
url = Bundle.main.url(forResource: filename, withExtension: fileExtension)
|
|
188
|
-
} else {
|
|
189
|
-
url = Bundle.main.url(forResource: path, withExtension: nil)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if let url = url {
|
|
194
|
-
do {
|
|
195
|
-
data = try Data(contentsOf: url)
|
|
196
|
-
} catch {
|
|
197
|
-
print("Error reading file: \(error)")
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
guard let fileData = data else {
|
|
202
|
-
sendResponse(code: 404, body: "Not Found", connection: connection)
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
let mimeType = mimeTypeForPath(path)
|
|
207
|
-
let headers = [
|
|
208
|
-
"HTTP/1.1 200 OK",
|
|
209
|
-
"Content-Type: \(mimeType)",
|
|
210
|
-
"Content-Length: \(fileData.count)",
|
|
211
|
-
"Connection: close",
|
|
212
|
-
"Server: EdgeCoreBundleServer/1.0",
|
|
213
|
-
// Cross-origin isolation headers required for SharedArrayBuffer (needed by mixFetch web workers)
|
|
214
|
-
"Cross-Origin-Opener-Policy: same-origin",
|
|
215
|
-
"Cross-Origin-Embedder-Policy: require-corp",
|
|
216
|
-
"\r\n"
|
|
217
|
-
].joined(separator: "\r\n")
|
|
218
|
-
|
|
219
|
-
let headerData = headers.data(using: .utf8)!
|
|
220
|
-
let responseData = NSMutableData()
|
|
221
|
-
responseData.append(headerData)
|
|
222
|
-
responseData.append(fileData)
|
|
223
|
-
|
|
224
|
-
connection.send(content: responseData as Data, completion: .contentProcessed { error in
|
|
225
|
-
if let error = error {
|
|
226
|
-
print("Error sending response: \(error)")
|
|
227
|
-
}
|
|
228
|
-
connection.cancel()
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
private func servePluginFile(_ path: String, method: String, connection: NWConnection) {
|
|
233
|
-
// Only support GET requests
|
|
234
|
-
guard method == "GET" else {
|
|
235
|
-
sendResponse(code: 405, body: "Method Not Allowed", connection: connection)
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Get the app's main bundle path - plugins are in edge-core/ subdirectory
|
|
240
|
-
let bundlePath = Bundle.main.bundlePath
|
|
241
|
-
let edgeCorePath = (bundlePath as NSString).appendingPathComponent("edge-core")
|
|
242
|
-
var data: Data?
|
|
243
|
-
|
|
244
|
-
// Try multiple path patterns
|
|
245
|
-
let pathsToTry: [String]
|
|
246
|
-
|
|
247
|
-
if path.contains(".bundle/") {
|
|
248
|
-
// Path like: "edge-currency-accountbased.bundle/edge-currency-accountbased.js"
|
|
249
|
-
pathsToTry = [path]
|
|
250
|
-
} else {
|
|
251
|
-
// Path like: "plugin-bundle.js" - try with .bundle folder too
|
|
252
|
-
let fileName = (path as NSString).lastPathComponent
|
|
253
|
-
let baseName = (fileName as NSString).deletingPathExtension
|
|
254
|
-
pathsToTry = [
|
|
255
|
-
path, // plugin-bundle.js
|
|
256
|
-
"\(baseName).bundle/\(fileName)" // plugin-bundle.bundle/plugin-bundle.js
|
|
257
|
-
]
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
for relativePath in pathsToTry {
|
|
261
|
-
// Try edge-core/ subdirectory first (for plugin-bundle.js)
|
|
262
|
-
let edgeCoreFull = (edgeCorePath as NSString).appendingPathComponent(relativePath)
|
|
263
|
-
if FileManager.default.fileExists(atPath: edgeCoreFull) {
|
|
264
|
-
do {
|
|
265
|
-
data = try Data(contentsOf: URL(fileURLWithPath: edgeCoreFull))
|
|
266
|
-
print("Found plugin file at: \(edgeCoreFull)")
|
|
267
|
-
break
|
|
268
|
-
} catch {
|
|
269
|
-
print("Error reading file at \(edgeCoreFull): \(error)")
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Fall back to app bundle root (for .bundle/ plugins)
|
|
274
|
-
if data == nil {
|
|
275
|
-
let rootFull = (bundlePath as NSString).appendingPathComponent(relativePath)
|
|
276
|
-
if FileManager.default.fileExists(atPath: rootFull) {
|
|
277
|
-
do {
|
|
278
|
-
data = try Data(contentsOf: URL(fileURLWithPath: rootFull))
|
|
279
|
-
print("Found plugin file at: \(rootFull)")
|
|
280
|
-
break
|
|
281
|
-
} catch {
|
|
282
|
-
print("Error reading file at \(rootFull): \(error)")
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
guard let fileData = data else {
|
|
289
|
-
print("Plugin file not found: \(path)")
|
|
290
|
-
sendResponse(code: 404, body: "Not Found: \(path)", connection: connection)
|
|
291
|
-
return
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
let mimeType = mimeTypeForPath(path)
|
|
295
|
-
let headers = [
|
|
296
|
-
"HTTP/1.1 200 OK",
|
|
297
|
-
"Content-Type: \(mimeType)",
|
|
298
|
-
"Content-Length: \(fileData.count)",
|
|
299
|
-
"Connection: close",
|
|
300
|
-
"Server: EdgeCoreBundleServer/1.0",
|
|
301
|
-
// Cross-origin isolation headers required for SharedArrayBuffer (needed by mixFetch web workers)
|
|
302
|
-
"Cross-Origin-Opener-Policy: same-origin",
|
|
303
|
-
"Cross-Origin-Embedder-Policy: require-corp",
|
|
304
|
-
"\r\n"
|
|
305
|
-
].joined(separator: "\r\n")
|
|
306
|
-
|
|
307
|
-
let headerData = headers.data(using: .utf8)!
|
|
308
|
-
let responseData = NSMutableData()
|
|
309
|
-
responseData.append(headerData)
|
|
310
|
-
responseData.append(fileData)
|
|
311
|
-
|
|
312
|
-
connection.send(content: responseData as Data, completion: .contentProcessed { error in
|
|
313
|
-
if let error = error {
|
|
314
|
-
print("Error sending plugin response: \(error)")
|
|
315
|
-
}
|
|
316
|
-
connection.cancel()
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private func sendResponse(code: Int, body: String, connection: NWConnection) {
|
|
321
|
-
var status = ""
|
|
322
|
-
switch code {
|
|
323
|
-
case 200: status = "OK"
|
|
324
|
-
case 400: status = "Bad Request"
|
|
325
|
-
case 404: status = "Not Found"
|
|
326
|
-
case 405: status = "Method Not Allowed"
|
|
327
|
-
default: status = "Internal Server Error"
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let bodyData = body.data(using: .utf8)!
|
|
331
|
-
let headers = [
|
|
332
|
-
"HTTP/1.1 \(code) \(status)",
|
|
333
|
-
"Content-Type: text/plain",
|
|
334
|
-
"Content-Length: \(bodyData.count)",
|
|
335
|
-
"Connection: close",
|
|
336
|
-
"Server: EdgeCoreBundleServer/1.0",
|
|
337
|
-
// Cross-origin isolation headers required for SharedArrayBuffer (needed by mixFetch web workers)
|
|
338
|
-
"Cross-Origin-Opener-Policy: same-origin",
|
|
339
|
-
"Cross-Origin-Embedder-Policy: require-corp",
|
|
340
|
-
"\r\n"
|
|
341
|
-
].joined(separator: "\r\n")
|
|
342
|
-
|
|
343
|
-
let headerData = headers.data(using: .utf8)!
|
|
344
|
-
let responseData = NSMutableData()
|
|
345
|
-
responseData.append(headerData)
|
|
346
|
-
responseData.append(bodyData)
|
|
347
|
-
|
|
348
|
-
connection.send(content: responseData as Data, completion: .contentProcessed { error in
|
|
349
|
-
if let error = error {
|
|
350
|
-
print("Error sending response: \(error)")
|
|
351
|
-
}
|
|
352
|
-
connection.cancel()
|
|
353
|
-
})
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
private func sendHtmlResponse(html: String, connection: NWConnection) {
|
|
357
|
-
let bodyData = html.data(using: .utf8)!
|
|
358
|
-
let headers = [
|
|
359
|
-
"HTTP/1.1 200 OK",
|
|
360
|
-
"Content-Type: text/html",
|
|
361
|
-
"Content-Length: \(bodyData.count)",
|
|
362
|
-
"Connection: close",
|
|
363
|
-
"Server: EdgeCoreBundleServer/1.0",
|
|
364
|
-
// Cross-origin isolation headers required for SharedArrayBuffer (needed by mixFetch web workers)
|
|
365
|
-
"Cross-Origin-Opener-Policy: same-origin",
|
|
366
|
-
"Cross-Origin-Embedder-Policy: require-corp",
|
|
367
|
-
"\r\n"
|
|
368
|
-
].joined(separator: "\r\n")
|
|
369
|
-
|
|
370
|
-
let headerData = headers.data(using: .utf8)!
|
|
371
|
-
let responseData = NSMutableData()
|
|
372
|
-
responseData.append(headerData)
|
|
373
|
-
responseData.append(bodyData)
|
|
374
|
-
|
|
375
|
-
connection.send(content: responseData as Data, completion: .contentProcessed { error in
|
|
376
|
-
if let error = error {
|
|
377
|
-
print("Error sending response: \(error)")
|
|
378
|
-
}
|
|
379
|
-
connection.cancel()
|
|
380
|
-
})
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private func mimeTypeForPath(_ path: String) -> String {
|
|
384
|
-
let ext = (path as NSString).pathExtension.lowercased()
|
|
385
|
-
|
|
386
|
-
// We only serve HTML, JS, and WASM files
|
|
387
|
-
switch ext {
|
|
388
|
-
case "html", "htm": return "text/html"
|
|
389
|
-
case "js": return "application/javascript"
|
|
390
|
-
case "wasm": return "application/wasm"
|
|
391
|
-
default: return "application/octet-stream"
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|