react-native-kookit 0.2.3 → 0.2.6

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.
@@ -311,32 +311,53 @@ class FtpClient: @unchecked Sendable {
311
311
  func downloadFile(remotePath: String, localPath: String) async throws {
312
312
  guard isConnected else { throw FtpError.notConnected }
313
313
 
314
+ print("FTP DEBUG: Starting download - Remote: \(remotePath), Local: \(localPath)")
315
+
314
316
  let dataConnection = try await enterPassiveMode()
317
+ print("FTP DEBUG: Passive mode established for download")
315
318
 
316
319
  let response = try await sendCommand("RETR \(remotePath)")
317
- guard response.hasPrefix("150") || response.hasPrefix("125") else {
320
+ print("FTP DEBUG: RETR command response: \(response)")
321
+
322
+ // Extract the first line for status check
323
+ let firstLine = response.components(separatedBy: .newlines).first ?? response
324
+ guard firstLine.hasPrefix("150") || firstLine.hasPrefix("125") else {
318
325
  dataConnection.cancel()
319
- throw FtpError.commandFailed("Download failed: \(response)")
326
+ throw FtpError.commandFailed("Download failed: \(firstLine)")
320
327
  }
321
328
 
322
329
  let localURL = URL(fileURLWithPath: localPath)
323
330
 
324
331
  // Create parent directories if needed
332
+ print("FTP DEBUG: Creating parent directories for: \(localURL.deletingLastPathComponent().path)")
325
333
  try FileManager.default.createDirectory(at: localURL.deletingLastPathComponent(),
326
334
  withIntermediateDirectories: true,
327
335
  attributes: nil)
328
336
 
337
+ print("FTP DEBUG: Starting data reception...")
329
338
  let data = try await receiveData(from: dataConnection, reportProgress: true)
339
+ print("FTP DEBUG: Data reception completed, received \(data.count) bytes")
330
340
  dataConnection.cancel()
331
341
 
342
+ print("FTP DEBUG: Writing data to file...")
332
343
  try data.write(to: localURL)
344
+ print("FTP DEBUG: File written successfully")
333
345
 
334
- let finalResponse = try await readResponse()
335
- guard finalResponse.hasPrefix("226") else {
336
- try? FileManager.default.removeItem(at: localURL)
337
- throw FtpError.commandFailed("Download completion failed: \(finalResponse)")
346
+ // Check if the response already contained the completion message (226)
347
+ if response.contains("226") {
348
+ print("FTP DEBUG: Transfer completion already received in RETR response - skipping final response read")
349
+ } else {
350
+ print("FTP DEBUG: Reading final response...")
351
+ let finalResponse = try await readResponse()
352
+ let finalFirstLine = finalResponse.components(separatedBy: .newlines).first ?? finalResponse
353
+ print("FTP DEBUG: Final download response: \(finalFirstLine)")
354
+ guard finalFirstLine.hasPrefix("226") else {
355
+ try? FileManager.default.removeItem(at: localURL)
356
+ throw FtpError.commandFailed("Download completion failed: \(finalFirstLine)")
357
+ }
338
358
  }
339
359
 
360
+ print("FTP DEBUG: Download completed successfully")
340
361
  progressDelegate?.onComplete()
341
362
  }
342
363
 
@@ -454,6 +475,7 @@ class FtpClient: @unchecked Sendable {
454
475
  throw FtpError.notConnected
455
476
  }
456
477
 
478
+ print("FTP DEBUG: Sending command: \(command)")
457
479
  let commandData = "\(command)\r\n".data(using: .utf8)!
458
480
 
459
481
  return try await withCheckedThrowingContinuation { continuation in
@@ -461,13 +483,24 @@ class FtpClient: @unchecked Sendable {
461
483
 
462
484
  connection.send(content: commandData, completion: .contentProcessed { error in
463
485
  if let error = error {
486
+ print("FTP DEBUG: Send command error: \(error)")
464
487
  connectionBox.resume(throwing: error)
465
488
  } else {
489
+ print("FTP DEBUG: Command sent successfully, waiting for response...")
466
490
  Task {
467
491
  do {
468
492
  let response = try await self.readResponse()
469
- connectionBox.resume(returning: response)
493
+ print("FTP DEBUG: Command response received: \(response)")
494
+
495
+ // Store the full response for multi-line responses
496
+ if response.contains("\n") {
497
+ print("FTP DEBUG: Multi-line response detected")
498
+ connectionBox.resume(returning: response)
499
+ } else {
500
+ connectionBox.resume(returning: response)
501
+ }
470
502
  } catch {
503
+ print("FTP DEBUG: Error reading response: \(error)")
471
504
  connectionBox.resume(throwing: error)
472
505
  }
473
506
  }
@@ -481,35 +514,47 @@ class FtpClient: @unchecked Sendable {
481
514
  throw FtpError.notConnected
482
515
  }
483
516
 
517
+ print("FTP DEBUG: Reading response...")
484
518
  return try await withCheckedThrowingContinuation { continuation in
485
519
  let connectionBox = ConnectionBox<String>(continuation: continuation)
486
520
 
487
521
  connection.receive(minimumIncompleteLength: 1, maximumLength: 4096) { data, _, isComplete, error in
488
522
  if let error = error {
523
+ print("FTP DEBUG: Read response error: \(error)")
489
524
  connectionBox.resume(throwing: error)
490
525
  } else if let data = data {
526
+ print("FTP DEBUG: Received response data: \(data.count) bytes")
491
527
  // Try multiple encodings for FTP responses
492
528
  var response: String?
493
529
 
494
530
  // Try UTF-8 first
495
531
  if let utf8Response = String(data: data, encoding: .utf8) {
496
532
  response = utf8Response
533
+ print("FTP DEBUG: Response decoded with UTF-8")
497
534
  }
498
535
  // Try GBK for Chinese FTP servers
499
536
  else if let gbkResponse = String(data: data, encoding: String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue)))) {
500
537
  response = gbkResponse
538
+ print("FTP DEBUG: Response decoded with GBK")
501
539
  }
502
540
  // Try ASCII as fallback
503
541
  else if let asciiResponse = String(data: data, encoding: .ascii) {
504
542
  response = asciiResponse
543
+ print("FTP DEBUG: Response decoded with ASCII")
505
544
  }
506
545
 
507
546
  if let validResponse = response {
508
- connectionBox.resume(returning: validResponse.trimmingCharacters(in: .whitespacesAndNewlines))
547
+ let trimmedResponse = validResponse.trimmingCharacters(in: .whitespacesAndNewlines)
548
+ print("FTP DEBUG: Response content: '\(trimmedResponse)'")
549
+
550
+ // Return the full response for multi-line processing
551
+ connectionBox.resume(returning: trimmedResponse)
509
552
  } else {
553
+ print("FTP DEBUG: Failed to decode response")
510
554
  connectionBox.resume(throwing: FtpError.invalidResponse)
511
555
  }
512
556
  } else {
557
+ print("FTP DEBUG: No data received")
513
558
  connectionBox.resume(throwing: FtpError.invalidResponse)
514
559
  }
515
560
  }
@@ -517,7 +562,9 @@ class FtpClient: @unchecked Sendable {
517
562
  }
518
563
 
519
564
  private func enterPassiveMode() async throws -> NWConnection {
565
+ print("FTP DEBUG: Entering passive mode...")
520
566
  let response = try await sendCommand("PASV")
567
+ print("FTP DEBUG: PASV response: \(response)")
521
568
  guard response.hasPrefix("227") else {
522
569
  throw FtpError.commandFailed("Failed to enter passive mode: \(response)")
523
570
  }
@@ -541,6 +588,8 @@ class FtpClient: @unchecked Sendable {
541
588
  let host = "\(h1).\(h2).\(h3).\(h4)"
542
589
  let port = p1 * 256 + p2
543
590
 
591
+ print("FTP DEBUG: Parsed passive mode - Host: \(host), Port: \(port)")
592
+
544
593
  let dataHost = NWEndpoint.Host(host)
545
594
  let dataPort = NWEndpoint.Port(integerLiteral: UInt16(port))
546
595
  let dataEndpoint = NWEndpoint.hostPort(host: dataHost, port: dataPort)
@@ -551,15 +600,18 @@ class FtpClient: @unchecked Sendable {
551
600
  let connectionBox = NWConnectionBox(connection: dataConnection, continuation: continuation)
552
601
 
553
602
  dataConnection.stateUpdateHandler = { state in
603
+ print("FTP DEBUG: Data connection state: \(state)")
554
604
  connectionBox.handleStateUpdate(state)
555
605
  }
556
606
 
607
+ print("FTP DEBUG: Starting data connection...")
557
608
  dataConnection.start(queue: .global())
558
609
  }
559
610
  }
560
611
 
561
612
  private func receiveData(from connection: NWConnection, reportProgress: Bool = false) async throws -> Data {
562
613
  var receivedData = Data()
614
+ print("FTP DEBUG: Starting data reception, reportProgress: \(reportProgress)")
563
615
 
564
616
  return try await withCheckedThrowingContinuation { continuation in
565
617
  let connectionBox = ConnectionBox<Data>(continuation: continuation)
@@ -567,12 +619,14 @@ class FtpClient: @unchecked Sendable {
567
619
  func receiveMore() {
568
620
  connection.receive(minimumIncompleteLength: 1, maximumLength: 8192) { data, _, isComplete, error in
569
621
  if let error = error {
622
+ print("FTP DEBUG: receiveData error: \(error)")
570
623
  connectionBox.resume(throwing: error)
571
624
  return
572
625
  }
573
626
 
574
627
  if let data = data {
575
628
  receivedData.append(data)
629
+ print("FTP DEBUG: Received chunk of \(data.count) bytes, total: \(receivedData.count) bytes")
576
630
  if reportProgress {
577
631
  DispatchQueue.main.async {
578
632
  self.progressDelegate?.onProgress(transferred: Int64(receivedData.count), total: -1)
@@ -581,8 +635,10 @@ class FtpClient: @unchecked Sendable {
581
635
  }
582
636
 
583
637
  if isComplete {
638
+ print("FTP DEBUG: Data reception complete, total bytes: \(receivedData.count)")
584
639
  connectionBox.resume(returning: receivedData)
585
640
  } else {
641
+ print("FTP DEBUG: Receiving more data...")
586
642
  receiveMore()
587
643
  }
588
644
  }
@@ -747,7 +803,7 @@ public class ReactNativeKookitModule: Module {
747
803
  private var volumeObserver: NSKeyValueObservation?
748
804
  private var isVolumeKeyInterceptionEnabled = false
749
805
  private var previousVolume: Float = 0.0
750
- private var ftpClient: FtpClient?
806
+ private var ftpClients: [String: FtpClient] = [:] // Store multiple FTP clients by ID
751
807
 
752
808
  // Each module class must implement the definition function. The definition consists of components
753
809
  // that describes the module's functionality and behavior.
@@ -790,8 +846,31 @@ public class ReactNativeKookitModule: Module {
790
846
  self.disableVolumeKeyInterception()
791
847
  }
792
848
 
793
- // FTP Functions
794
- AsyncFunction("ftpConnect") { (config: [String: Any], promise: Promise) in
849
+ // New FTP Client API - Create new FTP client instance
850
+ AsyncFunction("createFtpClient") { (clientId: String, promise: Promise) in
851
+ if self.ftpClients[clientId] != nil {
852
+ promise.reject("FTP_CLIENT_EXISTS", "FTP client with ID '\(clientId)' already exists")
853
+ return
854
+ }
855
+
856
+ let ftpClient = FtpClient()
857
+ let progressDelegate = FtpProgressDelegateImpl(module: self, clientId: clientId)
858
+ ftpClient.setProgressDelegate(progressDelegate)
859
+ self.ftpClients[clientId] = ftpClient
860
+
861
+ promise.resolve([
862
+ "clientId": clientId,
863
+ "created": true
864
+ ])
865
+ }
866
+
867
+ // Connect FTP client
868
+ AsyncFunction("ftpClientConnect") { (clientId: String, config: [String: Any], promise: Promise) in
869
+ guard let ftpClient = self.ftpClients[clientId] else {
870
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
871
+ return
872
+ }
873
+
795
874
  Task {
796
875
  do {
797
876
  let ftpConfig = FtpConnectionConfig(
@@ -803,30 +882,60 @@ public class ReactNativeKookitModule: Module {
803
882
  timeout: config["timeout"] as? TimeInterval ?? 30.0
804
883
  )
805
884
 
806
- self.ftpClient = FtpClient()
807
- let progressDelegate = FtpProgressDelegateImpl(module: self)
808
- self.ftpClient?.setProgressDelegate(progressDelegate)
809
-
810
- try await self.ftpClient?.connect(config: ftpConfig)
811
- promise.resolve()
885
+ try await ftpClient.connect(config: ftpConfig)
886
+ promise.resolve([
887
+ "clientId": clientId,
888
+ "connected": true
889
+ ])
812
890
  } catch {
813
891
  promise.reject("FTP_CONNECT_ERROR", error.localizedDescription)
814
892
  }
815
893
  }
816
894
  }
817
895
 
818
- AsyncFunction("ftpDisconnect") { (promise: Promise) in
896
+ // Disconnect FTP client
897
+ AsyncFunction("ftpClientDisconnect") { (clientId: String, promise: Promise) in
898
+ guard let ftpClient = self.ftpClients[clientId] else {
899
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
900
+ return
901
+ }
902
+
903
+ Task {
904
+ await ftpClient.disconnect()
905
+ promise.resolve([
906
+ "clientId": clientId,
907
+ "disconnected": true
908
+ ])
909
+ }
910
+ }
911
+
912
+ // Dispose FTP client
913
+ AsyncFunction("disposeFtpClient") { (clientId: String, promise: Promise) in
914
+ guard let ftpClient = self.ftpClients[clientId] else {
915
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
916
+ return
917
+ }
918
+
819
919
  Task {
820
- await self.ftpClient?.disconnect()
821
- self.ftpClient = nil
822
- promise.resolve()
920
+ await ftpClient.disconnect()
921
+ self.ftpClients.removeValue(forKey: clientId)
922
+ promise.resolve([
923
+ "clientId": clientId,
924
+ "disposed": true
925
+ ])
823
926
  }
824
927
  }
825
928
 
826
- AsyncFunction("ftpList") { (path: String?, promise: Promise) in
929
+ // List files
930
+ AsyncFunction("ftpClientList") { (clientId: String, path: String?, promise: Promise) in
931
+ guard let ftpClient = self.ftpClients[clientId] else {
932
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
933
+ return
934
+ }
935
+
827
936
  Task {
828
937
  do {
829
- let files = try await self.ftpClient?.listFiles(path: path) ?? []
938
+ let files = try await ftpClient.listFiles(path: path)
830
939
  let result = files.map { file in
831
940
  [
832
941
  "name": file.name,
@@ -843,72 +952,163 @@ public class ReactNativeKookitModule: Module {
843
952
  }
844
953
  }
845
954
 
846
- AsyncFunction("ftpDownload") { (remotePath: String, localPath: String, promise: Promise) in
955
+ // Download file
956
+ AsyncFunction("ftpClientDownload") { (clientId: String, remotePath: String, localPath: String, promise: Promise) in
957
+ guard let ftpClient = self.ftpClients[clientId] else {
958
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
959
+ return
960
+ }
961
+
847
962
  Task {
848
963
  do {
849
- try await self.ftpClient?.downloadFile(remotePath: remotePath, localPath: localPath)
850
- promise.resolve()
964
+ try await ftpClient.downloadFile(remotePath: remotePath, localPath: localPath)
965
+ promise.resolve([
966
+ "clientId": clientId,
967
+ "remotePath": remotePath,
968
+ "localPath": localPath,
969
+ "downloaded": true
970
+ ])
851
971
  } catch {
852
972
  promise.reject("FTP_DOWNLOAD_ERROR", error.localizedDescription)
853
973
  }
854
974
  }
855
975
  }
856
976
 
857
- AsyncFunction("ftpUpload") { (localPath: String, remotePath: String, promise: Promise) in
977
+ // Upload file
978
+ AsyncFunction("ftpClientUpload") { (clientId: String, localPath: String, remotePath: String, promise: Promise) in
979
+ guard let ftpClient = self.ftpClients[clientId] else {
980
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
981
+ return
982
+ }
983
+
858
984
  Task {
859
985
  do {
860
- try await self.ftpClient?.uploadFile(localPath: localPath, remotePath: remotePath)
861
- promise.resolve()
986
+ try await ftpClient.uploadFile(localPath: localPath, remotePath: remotePath)
987
+ promise.resolve([
988
+ "clientId": clientId,
989
+ "localPath": localPath,
990
+ "remotePath": remotePath,
991
+ "uploaded": true
992
+ ])
862
993
  } catch {
863
994
  promise.reject("FTP_UPLOAD_ERROR", error.localizedDescription)
864
995
  }
865
996
  }
866
997
  }
867
998
 
868
- AsyncFunction("ftpDelete") { (remotePath: String, isDirectory: Bool?, promise: Promise) in
999
+ // Delete file
1000
+ AsyncFunction("ftpClientDelete") { (clientId: String, remotePath: String, isDirectory: Bool?, promise: Promise) in
1001
+ guard let ftpClient = self.ftpClients[clientId] else {
1002
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
1003
+ return
1004
+ }
1005
+
869
1006
  Task {
870
1007
  do {
871
- try await self.ftpClient?.deleteFile(remotePath: remotePath, isDirectory: isDirectory ?? false)
872
- promise.resolve()
1008
+ try await ftpClient.deleteFile(remotePath: remotePath, isDirectory: isDirectory ?? false)
1009
+ promise.resolve([
1010
+ "clientId": clientId,
1011
+ "remotePath": remotePath,
1012
+ "deleted": true
1013
+ ])
873
1014
  } catch {
874
1015
  promise.reject("FTP_DELETE_ERROR", error.localizedDescription)
875
1016
  }
876
1017
  }
877
1018
  }
878
1019
 
879
- AsyncFunction("ftpCreateDirectory") { (remotePath: String, promise: Promise) in
1020
+ // Create directory
1021
+ AsyncFunction("ftpClientCreateDirectory") { (clientId: String, remotePath: String, promise: Promise) in
1022
+ guard let ftpClient = self.ftpClients[clientId] else {
1023
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
1024
+ return
1025
+ }
1026
+
880
1027
  Task {
881
1028
  do {
882
- try await self.ftpClient?.createDirectory(remotePath: remotePath)
883
- promise.resolve()
1029
+ try await ftpClient.createDirectory(remotePath: remotePath)
1030
+ promise.resolve([
1031
+ "clientId": clientId,
1032
+ "remotePath": remotePath,
1033
+ "created": true
1034
+ ])
884
1035
  } catch {
885
1036
  promise.reject("FTP_CREATE_DIR_ERROR", error.localizedDescription)
886
1037
  }
887
1038
  }
888
1039
  }
889
1040
 
890
- AsyncFunction("ftpChangeDirectory") { (remotePath: String, promise: Promise) in
1041
+ // Change directory
1042
+ AsyncFunction("ftpClientChangeDirectory") { (clientId: String, remotePath: String, promise: Promise) in
1043
+ guard let ftpClient = self.ftpClients[clientId] else {
1044
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
1045
+ return
1046
+ }
1047
+
891
1048
  Task {
892
1049
  do {
893
- try await self.ftpClient?.changeDirectory(remotePath: remotePath)
894
- promise.resolve()
1050
+ try await ftpClient.changeDirectory(remotePath: remotePath)
1051
+ promise.resolve([
1052
+ "clientId": clientId,
1053
+ "currentDirectory": remotePath,
1054
+ "changed": true
1055
+ ])
895
1056
  } catch {
896
1057
  promise.reject("FTP_CHANGE_DIR_ERROR", error.localizedDescription)
897
1058
  }
898
1059
  }
899
1060
  }
900
1061
 
901
- AsyncFunction("ftpGetCurrentDirectory") { (promise: Promise) in
1062
+ // Get current directory
1063
+ AsyncFunction("ftpClientGetCurrentDirectory") { (clientId: String, promise: Promise) in
1064
+ guard let ftpClient = self.ftpClients[clientId] else {
1065
+ promise.reject("FTP_CLIENT_NOT_FOUND", "FTP client with ID '\(clientId)' not found")
1066
+ return
1067
+ }
1068
+
902
1069
  Task {
903
1070
  do {
904
- let currentDir = try await self.ftpClient?.getCurrentDirectory() ?? "/"
905
- promise.resolve(currentDir)
1071
+ let currentDir = try await ftpClient.getCurrentDirectory()
1072
+ promise.resolve([
1073
+ "clientId": clientId,
1074
+ "currentDirectory": currentDir
1075
+ ])
906
1076
  } catch {
907
1077
  promise.reject("FTP_PWD_ERROR", error.localizedDescription)
908
1078
  }
909
1079
  }
910
1080
  }
911
1081
 
1082
+ // Get FTP client status
1083
+ AsyncFunction("getFtpClientStatus") { (clientId: String, promise: Promise) in
1084
+ guard let ftpClient = self.ftpClients[clientId] else {
1085
+ promise.resolve([
1086
+ "exists": false,
1087
+ "connected": false
1088
+ ])
1089
+ return
1090
+ }
1091
+
1092
+ promise.resolve([
1093
+ "exists": true,
1094
+ "connected": ftpClient.isConnected
1095
+ ])
1096
+ }
1097
+
1098
+ // List all FTP clients
1099
+ AsyncFunction("listFtpClients") { (promise: Promise) in
1100
+ let clientsInfo = self.ftpClients.mapValues { client in
1101
+ [
1102
+ "connected": client.isConnected
1103
+ ]
1104
+ }
1105
+
1106
+ promise.resolve([
1107
+ "clients": clientsInfo,
1108
+ "count": self.ftpClients.count
1109
+ ])
1110
+ }
1111
+
912
1112
  // Enables the module to be used as a native view. Definition components that are accepted as part of
913
1113
  // the view definition: Prop, Events.
914
1114
  View(ReactNativeKookitView.self) {
@@ -1007,14 +1207,17 @@ public class ReactNativeKookitModule: Module {
1007
1207
  // Helper class to handle FTP progress callbacks
1008
1208
  private class FtpProgressDelegateImpl: FtpProgressDelegate {
1009
1209
  weak var module: ReactNativeKookitModule?
1210
+ let clientId: String
1010
1211
 
1011
- init(module: ReactNativeKookitModule) {
1212
+ init(module: ReactNativeKookitModule, clientId: String) {
1012
1213
  self.module = module
1214
+ self.clientId = clientId
1013
1215
  }
1014
1216
 
1015
1217
  func onProgress(transferred: Int64, total: Int64) {
1016
1218
  let percentage = total > 0 ? Int((transferred * 100) / total) : 0
1017
1219
  module?.sendEvent("onFtpProgress", [
1220
+ "clientId": clientId,
1018
1221
  "transferred": transferred,
1019
1222
  "total": total,
1020
1223
  "percentage": percentage
@@ -1022,10 +1225,15 @@ private class FtpProgressDelegateImpl: FtpProgressDelegate {
1022
1225
  }
1023
1226
 
1024
1227
  func onComplete() {
1025
- module?.sendEvent("onFtpComplete")
1228
+ module?.sendEvent("onFtpComplete", [
1229
+ "clientId": clientId
1230
+ ])
1026
1231
  }
1027
1232
 
1028
1233
  func onError(error: String) {
1029
- module?.sendEvent("onFtpError", ["error": error])
1234
+ module?.sendEvent("onFtpError", [
1235
+ "clientId": clientId,
1236
+ "error": error
1237
+ ])
1030
1238
  }
1031
1239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-kookit",
3
- "version": "0.2.3",
3
+ "version": "0.2.6",
4
4
  "description": "React Native module for intercepting volume button presses on iOS and Android, with FTP client functionality",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",