@onekeyfe/react-native-sni-connect 1.0.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.
@@ -0,0 +1,490 @@
1
+ import Foundation
2
+ import EMASCurl
3
+
4
+ /// Core HTTPS client that enforces IP direct connection with SNI.
5
+ final class SniConnectClient {
6
+
7
+ // Logging callback
8
+ var onLog: ((String, String) -> Void)?
9
+
10
+ // Active requests tracking for cancellation support
11
+ private var activeTasks: [String: Task<Response, Error>] = [:]
12
+ private let tasksQueue = DispatchQueue(label: "com.onekey.sni.connect.tasks", attributes: .concurrent)
13
+
14
+ init() {
15
+ // Register for memory warnings to clean cache
16
+ NotificationCenter.default.addObserver(
17
+ forName: UIApplication.didReceiveMemoryWarningNotification,
18
+ object: nil,
19
+ queue: .main
20
+ ) { [weak self] _ in
21
+ self?.onLog?("warning", "Memory warning received, cleaning DNS cache")
22
+ DNSResolver.cleanExpiredEntries()
23
+ }
24
+ }
25
+
26
+ deinit {
27
+ NotificationCenter.default.removeObserver(self)
28
+ }
29
+
30
+ struct RequestConfig {
31
+ let requestId: String? // Optional request ID for cancellation
32
+ let ip: String
33
+ let hostname: String
34
+ let method: String
35
+ let path: String
36
+ let headers: [String: String]
37
+ let body: String?
38
+ let timeout: TimeInterval
39
+
40
+ // Advanced timeout configurations
41
+ let connectTimeout: TimeInterval? // Connection establishment timeout
42
+ let totalTimeout: TimeInterval? // Total request timeout (overrides `timeout`)
43
+
44
+ var effectiveConnectTimeout: TimeInterval {
45
+ connectTimeout ?? min(timeout / 3, 10.0)
46
+ }
47
+
48
+ var effectiveTotalTimeout: TimeInterval {
49
+ totalTimeout ?? timeout
50
+ }
51
+ }
52
+
53
+ struct Response {
54
+ let data: Any
55
+ let status: Int
56
+ let statusText: String
57
+ let headers: [String: String] // Single-value headers (backward compatible)
58
+ let multiValueHeaders: [String: [String]] // Multi-value headers (e.g., Set-Cookie)
59
+ }
60
+
61
+ enum SniConnectError: Error {
62
+ case invalidURL
63
+ case invalidConfig(String)
64
+ case dnsResolutionFailed(String)
65
+ case tlsHandshakeFailed(String)
66
+ case certificateValidationFailed(String)
67
+ case connectionTimeout
68
+ case connectionRefused
69
+ case networkUnreachable
70
+ case requestTimeout
71
+ case httpError(code: Int, message: String)
72
+ case cancelled
73
+ case unknown(Error)
74
+
75
+ var code: String {
76
+ switch self {
77
+ case .invalidURL: return "SNI_INVALID_URL"
78
+ case .invalidConfig: return "SNI_INVALID_CONFIG"
79
+ case .dnsResolutionFailed: return "SNI_DNS_FAILED"
80
+ case .tlsHandshakeFailed: return "SNI_TLS_FAILED"
81
+ case .certificateValidationFailed: return "SNI_CERT_FAILED"
82
+ case .connectionTimeout: return "SNI_TIMEOUT"
83
+ case .connectionRefused: return "SNI_CONNECTION_REFUSED"
84
+ case .networkUnreachable: return "SNI_NETWORK_UNREACHABLE"
85
+ case .requestTimeout: return "SNI_REQUEST_TIMEOUT"
86
+ case .httpError: return "SNI_HTTP_ERROR"
87
+ case .cancelled: return "SNI_CANCELLED"
88
+ case .unknown: return "SNI_UNKNOWN_ERROR"
89
+ }
90
+ }
91
+
92
+ var message: String {
93
+ switch self {
94
+ case .invalidURL:
95
+ return "Invalid URL format"
96
+ case .invalidConfig(let details):
97
+ return "Invalid configuration: \(details)"
98
+ case .dnsResolutionFailed(let domain):
99
+ return "DNS resolution failed for domain: \(domain)"
100
+ case .tlsHandshakeFailed(let details):
101
+ return "TLS handshake failed: \(details)"
102
+ case .certificateValidationFailed(let details):
103
+ return "Certificate validation failed: \(details)"
104
+ case .connectionTimeout:
105
+ return "Connection timeout"
106
+ case .connectionRefused:
107
+ return "Connection refused by server"
108
+ case .networkUnreachable:
109
+ return "Network unreachable"
110
+ case .requestTimeout:
111
+ return "Request timeout"
112
+ case .httpError(let code, let message):
113
+ return "HTTP error \(code): \(message)"
114
+ case .cancelled:
115
+ return "Request cancelled"
116
+ case .unknown(let error):
117
+ return "Unknown error: \(error.localizedDescription)"
118
+ }
119
+ }
120
+
121
+ /// Convert NSError to SniConnectError with detailed classification
122
+ static func from(_ error: Error) -> SniConnectError {
123
+ let nsError = error as NSError
124
+
125
+ // Check for URL-related errors
126
+ if nsError.domain == NSURLErrorDomain {
127
+ switch nsError.code {
128
+ case NSURLErrorTimedOut:
129
+ return .requestTimeout
130
+ case NSURLErrorCannotConnectToHost:
131
+ return .connectionRefused
132
+ case NSURLErrorNotConnectedToInternet, NSURLErrorNetworkConnectionLost:
133
+ return .networkUnreachable
134
+ case NSURLErrorSecureConnectionFailed:
135
+ return .tlsHandshakeFailed(nsError.localizedDescription)
136
+ case NSURLErrorServerCertificateUntrusted, NSURLErrorServerCertificateHasBadDate,
137
+ NSURLErrorServerCertificateHasUnknownRoot, NSURLErrorServerCertificateNotYetValid:
138
+ return .certificateValidationFailed(nsError.localizedDescription)
139
+ case NSURLErrorCannotFindHost, NSURLErrorDNSLookupFailed:
140
+ return .dnsResolutionFailed(nsError.localizedDescription)
141
+ case NSURLErrorCancelled:
142
+ return .cancelled
143
+ default:
144
+ return .unknown(error)
145
+ }
146
+ }
147
+
148
+ return .unknown(error)
149
+ }
150
+ }
151
+
152
+ private static let urlSession: URLSession = {
153
+ let configuration = URLSessionConfiguration.default
154
+ configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
155
+ configuration.urlCache = nil
156
+ configuration.httpCookieStorage = nil
157
+ configuration.httpShouldSetCookies = false
158
+ configuration.shouldUseExtendedBackgroundIdleMode = false
159
+
160
+ let curlConfig = EMASCurlConfiguration.default()
161
+ curlConfig.httpVersion = .HTTP2
162
+ curlConfig.connectTimeoutInterval = 2.5
163
+ curlConfig.enableBuiltInGzip = true
164
+ curlConfig.enableBuiltInRedirection = true
165
+ curlConfig.cacheEnabled = false
166
+
167
+ // Enable full certificate validation for security
168
+ // The certificate will be validated against the SNI hostname, not the IP
169
+ curlConfig.certificateValidationEnabled = true
170
+ curlConfig.domainNameVerificationEnabled = true
171
+ curlConfig.dnsResolver = DNSResolver.self
172
+
173
+ EMASCurlProtocol.install(into: configuration, with: curlConfig)
174
+ return URLSession(configuration: configuration)
175
+ }()
176
+
177
+ @objc private final class DNSResolver: NSObject, EMASCurlProtocolDNSResolver {
178
+ private static let queue = DispatchQueue(label: "com.onekey.sni.connect.dns", attributes: .concurrent)
179
+ private static let cache = DNSCache()
180
+
181
+ /// LRU cache for DNS mappings with size limit and TTL support
182
+ private class DNSCache {
183
+ private struct CacheEntry {
184
+ let ip: String
185
+ let timestamp: TimeInterval
186
+ }
187
+
188
+ private var storage: [String: CacheEntry] = [:]
189
+ private var accessOrder: [String] = []
190
+
191
+ // Maximum cache entries (100 as specified in requirements)
192
+ private let maxSize = 100
193
+ // TTL in seconds (5 minutes default)
194
+ private let ttl: TimeInterval = 300
195
+
196
+ func get(_ domain: String) -> String? {
197
+ let key = domain.lowercased()
198
+ guard let entry = storage[key] else { return nil }
199
+
200
+ // Check if entry has expired
201
+ let now = Date().timeIntervalSince1970
202
+ if now - entry.timestamp > ttl {
203
+ remove(key)
204
+ return nil
205
+ }
206
+
207
+ // Update access order (LRU)
208
+ if let index = accessOrder.firstIndex(of: key) {
209
+ accessOrder.remove(at: index)
210
+ }
211
+ accessOrder.append(key)
212
+
213
+ return entry.ip
214
+ }
215
+
216
+ func set(_ ip: String, for domain: String) {
217
+ let key = domain.lowercased()
218
+
219
+ // Evict oldest entry if cache is full
220
+ if storage.count >= maxSize && storage[key] == nil {
221
+ if let oldest = accessOrder.first {
222
+ storage.removeValue(forKey: oldest)
223
+ accessOrder.removeFirst()
224
+ }
225
+ }
226
+
227
+ // Add or update entry
228
+ let entry = CacheEntry(ip: ip, timestamp: Date().timeIntervalSince1970)
229
+ storage[key] = entry
230
+
231
+ // Update access order
232
+ if let index = accessOrder.firstIndex(of: key) {
233
+ accessOrder.remove(at: index)
234
+ }
235
+ accessOrder.append(key)
236
+ }
237
+
238
+ func remove(_ domain: String) {
239
+ let key = domain.lowercased()
240
+ storage.removeValue(forKey: key)
241
+ if let index = accessOrder.firstIndex(of: key) {
242
+ accessOrder.remove(at: index)
243
+ }
244
+ }
245
+
246
+ func clear() {
247
+ storage.removeAll()
248
+ accessOrder.removeAll()
249
+ }
250
+
251
+ func cleanExpired() {
252
+ let now = Date().timeIntervalSince1970
253
+ let expiredKeys = storage.filter { now - $0.value.timestamp > ttl }.map { $0.key }
254
+ for key in expiredKeys {
255
+ remove(key)
256
+ }
257
+ }
258
+ }
259
+
260
+ @objc static func resolveDomain(_ domain: String) -> String? {
261
+ var result: String?
262
+ queue.sync {
263
+ result = cache.get(domain)
264
+ }
265
+ return result
266
+ }
267
+
268
+ static func setIP(_ ip: String, for host: String) {
269
+ queue.sync(flags: .barrier) {
270
+ cache.set(ip, for: host)
271
+ }
272
+ }
273
+
274
+ /// Clear all DNS cache entries
275
+ static func clearCache() {
276
+ queue.sync(flags: .barrier) {
277
+ cache.clear()
278
+ }
279
+ }
280
+
281
+ /// Clean expired DNS cache entries
282
+ static func cleanExpiredEntries() {
283
+ queue.sync(flags: .barrier) {
284
+ cache.cleanExpired()
285
+ }
286
+ }
287
+ }
288
+
289
+ /// Clear all DNS cache entries
290
+ func clearDNSCache() {
291
+ DNSResolver.clearCache()
292
+ onLog?("info", "DNS cache cleared")
293
+ }
294
+
295
+ /// Cancel a request by ID
296
+ func cancelRequest(requestId: String) {
297
+ tasksQueue.async(flags: .barrier) { [weak self] in
298
+ guard let task = self?.activeTasks[requestId] else {
299
+ self?.onLog?("warning", "No active request found with ID: \(requestId)")
300
+ return
301
+ }
302
+ task.cancel()
303
+ self?.activeTasks.removeValue(forKey: requestId)
304
+ self?.onLog?("info", "Request cancelled: \(requestId)")
305
+ }
306
+ }
307
+
308
+ /// Cancel all active requests
309
+ func cancelAllRequests() {
310
+ tasksQueue.async(flags: .barrier) { [weak self] in
311
+ guard let self = self else { return }
312
+ let count = self.activeTasks.count
313
+ for (_, task) in self.activeTasks {
314
+ task.cancel()
315
+ }
316
+ self.activeTasks.removeAll()
317
+ self.onLog?("info", "Cancelled \(count) active requests")
318
+ }
319
+ }
320
+
321
+ /// Register an active task (synchronous for immediate registration)
322
+ func registerTask(_ task: Task<Response, Error>, for requestId: String) {
323
+ tasksQueue.sync(flags: .barrier) { [weak self] in
324
+ self?.activeTasks[requestId] = task
325
+ self?.onLog?("info", "Registered request: \(requestId), total active: \(self?.activeTasks.count ?? 0)")
326
+ }
327
+ }
328
+
329
+ /// Unregister a completed or failed task
330
+ private func unregisterTask(requestId: String?) {
331
+ guard let requestId = requestId else { return }
332
+ tasksQueue.async(flags: .barrier) { [weak self] in
333
+ self?.activeTasks.removeValue(forKey: requestId)
334
+ self?.onLog?("info", "Unregistered request: \(requestId)")
335
+ }
336
+ }
337
+
338
+ func performRequest(config: RequestConfig) async throws -> Response {
339
+ // Check if task is cancelled
340
+ try Task.checkCancellation()
341
+
342
+ defer {
343
+ unregisterTask(requestId: config.requestId)
344
+ }
345
+
346
+ DNSResolver.setIP(config.ip, for: config.hostname)
347
+
348
+ let url = try Self.buildURL(hostname: config.hostname, path: config.path)
349
+
350
+ let mutableRequest = NSMutableURLRequest(url: url)
351
+ mutableRequest.httpMethod = config.method.uppercased()
352
+
353
+ // Convert milliseconds to seconds for timeout values
354
+ let totalTimeoutSeconds = config.effectiveTotalTimeout / 1000.0
355
+ let connectTimeoutSeconds = config.effectiveConnectTimeout / 1000.0
356
+
357
+ // Set total request timeout
358
+ mutableRequest.timeoutInterval = totalTimeoutSeconds
359
+ mutableRequest.cachePolicy = .reloadIgnoringLocalCacheData
360
+
361
+ // Explicitly set Host header for SNI
362
+ mutableRequest.setValue(config.hostname, forHTTPHeaderField: "Host")
363
+
364
+ for (key, value) in config.headers {
365
+ if key.caseInsensitiveCompare("host") == .orderedSame {
366
+ // Host header is already set above, skip duplicate
367
+ continue
368
+ }
369
+ mutableRequest.setValue(value, forHTTPHeaderField: key)
370
+ }
371
+
372
+ if let bodyString = config.body, let bodyData = bodyString.data(using: .utf8) {
373
+ mutableRequest.httpBody = bodyData
374
+ }
375
+
376
+ // Configure connection timeout for EMASCurl
377
+ EMASCurlProtocol.setConnectTimeoutInterval(connectTimeoutSeconds)
378
+ let request = mutableRequest as URLRequest
379
+
380
+ do {
381
+ let (data, response) = try await Self.urlSession.data(for: request)
382
+ guard let httpResponse = response as? HTTPURLResponse else {
383
+ let errorMsg = "Invalid HTTP response type"
384
+ onLog?("error", errorMsg)
385
+ throw SniConnectError.invalidConfig(errorMsg)
386
+ }
387
+
388
+ let status = httpResponse.statusCode
389
+ let parsedData = Self.parseResponseData(data)
390
+ let (headers, multiValueHeaders) = Self.extractHeaders(from: httpResponse)
391
+ let statusText = HTTPURLResponse.localizedString(forStatusCode: status)
392
+
393
+ // Check for HTTP errors (4xx, 5xx)
394
+ if status >= 400 {
395
+ let error = SniConnectError.httpError(code: status, message: statusText)
396
+ onLog?("error", "HTTP error: \(error.message)")
397
+ // Still return the response for client to handle
398
+ }
399
+
400
+ return Response(
401
+ data: parsedData,
402
+ status: status,
403
+ statusText: statusText,
404
+ headers: headers,
405
+ multiValueHeaders: multiValueHeaders
406
+ )
407
+ } catch let error as SniConnectError {
408
+ onLog?("error", "[\(error.code)] \(error.message)")
409
+ throw error
410
+ } catch {
411
+ // Convert generic errors to specific SniConnectError types
412
+ let sniError = SniConnectError.from(error)
413
+ onLog?("error", "[\(sniError.code)] \(sniError.message)")
414
+ throw sniError
415
+ }
416
+ }
417
+
418
+ private static func buildURL(hostname: String, path: String) throws -> URL {
419
+ let trimmedPath = path.trimmingCharacters(in: .whitespacesAndNewlines)
420
+
421
+ if let url = URL(string: trimmedPath), url.scheme != nil {
422
+ return url
423
+ }
424
+
425
+ let normalizedPath: String
426
+ if trimmedPath.isEmpty {
427
+ normalizedPath = "/"
428
+ } else if trimmedPath.hasPrefix("/") {
429
+ normalizedPath = trimmedPath
430
+ } else {
431
+ normalizedPath = "/" + trimmedPath
432
+ }
433
+
434
+ guard let url = URL(string: "https://\(hostname)\(normalizedPath)") else {
435
+ throw SniConnectError.invalidURL
436
+ }
437
+ return url
438
+ }
439
+
440
+ private static func parseResponseData(_ data: Data) -> Any {
441
+ guard !data.isEmpty else {
442
+ return [:]
443
+ }
444
+
445
+ if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) {
446
+ return jsonObject
447
+ }
448
+
449
+ if let text = String(data: data, encoding: .utf8) {
450
+ return text
451
+ }
452
+
453
+ return data.base64EncodedString()
454
+ }
455
+
456
+ /// Extract headers from HTTP response
457
+ /// Returns both single-value headers (for backward compatibility) and multi-value headers
458
+ private static func extractHeaders(from response: HTTPURLResponse) -> ([String: String], [String: [String]]) {
459
+ var singleValueHeaders: [String: String] = [:]
460
+ var multiValueHeaders: [String: [String]] = [:]
461
+
462
+ // Group headers by normalized key (lowercase)
463
+ var headerGroups: [String: [String]] = [:]
464
+
465
+ for (key, value) in response.allHeaderFields {
466
+ let headerKey = String(describing: key).lowercased()
467
+ let headerValue = String(describing: value)
468
+
469
+ if headerGroups[headerKey] == nil {
470
+ headerGroups[headerKey] = []
471
+ }
472
+ headerGroups[headerKey]?.append(headerValue)
473
+ }
474
+
475
+ // Process grouped headers
476
+ for (key, values) in headerGroups {
477
+ // For backward compatibility, single-value headers use the last value
478
+ singleValueHeaders[key] = values.last
479
+
480
+ // Multi-value headers contain all values
481
+ if values.count > 1 {
482
+ multiValueHeaders[key] = values
483
+ } else {
484
+ multiValueHeaders[key] = values
485
+ }
486
+ }
487
+
488
+ return (singleValueHeaders, multiValueHeaders)
489
+ }
490
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=react-native-codegen.d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sourceRoot":"../../../src","sources":["@types/react-native-codegen.d.ts"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ import { NativeModules, Platform, TurboModuleRegistry } from 'react-native';
4
+ const LINKING_ERROR = `The package '@onekeyfe/react-native-sni-connect' doesn't seem to be linked. Make sure:\n\n` + Platform.select({
5
+ ios: "- You have run 'pod install'\n",
6
+ default: ''
7
+ }) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go; create a custom dev client instead\n';
8
+ const turboModuleResult = TurboModuleRegistry.get('SniConnect');
9
+ const turboModule = turboModuleResult ?? null;
10
+ const bridgeModule = NativeModules.SniConnect ?? null;
11
+ const nativeModule = turboModule ?? bridgeModule;
12
+ if (nativeModule == null) {
13
+ throw new Error(LINKING_ERROR);
14
+ }
15
+ const NativeSniConnect = nativeModule;
16
+ export default NativeSniConnect;
17
+ //# sourceMappingURL=NativeSniConnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","Platform","TurboModuleRegistry","LINKING_ERROR","select","ios","default","turboModuleResult","get","turboModule","bridgeModule","SniConnect","nativeModule","Error","NativeSniConnect"],"sourceRoot":"../../src","sources":["NativeSniConnect.ts"],"mappings":";;AAAA,SACEA,aAAa,EACbC,QAAQ,EACRC,mBAAmB,QAEd,cAAc;AAoCrB,MAAMC,aAAa,GACjB,4FAA4F,GAC5FF,QAAQ,CAACG,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,mEAAmE;AASrE,MAAMC,iBAAiB,GAAGL,mBAAmB,CAACM,GAAG,CAAO,YAAY,CAAC;AAErE,MAAMC,WAAwB,GAAGF,iBAAiB,IAAI,IAAI;AAE1D,MAAMG,YAAyB,GAC5BV,aAAa,CAACW,UAAU,IAAyB,IAAI;AAExD,MAAMC,YAAY,GAAIH,WAAW,IAAIC,YAAwC;AAE7E,IAAIE,YAAY,IAAI,IAAI,EAAE;EACxB,MAAM,IAAIC,KAAK,CAACV,aAAa,CAAC;AAChC;AAEA,MAAMW,gBAAkC,GAAGF,YAAY;AAEvD,eAAeE,gBAAgB","ignoreList":[]}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ import { NativeEventEmitter } from 'react-native';
4
+ import NativeSniConnect from "./NativeSniConnect.js";
5
+
6
+ // Log entry type definition
7
+
8
+ // Create event emitter instance
9
+ const eventEmitter = new NativeEventEmitter(NativeSniConnect);
10
+
11
+ // Simple log subscription function
12
+ export function subscribeToLogs(callback) {
13
+ const subscription = eventEmitter.addListener('SniConnectLog', log => {
14
+ // Type assertion since we know the structure
15
+ callback(log);
16
+ });
17
+ return () => subscription.remove();
18
+ }
19
+ export function request(config) {
20
+ return NativeSniConnect.request(config);
21
+ }
22
+ export function cancelRequest(requestId) {
23
+ return NativeSniConnect.cancelRequest(requestId);
24
+ }
25
+ export function cancelAllRequests() {
26
+ return NativeSniConnect.cancelAllRequests();
27
+ }
28
+ export function clearDNSCache() {
29
+ return NativeSniConnect.clearDNSCache();
30
+ }
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeEventEmitter","NativeSniConnect","eventEmitter","subscribeToLogs","callback","subscription","addListener","log","remove","request","config","cancelRequest","requestId","cancelAllRequests","clearDNSCache"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,kBAAkB,QAAQ,cAAc;AACjD,OAAOC,gBAAgB,MAGhB,uBAAoB;;AAE3B;;AAOA;AACA,MAAMC,YAAY,GAAG,IAAIF,kBAAkB,CAACC,gBAAuB,CAAC;;AAEpE;AACA,OAAO,SAASE,eAAeA,CAACC,QAAiC,EAAc;EAC7E,MAAMC,YAAY,GAAGH,YAAY,CAACI,WAAW,CAAC,eAAe,EAAGC,GAAQ,IAAK;IAC3E;IACAH,QAAQ,CAACG,GAAe,CAAC;EAC3B,CAAC,CAAC;EACF,OAAO,MAAMF,YAAY,CAACG,MAAM,CAAC,CAAC;AACpC;AAEA,OAAO,SAASC,OAAOA,CACrBC,MAAyB,EACI;EAC7B,OAAOT,gBAAgB,CAACQ,OAAO,CAACC,MAAM,CAAC;AACzC;AAEA,OAAO,SAASC,aAAaA,CAC3BC,SAAiB,EACc;EAC/B,OAAOX,gBAAgB,CAACU,aAAa,CAACC,SAAS,CAAC;AAClD;AAEA,OAAO,SAASC,iBAAiBA,CAAA,EAAkC;EACjE,OAAOZ,gBAAgB,CAACY,iBAAiB,CAAC,CAAC;AAC7C;AAEA,OAAO,SAASC,aAAaA,CAAA,EAAkC;EAC7D,OAAOb,gBAAgB,CAACa,aAAa,CAAC,CAAC;AACzC","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,50 @@
1
+ import { type TurboModule } from 'react-native';
2
+ import type { Double, Int32 } from 'react-native/Libraries/Types/CodegenTypes';
3
+ type HeaderMap = {
4
+ [key: string]: string;
5
+ };
6
+ export type SniConnectRequest = {
7
+ requestId?: string;
8
+ ip: string;
9
+ hostname: string;
10
+ method: string;
11
+ path: string;
12
+ headers: HeaderMap;
13
+ body?: string | null;
14
+ timeout: Double;
15
+ };
16
+ export type SniConnectResponse = {
17
+ data: string;
18
+ status: Int32;
19
+ statusText: string;
20
+ headers: HeaderMap;
21
+ };
22
+ export interface Spec extends TurboModule {
23
+ request(config: SniConnectRequest): Promise<SniConnectResponse>;
24
+ cancelRequest(requestId: string): Promise<{
25
+ success: boolean;
26
+ }>;
27
+ cancelAllRequests(): Promise<{
28
+ success: boolean;
29
+ }>;
30
+ clearDNSCache(): Promise<{
31
+ success: boolean;
32
+ }>;
33
+ addListener(eventType: string): void;
34
+ removeListeners(count: Int32): void;
35
+ }
36
+ export type SniConnectModule = Spec & {
37
+ request(config: SniConnectRequest): Promise<SniConnectResponse>;
38
+ cancelRequest(requestId: string): Promise<{
39
+ success: boolean;
40
+ }>;
41
+ cancelAllRequests(): Promise<{
42
+ success: boolean;
43
+ }>;
44
+ clearDNSCache(): Promise<{
45
+ success: boolean;
46
+ }>;
47
+ };
48
+ declare const NativeSniConnect: SniConnectModule;
49
+ export default NativeSniConnect;
50
+ //# sourceMappingURL=NativeSniConnect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeSniConnect.d.ts","sourceRoot":"","sources":["../../../src/NativeSniConnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAE/E,KAAK,SAAS,GAAG;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChE,iBAAiB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACnD,aAAa,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAG/C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrC;AAQD,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG;IACpC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChE,iBAAiB,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACnD,aAAa,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAChD,CAAC;AAeF,QAAA,MAAM,gBAAgB,EAAE,gBAA+B,CAAC;AAExD,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type SniConnectRequest, type SniConnectResponse } from './NativeSniConnect';
2
+ export type LogEntry = {
3
+ level: string;
4
+ message: string;
5
+ timestamp: number;
6
+ };
7
+ export declare function subscribeToLogs(callback: (log: LogEntry) => void): () => void;
8
+ export declare function request(config: SniConnectRequest): Promise<SniConnectResponse>;
9
+ export declare function cancelRequest(requestId: string): Promise<{
10
+ success: boolean;
11
+ }>;
12
+ export declare function cancelAllRequests(): Promise<{
13
+ success: boolean;
14
+ }>;
15
+ export declare function clearDNSCache(): Promise<{
16
+ success: boolean;
17
+ }>;
18
+ export type { SniConnectRequest, SniConnectResponse } from './NativeSniConnect';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAyB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAC;AAG5B,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAM7E;AAED,wBAAgB,OAAO,CACrB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAE7B;AAED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAE/B;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAEjE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAE7D;AAED,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}