diff --git a/android/app/build.gradle b/android/app/build.gradle index 244499d0a..6749ec484 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -87,7 +87,7 @@ android { applicationId "io.hexawallet.keeper" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 557 + versionCode 558 versionName "2.5.2" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' diff --git a/android/build.gradle b/android/build.gradle index 700fd48f4..ff3ba99df 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,7 +3,7 @@ buildscript { ext { buildToolsVersion = "35.0.0" - minSdkVersion = 26 + minSdkVersion = 28 compileSdkVersion = 35 targetSdkVersion = 35 kotlin_version = '1.9.22' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 22a0feee6..422df68bd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -83,9 +83,9 @@ PODS: - libwebp/sharpyuv (1.5.0) - libwebp/webp (1.5.0): - libwebp/sharpyuv - - MMKV (2.1.0): - - MMKVCore (~> 2.1.0) - - MMKVCore (2.1.1) + - MMKV (2.0.0): + - MMKVCore (~> 2.0.0) + - MMKVCore (2.0.0) - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) - nanopb/encode (= 2.30908.0) @@ -1026,6 +1026,27 @@ PODS: - React-debug - react-native-background-timer (2.4.1): - React-Core + - "react-native-bare-kit (0.5.6+dea2b7)": + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-biometrics (2.2.0): - React-Core - react-native-blob-util (0.18.3): @@ -1093,8 +1114,6 @@ PODS: - react-native-tcp-socket (5.6.2): - CocoaAsyncSocket - React-Core - - react-native-tor (0.1.8): - - React - React-nativeconfig (0.74.7) - React-NativeModulesApple (0.74.7): - glog @@ -1534,6 +1553,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - react-native-background-timer (from `../node_modules/react-native-background-timer`) + - react-native-bare-kit (from `../node_modules/react-native-bare-kit`) - react-native-biometrics (from `../node_modules/react-native-biometrics`) - react-native-blob-util (from `../node_modules/react-native-blob-util`) - react-native-camera (from `../node_modules/react-native-camera`) @@ -1553,7 +1573,6 @@ DEPENDENCIES: - react-native-rsa-native (from `../node_modules/react-native-rsa-native`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`) - - react-native-tor (from `../node_modules/react-native-tor`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1689,6 +1708,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" react-native-background-timer: :path: "../node_modules/react-native-background-timer" + react-native-bare-kit: + :path: "../node_modules/react-native-bare-kit" react-native-biometrics: :path: "../node_modules/react-native-biometrics" react-native-blob-util: @@ -1727,8 +1748,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-tcp-socket: :path: "../node_modules/react-native-tcp-socket" - react-native-tor: - :path: "../node_modules/react-native-tor" React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1817,7 +1836,7 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - BVLinearGradient: cb006ba232a1f3e4f341bb62c42d1098c284da70 + BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 FBLazyVector: 04dc972982abebd96d823752c3a426bbe6ac397f @@ -1832,105 +1851,105 @@ SPEC CHECKSUMS: GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 hermes-engine: 21ea4e6a0b64854652c8c20cb815efdbb3131fdd libportal-ios: d9aa55474e2d5be8e38e96345dd37be34fda45b4 - libportal-react-native: 32660fafccb012ffc79916bbc1b6e601d919cc72 + libportal-react-native: f0db446632b3d3e3da5837112d609a71cb47da7d libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 - MMKV: ce484c1ac40bf76d5f09a0195d2ec5b3d3840d55 - MMKVCore: 1eb661c6c498ab88e3df9ce5d8ff94d05fcc0567 + MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801 + MMKVCore: c04b296010fcb1d1638f2c69405096aac12f6390 nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 PDFGenerator: 17d6bc525db0a3fcd156fbf00f9d1b8d5cc75d1e PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - QrCode: 2daa9df3b0c309db370888fb207714acdadc4aa4 + QrCode: 703276888a8e1e5a77606392f82ce758d895a450 QRCoder: cbd2bee531cc86d286df7942334cfed94c803ae4 - RCT-Folly: 5dc73daec3476616d19e8a53f0156176f7b55461 + RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: c4e6e3f6d44f76c45a964b40fa3eb2475259c027 RCTRequired: c4886806a178cd895cd4a17dae1642b72e7e8233 RCTTypeSafety: 4e9f36465ccbcca7b62f5cb8fc1ff2c997c3b92e React: 7f6ee889aa17245726efe5c0be52389e58d9d7c1 React-callinvoker: 0155d33f43924c206dfaa040c020d0404bbb54d6 - React-Codegen: 16b3c98135968de37085da126814187e6db73d7b - React-Core: 063d3e42f48c671822d16ade452b8d25793cc9fa - React-CoreModules: 46303f9f50e942fdd8763626464a8a24d9a36419 - React-cxxreact: 08e7fb41e693d0d18266c394d95501c719f81a7e + React-Codegen: 19dcd3c5d2418af62dcab56a09ff62accee8b60e + React-Core: da6746240394ea2bb828e6e93baed4dea3c27689 + React-CoreModules: 319cbc30aee816ea4716115b0f77035b95902a80 + React-cxxreact: b8e823d419880d779be49fc049732e8facf64fc7 React-debug: 81d2423e256c8f0dad94f368e7a5450b3885c5b5 - React-Fabric: 87f30b642973d16bbe84c0b898cebab6a26d8b65 - React-FabricImage: 1ebed5160efb85acf9c492dfff7d21ec04225369 + React-Fabric: a96f6898862c047023f140a787289ef4111e059d + React-FabricImage: 0cb85c8a9672cf3eddf340d96806b0d57e5a4d60 React-featureflags: c3e59ddabf0bc2b8e125aff4aab6943112f5d852 - React-graphics: cd4fdf0130e3ff941044cac69b7706608f3a7d08 - React-hermes: 1313182de2921855390a3cde52e2a407a345775f - React-ImageManager: bc485f4de8fb13ebb8663e1f969fc1f28d1209cc - React-jserrorhandler: 863c218d19b34470fc3fc99872fb5175fee47356 - React-jsi: ffc343198a6e03042a205db9e149f321d356f033 - React-jsiexecutor: 50079a521784bfb675522f1c93dd741200c6d8dc - React-jsinspector: f250539d29817196179ea349ba4d475d256777a6 - React-jsitracing: a884f4eb46ba8c373d909f9a712824af65ff41b8 - React-logger: 58cd5ca2c3ab03a06b33e6d46a210d2b17ca116d - React-Mapbuffer: f245095650540b8ddd09ac907a79605f68b1f4d4 - react-native-background-timer: 4638ae3bee00320753647900b21260b10587b6f7 - react-native-biometrics: ecca256a9e1c8b430f78c4e3dcb16b57ea1418e0 - react-native-blob-util: 4be49d669870f01645b63d33342718e95d0d48b1 - react-native-camera: 079d80421f0572d6b4e836908114d614d0adb553 - react-native-change-icon: 5833a440377a3df338d6d71b7610265e758597ca - react-native-config: 8e425892a531627c52db765be3088185cb871e19 - react-native-contacts: f551920b74ebfc5f7f6df307495f085e504a4369 - react-native-document-picker: a338165804b1a14c8e408448115dc0edfd7b73ca - react-native-get-random-values: 0fd2b6a3129988d701d10e30f0622d5f039531bc - react-native-hce: 164e785abe2648edac12934203f55dc785bb1c0b - react-native-html-to-pdf: 7a49e6c58ac5221bcc093027b195f4b214f27a9d - react-native-image-picker: 1f5318beec2ebed6695454ffb9bea8c4152f1598 - react-native-mmkv: 5a46c73e3e12aa872c4485ae0e4414b4040af79a - react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187 - react-native-nfc-manager: e5d2ce2f7fee42d1ee0eb6b99f3405d5e330112e - react-native-pdf: b0380c1ad48f30943665cd783c4c305e341c0822 - react-native-randombytes: 3c8f3e89d12487fd03a2f966c288d495415fc116 - react-native-rsa-native: a7931cdda1f73a8576a46d7f431378c5550f0c38 - react-native-safe-area-context: df9763c5de6fa38883028e243a0b60123acb8858 - react-native-tcp-socket: 0a0455b843f9dfcad875ea6cfafeb05c0abb0a24 - react-native-tor: dc46832ab810a21fb8954afa20b5aeb740ef93f5 + React-graphics: 4426a34fd9695e861d644fac89ab6ba5a42e110d + React-hermes: 010c8dc210afa547991e13fc5acee7e63cf9f2e8 + React-ImageManager: 4fc40ebeac12716ed8beeadc17785b799096cae9 + React-jserrorhandler: d2621ce6fee5bf965ba8968a5315f1c6ea6fd731 + React-jsi: df1b6ca5308a888cbdf44c5035ca2e46822f1902 + React-jsiexecutor: 88e8f50d2e0cdb1531c6d956c148577698311ac8 + React-jsinspector: cbd4d9bf7d0944f662af337727eec69b9eca1db6 + React-jsitracing: 549d1177ff45058b1300652db58add8c357a11ed + React-logger: 87cddd161bd9784d60fc5528f21c57ca07d2962a + React-Mapbuffer: b6e208217a18044f0f1a183be8f6756ad67b42d7 + react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe + react-native-bare-kit: e5778d30c237c784b138ffc119620e8efbe36f25 + react-native-biometrics: 3b95f2eb074d537d3a8b05d853c17778f6685c4a + react-native-blob-util: 2d36383bb52c15c5451be81cb7ddf22bc34a12a6 + react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f + react-native-change-icon: a7cfe810b8d7cc159cb32903bde8ae17a598df5c + react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e + react-native-contacts: cb4f3b823550adbce0802901b9e63bf1526ec0af + react-native-document-picker: cd4d6b36a5207ad7a9e599ebb9eb0c2e84fa0b87 + react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a + react-native-hce: d416a1fcb8fd85e1d46260f72c29589a77869879 + react-native-html-to-pdf: 4c5c6e26819fe202971061594058877aa9b25265 + react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695 + react-native-mmkv: e97c0c79403fb94577e5d902ab1ebd42b0715b43 + react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac + react-native-nfc-manager: 1bcdf6f96c9065a8f4c8d9a0c6fb2db625205b2c + react-native-pdf: 7c0e91ada997bac8bac3bb5bea5b6b81f5a3caae + react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 + react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a + react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 + react-native-tcp-socket: c1b7297619616b4c9caae6889bcb0aba78086989 React-nativeconfig: 3b359be06d9ee8d64c1eacbca4f1040f331573fd - React-NativeModulesApple: 8fa1db4855dbc1d437d017bc080e75535c85d2d2 + React-NativeModulesApple: 511a01d5be0fdb768f6438dd672a79808451ab00 React-perflogger: e9ebfc705cb9f60ef5d471637350ccab7abd0444 React-RCTActionSheet: 75cf1acf78e9c2eab4431c3bec100a6462a7f0d5 - React-RCTAnimation: 93c464b62848bf9eddecf592819ca76111efb542 - React-RCTAppDelegate: 2f909ac09a87c15465f8b9a5a23c522ca1c19bb3 - React-RCTBlob: 946b43b7698e04ca313f4c872d716887f9e87fd8 - React-RCTFabric: f92685634fe3ef6ec0fc08ce3b55174b0a7cfb55 - React-RCTImage: f3e09ea018d9d49908ce13b4a56949bb7a27a640 - React-RCTLinking: d808fd43aeb59a4e8851a31b82adcf86d585ac5d - React-RCTNetwork: c36cabc37ebd5b6944ad2ba6b41971d8fb273dae - React-RCTSettings: 4268e08911a0005568328fa8909f3558c8ce2a83 - React-RCTText: 82d4e9e279c9cfaed5b95bb86a1d7212a7fd6f21 - React-RCTVibration: 6a0fe57a35bf19d88a611d64563b34fce81af0a7 - React-rendererdebug: 98e83fde6f560b98727a050c602791b72eb7d792 + React-RCTAnimation: 57a1a83a919ead49436ebe69a902d823f2059e61 + React-RCTAppDelegate: 7732b1367774126055f64b3154cef2ba7d28eea2 + React-RCTBlob: efb9cd99a32b22ca94989adde148a9938a8f6fcc + React-RCTFabric: 08a9ff1c3e612f6cc0b4c1cb06f748daea4073d1 + React-RCTImage: 862823815db42c847cbd875b5ccdd64d320da693 + React-RCTLinking: 682e3018e2192f2cf87ddc601cccdac53fb8b1b7 + React-RCTNetwork: 2945ab8c3ff4e2a6603b5d9a220c35b49f3a3ae0 + React-RCTSettings: 807d08082b799d79a00b9fb3ac1590183afaa241 + React-RCTText: d0e6f900dc2d9796240ff991abec9af8ef713a7b + React-RCTVibration: 4feafdb46bebf5380ab343d86b3ddfa4aadcb071 + React-rendererdebug: 3c1a0a5775f81aab7c1fdd0cb0ce0913802170ce React-rncore: bb90d227f926d19683f9c5790d8e3687a4db051a - React-RuntimeApple: d6a09a97e8e8b2b110df39078785afd091e9118f - React-RuntimeCore: 5473e766306c1c9036a5867544673693d1412da8 + React-RuntimeApple: 03bc1bb50bb621d3dd5a7ad0b0fd9ae5b7aa7d03 + React-RuntimeCore: 5411916dbb5d27b2cade9781bbe210ae23906d41 React-runtimeexecutor: 99640ce20e73e06301dd5e18cc6646fa0968347b - React-RuntimeHermes: aba99ffa7ddace35e066beedb8610a8210ee074b - React-runtimescheduler: c5080bd8fd77bd9b98d9a5ec2a4a5cee523e3ec8 - React-utils: 005eaf562b377922d8cf8a5e433046185e479796 - ReactCommon: 788c996e0ae30635a67c2567db2e21f459bcd632 - RealmJS: ff8a7d6d6e339c1b3410a96ceb4151b151fe56f5 - RNCClipboard: e1d17c9d093d8129ef50b39b63a17a0e8ccd0ade - RNDeviceInfo: f9d6fd102f8b11daa7ee322391b23cebee3bd95f - RNFastImage: 462a183c4b0b6b26fdfd639e1ed6ba37536c3b87 - RNFBApp: 21b8f1dd60e9fb1ef8f6fd191a6dbbfacecc7d31 - RNFBMessaging: 49fb34853809c4694bf9d3d794a0f8916b32b94e - RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8 - RNGestureHandler: 277be33fdf1d6cec044909a657d5014a97266135 - RNIap: db3f43e41ed9d7b5c04fbfe759b10ac344521af6 - RNKeychain: df33ae4d27df06622fc14190b790ba8749f6be76 - RNLocalize: 19917c3f32cf6386f7d2957a638a4281669cae57 - RNReanimated: 608f4e674643d5fe9c91d2575db2cf306807c67c - RNScreens: 6584fce529be4800e3d12c8069376df9e12bafaf - RNSentry: 6e4fa452ffa621a4bbcd08b53cbb12c55c955593 - RNShare: d1da96f570164d84821ee9d75740eee7ec2efa87 - RNSVG: 4611b66c2167ba03429e7428a3a490be6e439391 + React-RuntimeHermes: 872b0ab24196e98f7ef6857241edaeb9c576a7fb + React-runtimescheduler: bd24ce5b35c288355514aa42cacc9dc782d12dba + React-utils: eb18bf39ea87c4d38f175f41d77b07093eaf7765 + ReactCommon: 9ed4522c478fc82fd9822fd2be960875fccb2c45 + RealmJS: ab0e1afd06ef856f8ae396520da1690bcaa669f8 + RNCClipboard: 4411a880506f11fbcbdc03b5093eb8ffb246d900 + RNDeviceInfo: 0a7c1d2532aa7691f9b9925a27e43af006db4dae + RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 + RNFBApp: 9646e09d041ea159b84584865212e4cf33acd179 + RNFBMessaging: 3e2682ea5e15fe86da24d16d16019395a881f33c + RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 + RNGestureHandler: ce6dc347cb4edb664bfc250fc37cd753ae7432ec + RNIap: 2a3674383dcf383e5b7b3c4680dc87f1b2657326 + RNKeychain: a65256b6ca6ba6976132cc4124b238a5b13b3d9c + RNLocalize: 95a43f85e41a966be7bc9cff2437128911c52da0 + RNReanimated: 45553a3ae29a75a76269595f8554d07d4090e392 + RNScreens: aa943ad421c3ced3ef5a47ede02b0cbfc43a012e + RNSentry: c74f756c88aa2a90070f555609d2a31ab2a7f461 + RNShare: da6d90b6dc332f51f86498041d6e34211f96b630 + RNSVG: ed492aaf3af9ca01bc945f7a149d76d62e73ec82 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d Sentry: f8374b5415bc38dfb5645941b3ae31230fbeae57 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 9db4b2da1ed5d8e0c94158f9ac379c8b1b529f59 + Yoga: 272dddd3c0e1e23d09ba81ed55ddfa7fdd3caa17 PODFILE CHECKSUM: 96f11e0207a02f8dd77f13d8f57cfca43b677e6b -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/ios/hexa_keeper.xcodeproj/project.pbxproj b/ios/hexa_keeper.xcodeproj/project.pbxproj index 8c7d912d1..d8c2c6762 100644 --- a/ios/hexa_keeper.xcodeproj/project.pbxproj +++ b/ios/hexa_keeper.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 159FF345F1297D1FF48528D9 /* libPods-hexa_keeper_dev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E848F7DFCADFAE3FBFB35DC7 /* libPods-hexa_keeper_dev.a */; }; 18788ED8284652050002EFFD /* BuildFile in Resources */ = {isa = PBXBuildFile; }; 1889D6352DB0076400781072 /* Lora-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1889D6312DB0076400781072 /* Lora-Medium.ttf */; }; 1889D6362DB0076400781072 /* Lora-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1889D6332DB0076400781072 /* Lora-SemiBold.ttf */; }; @@ -31,11 +30,12 @@ 35112D542BD7DB3000343479 /* cloudbackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 35112D532BD7DB3000343479 /* cloudbackup.m */; }; 35112D552BD7DB3000343479 /* cloudbackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 35112D532BD7DB3000343479 /* cloudbackup.m */; }; 413074DFD01E46F1A0D40BD6 /* Inter-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6702D068A26E40DDA1F0E809 /* Inter-Bold.ttf */; }; + 52E89955AACE59B0203F4C41 /* libPods-hexa_keeper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A8037C6818E71B30BA8AFC5 /* libPods-hexa_keeper.a */; }; 75EA47EFF012F028F07CCEA1 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 8F3F2BAB5E21BDF6D6F8747B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 435B6B72AB94B4AA09AF3B47 /* PrivacyInfo.xcprivacy */; }; 97223FD2D6714257A178B813 /* Inter-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 72B7870523C24718A997BDFB /* Inter-Italic.ttf */; }; - D5BDB3AB19BE735A37AFA3AB /* libPods-hexa_keeper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 73E1453C58CE6BD945B34DA0 /* libPods-hexa_keeper.a */; }; + 9738E9F2EA173CB392DD22E3 /* libPods-hexa_keeper_dev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B93466085C8D4E3F904D571 /* libPods-hexa_keeper_dev.a */; }; DEFBEF21CC7B49DC8A027D1B /* Inter-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 94C4DE0519174B57A37E3D56 /* Inter-Light.ttf */; }; ECAEA7317141416FAA0B52C5 /* Inter-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 674E13DE09DE4AC0AD5E4C31 /* Inter-BoldItalic.ttf */; }; F42D52AF284A3D2600235E3E /* hexa_keeper_dev-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F42D52AE284A3D2600235E3E /* hexa_keeper_dev-Info.plist */; }; @@ -64,7 +64,7 @@ 00E356EE1AD99517003FC87E /* hexa_keeperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = hexa_keeperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* hexa_keeperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = hexa_keeperTests.m; sourceTree = ""; }; - 08FBE774D80E15AF57C7A99D /* Pods-hexa_keeper.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper.debug.xcconfig"; path = "Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper.debug.xcconfig"; sourceTree = ""; }; + 0767111BA75AA389A0EF46DC /* Pods-hexa_keeper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper.release.xcconfig"; path = "Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* hexa_keeper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = hexa_keeper.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = hexa_keeper/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.mm; path = hexa_keeper/AppDelegate.mm; sourceTree = ""; }; @@ -79,19 +79,21 @@ 18A6256AB47842ABA0F4BF8D /* Inter-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-LightItalic.ttf"; path = "../src/assets/fonts/Inter-LightItalic.ttf"; sourceTree = ""; }; 18B4D55E2847898D004AB91F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 1F04EB0F29BFB13600326473 /* hexa_keeper-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "hexa_keeper-Bridging-Header.h"; sourceTree = ""; }; + 2086559EDB2D232BF5C112A3 /* Pods-hexa_keeper.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper.debug.xcconfig"; path = "Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper.debug.xcconfig"; sourceTree = ""; }; 35112D4F2BD7DAE700343479 /* CloudBackupHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudBackupHelper.swift; sourceTree = ""; }; 35112D522BD7DB1700343479 /* cloudbackup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cloudbackup.h; sourceTree = ""; }; 35112D532BD7DB3000343479 /* cloudbackup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = cloudbackup.m; sourceTree = ""; }; 435B6B72AB94B4AA09AF3B47 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = hexa_keeper/PrivacyInfo.xcprivacy; sourceTree = ""; }; 6702D068A26E40DDA1F0E809 /* Inter-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Bold.ttf"; path = "../src/assets/fonts/Inter-Bold.ttf"; sourceTree = ""; }; 674E13DE09DE4AC0AD5E4C31 /* Inter-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-BoldItalic.ttf"; path = "../src/assets/fonts/Inter-BoldItalic.ttf"; sourceTree = ""; }; + 6C34800F899C5517558E0FB0 /* Pods-hexa_keeper_dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper_dev.debug.xcconfig"; path = "Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev.debug.xcconfig"; sourceTree = ""; }; 72B7870523C24718A997BDFB /* Inter-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Italic.ttf"; path = "../src/assets/fonts/Inter-Italic.ttf"; sourceTree = ""; }; - 73E1453C58CE6BD945B34DA0 /* libPods-hexa_keeper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hexa_keeper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = hexa_keeper/LaunchScreen.storyboard; sourceTree = ""; }; + 8B93466085C8D4E3F904D571 /* libPods-hexa_keeper_dev.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hexa_keeper_dev.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 94C4DE0519174B57A37E3D56 /* Inter-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Light.ttf"; path = "../src/assets/fonts/Inter-Light.ttf"; sourceTree = ""; }; - CFF59ED1275C7DD527FF60A7 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = hexa_keeper/PrivacyInfo.xcprivacy; sourceTree = ""; }; - DA190250F1EBDD229E110BFA /* Pods-hexa_keeper_dev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper_dev.release.xcconfig"; path = "Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev.release.xcconfig"; sourceTree = ""; }; - E848F7DFCADFAE3FBFB35DC7 /* libPods-hexa_keeper_dev.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hexa_keeper_dev.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 94DA7C0B1780AD8AE9D0AF40 /* Pods-hexa_keeper_dev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper_dev.release.xcconfig"; path = "Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev.release.xcconfig"; sourceTree = ""; }; + 9A8037C6818E71B30BA8AFC5 /* libPods-hexa_keeper.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-hexa_keeper.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + CFF59ED1275C7DD527FF60A7 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = hexa_keeper/PrivacyInfo.xcprivacy; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F1E12F69D30C4430BF22E132 /* Inter-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Regular.ttf"; path = "../src/assets/fonts/Inter-Regular.ttf"; sourceTree = ""; }; F42D518A2849E2AA00235E3E /* hexa_keeper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = hexa_keeper.entitlements; path = hexa_keeper/hexa_keeper.entitlements; sourceTree = ""; }; @@ -100,8 +102,6 @@ F4F207ED2849D20A005E79DA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; F4F7F75C2A0558650027FE4C /* keepScreenAwake.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = keepScreenAwake.m; sourceTree = ""; }; F4F7F75F2A0558900027FE4C /* keepScreenAwake.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = keepScreenAwake.h; sourceTree = ""; }; - F89B891DBAA38F48E9272960 /* Pods-hexa_keeper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper.release.xcconfig"; path = "Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper.release.xcconfig"; sourceTree = ""; }; - FF44D0B6D2E87C0D657DAFA5 /* Pods-hexa_keeper_dev.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hexa_keeper_dev.debug.xcconfig"; path = "Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -117,7 +117,7 @@ buildActionMask = 2147483647; files = ( 75EA47EFF012F028F07CCEA1 /* BuildFile in Frameworks */, - D5BDB3AB19BE735A37AFA3AB /* libPods-hexa_keeper.a in Frameworks */, + 52E89955AACE59B0203F4C41 /* libPods-hexa_keeper.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,7 +125,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 159FF345F1297D1FF48528D9 /* libPods-hexa_keeper_dev.a in Frameworks */, + 9738E9F2EA173CB392DD22E3 /* libPods-hexa_keeper_dev.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -179,8 +179,8 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 73E1453C58CE6BD945B34DA0 /* libPods-hexa_keeper.a */, - E848F7DFCADFAE3FBFB35DC7 /* libPods-hexa_keeper_dev.a */, + 9A8037C6818E71B30BA8AFC5 /* libPods-hexa_keeper.a */, + 8B93466085C8D4E3F904D571 /* libPods-hexa_keeper_dev.a */, ); name = Frameworks; sourceTree = ""; @@ -249,10 +249,10 @@ E233CBF5F47BEE60B243DCF8 /* Pods */ = { isa = PBXGroup; children = ( - 08FBE774D80E15AF57C7A99D /* Pods-hexa_keeper.debug.xcconfig */, - F89B891DBAA38F48E9272960 /* Pods-hexa_keeper.release.xcconfig */, - FF44D0B6D2E87C0D657DAFA5 /* Pods-hexa_keeper_dev.debug.xcconfig */, - DA190250F1EBDD229E110BFA /* Pods-hexa_keeper_dev.release.xcconfig */, + 2086559EDB2D232BF5C112A3 /* Pods-hexa_keeper.debug.xcconfig */, + 0767111BA75AA389A0EF46DC /* Pods-hexa_keeper.release.xcconfig */, + 6C34800F899C5517558E0FB0 /* Pods-hexa_keeper_dev.debug.xcconfig */, + 94DA7C0B1780AD8AE9D0AF40 /* Pods-hexa_keeper_dev.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -319,14 +319,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "hexa_keeper" */; buildPhases = ( - 8962DD9D01ABDAE58D6CD766 /* [CP] Check Pods Manifest.lock */, + ADFAFEE20899C62540657FD8 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 519FB40A87538C8DCE3A4B59 /* [CP] Embed Pods Frameworks */, - 9BE07BDBC93FF0B0090C4B28 /* [CP] Copy Pods Resources */, - F41DE941DF6F218D92EBA386 /* [CP-User] [RNFB] Core Configuration */, + ED87A419ACA52A5198C20778 /* [CP] Embed Pods Frameworks */, + D65ACA0151D9E9005579D9FB /* [CP] Copy Pods Resources */, + 90E03B945E98081D718BEA5F /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -341,15 +341,15 @@ isa = PBXNativeTarget; buildConfigurationList = F49B77F72848DF5200F9AFBD /* Build configuration list for PBXNativeTarget "hexa_keeper_dev" */; buildPhases = ( - 3B72837E09FDCE8AE9B64260 /* [CP] Check Pods Manifest.lock */, + 44AB5644361D37D684C09DDC /* [CP] Check Pods Manifest.lock */, F49B77E12848DF5200F9AFBD /* Start Packager */, F49B77E22848DF5200F9AFBD /* Sources */, F49B77E52848DF5200F9AFBD /* Frameworks */, F49B77E72848DF5200F9AFBD /* Resources */, F49B77F32848DF5200F9AFBD /* Bundle React Native code and images */, - 8EBB6A86DEA895EEFA0939C0 /* [CP] Embed Pods Frameworks */, - 5ABA2DEEAC8D206BDE9D1A6B /* [CP] Copy Pods Resources */, - 917FB4A01F8341AE0ADBBA05 /* [CP-User] [RNFB] Core Configuration */, + 84880E6E4CEE76DF6AF27E98 /* [CP] Embed Pods Frameworks */, + 6792783B5BC4A3D58DA25B80 /* [CP] Copy Pods Resources */, + 4B8B1D9AB646A83D6E1700C1 /* [CP-User] [RNFB] Core Configuration */, ); buildRules = ( ); @@ -466,7 +466,7 @@ shellPath = /bin/zsh; shellScript = "set -e\n\nexport NODE_BINARY=/opt/homebrew/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 3B72837E09FDCE8AE9B64260 /* [CP] Check Pods Manifest.lock */ = { + 44AB5644361D37D684C09DDC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -488,24 +488,20 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 519FB40A87538C8DCE3A4B59 /* [CP] Embed Pods Frameworks */ = { + 4B8B1D9AB646A83D6E1700C1 /* [CP-User] [RNFB] Core Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks-${CONFIGURATION}-output-files.xcfilelist", + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); + name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; - 5ABA2DEEAC8D206BDE9D1A6B /* [CP] Copy Pods Resources */ = { + 6792783B5BC4A3D58DA25B80 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -522,59 +518,59 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 8962DD9D01ABDAE58D6CD766 /* [CP] Check Pods Manifest.lock */ = { + 84880E6E4CEE76DF6AF27E98 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-hexa_keeper-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 8EBB6A86DEA895EEFA0939C0 /* [CP] Embed Pods Frameworks */ = { + 90E03B945E98081D718BEA5F /* [CP-User] [RNFB] Core Configuration */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks-${CONFIGURATION}-output-files.xcfilelist", + inputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", ); + name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper_dev/Pods-hexa_keeper_dev-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; }; - 917FB4A01F8341AE0ADBBA05 /* [CP-User] [RNFB] Core Configuration */ = { + ADFAFEE20899C62540657FD8 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-hexa_keeper-checkManifestLockResult.txt", ); - name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - 9BE07BDBC93FF0B0090C4B28 /* [CP] Copy Pods Resources */ = { + D65ACA0151D9E9005579D9FB /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -591,18 +587,22 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-resources.sh\"\n"; showEnvVarsInLog = 0; }; - F41DE941DF6F218D92EBA386 /* [CP-User] [RNFB] Core Configuration */ = { + ED87A419ACA52A5198C20778 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - name = "[CP-User] [RNFB] Core Configuration"; runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hexa_keeper/Pods-hexa_keeper-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; F49B77E12848DF5200F9AFBD /* Start Packager */ = { isa = PBXShellScriptBuildPhase; @@ -740,7 +740,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 08FBE774D80E15AF57C7A99D /* Pods-hexa_keeper.debug.xcconfig */; + baseConfigurationReference = 2086559EDB2D232BF5C112A3 /* Pods-hexa_keeper.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -748,7 +748,7 @@ CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 557; + CURRENT_PROJECT_VERSION = 558; DEVELOPMENT_TEAM = Y5TCB759QL; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( @@ -867,14 +867,14 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F89B891DBAA38F48E9272960 /* Pods-hexa_keeper.release.xcconfig */; + baseConfigurationReference = 0767111BA75AA389A0EF46DC /* Pods-hexa_keeper.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: Bithyve UK Ltd (Y5TCB759QL)"; - CURRENT_PROJECT_VERSION = 557; + CURRENT_PROJECT_VERSION = 558; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( @@ -1135,7 +1135,7 @@ }; F49B77F82848DF5200F9AFBD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FF44D0B6D2E87C0D657DAFA5 /* Pods-hexa_keeper_dev.debug.xcconfig */; + baseConfigurationReference = 6C34800F899C5517558E0FB0 /* Pods-hexa_keeper_dev.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "dev-AppIcon"; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -1144,7 +1144,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 557; + CURRENT_PROJECT_VERSION = 558; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; ENABLE_BITCODE = NO; @@ -1267,7 +1267,7 @@ }; F49B77F92848DF5200F9AFBD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DA190250F1EBDD229E110BFA /* Pods-hexa_keeper_dev.release.xcconfig */; + baseConfigurationReference = 94DA7C0B1780AD8AE9D0AF40 /* Pods-hexa_keeper_dev.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "dev-AppIcon"; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; @@ -1275,7 +1275,7 @@ CODE_SIGN_ENTITLEMENTS = hexa_keeper_dev.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 557; + CURRENT_PROJECT_VERSION = 558; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( diff --git a/ios/hexa_keeper/Info.plist b/ios/hexa_keeper/Info.plist index e5e955b88..905c8cacf 100644 --- a/ios/hexa_keeper/Info.plist +++ b/ios/hexa_keeper/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 557 + 558 LSApplicationQueriesSchemes itms-apps diff --git a/ios/hexa_keeperTests/Info.plist b/ios/hexa_keeperTests/Info.plist index 9d400e8ef..a7b97d4a7 100644 --- a/ios/hexa_keeperTests/Info.plist +++ b/ios/hexa_keeperTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 557 + 558 diff --git a/ios/hexa_keeper_dev-Info.plist b/ios/hexa_keeper_dev-Info.plist index 4425b2456..648afdc0d 100644 --- a/ios/hexa_keeper_dev-Info.plist +++ b/ios/hexa_keeper_dev-Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 557 + 558 LSApplicationQueriesSchemes itms-apps diff --git a/package.json b/package.json index 4fb06d235..385e486b5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "lint:fix": "npm run lint -- --fix", "prepare": "husky install && chmod +x setup.sh && sh ./setup.sh", "postinstall": "patch-package && node patches/patch-sifir_android.mjs", + "bare-pack": "npx bare-pack --target ios --target android --linked --out src/services/p2p/app.bundle.mjs src/services/p2p/worklet.mjs", "androidDevelopmentDebug": "ENVFILE=.env react-native run-android --mode=developmentDebug --appIdSuffix=development", "androidDevelopmentRelease": "ENVFILE=.env react-native run-android --mode=developmentRelease --appIdSuffix=development", "androidProductionDebug": "ENVFILE=.env react-native run-android --mode=productionDebug", @@ -27,6 +28,7 @@ "dependencies": { "@bitcoinerlab/miniscript": "1.4.0", "@ngraveio/bc-ur": "1.1.6", + "@noble/curves": "1.9.4", "@noble/secp256k1": "1.6.3", "@react-native-clipboard/clipboard": "1.16.2", "@react-native-community/netinfo": "11.4.1", @@ -38,9 +40,12 @@ "@reduxjs/toolkit": "1.8.2", "@sentry/react-native": "5.33.1", "@shipt/segmented-arc-for-react-native": "1.2.1", + "@stablelib/base64": "2.0.1", "@testing-library/react-native": "11.0.0", "assert": "2.0.0", "axios": "1.8.2", + "b4a": "1.6.7", + "bare-rpc": "0.2.6", "base-64": "1.0.0", "base58check": "2.0.0", "bip21": "2.0.3", @@ -59,6 +64,7 @@ "ecpair": "2.0.1", "electrum-client": "git+https://github.com/bithyve/rn-electrum-client.git#76c0ea35e1a50c47f3a7f818d529ebd100161496", "events": "1.0.0", + "hyperswarm": "4.11.7", "idx": "2.5.6", "libportal-react-native": "git+https://github.com/bithyve/libportal-react-native.git#3f9373785265f3e18218eefb8958109feec8f7c3", "lodash": "4.17.21", @@ -74,6 +80,7 @@ "react-localization": "1.0.19", "react-native": "0.74.7", "react-native-background-timer": "2.4.1", + "react-native-bare-kit": "0.5.6", "react-native-biometrics": "2.2.0", "react-native-blob-util": "0.18.3", "react-native-camera": "4.2.1", @@ -124,7 +131,7 @@ "stream": "0.0.2", "stream-browserify": "1.0.0", "tronweb": "6.0.3", - "url": "0.10.1" + "uuid": "11.1.0" }, "devDependencies": { "@babel/core": "7.26.0", diff --git a/src/assets/images/Contact-footer-dark.svg b/src/assets/images/Contact-footer-dark.svg new file mode 100644 index 000000000..e85c360fe --- /dev/null +++ b/src/assets/images/Contact-footer-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/Contact-footer-light.svg b/src/assets/images/Contact-footer-light.svg new file mode 100644 index 000000000..29e7eb4a1 --- /dev/null +++ b/src/assets/images/Contact-footer-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/Pin.svg b/src/assets/images/Pin.svg new file mode 100644 index 000000000..bfd4703f6 --- /dev/null +++ b/src/assets/images/Pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/add-image-icon.svg b/src/assets/images/add-image-icon.svg new file mode 100644 index 000000000..6fd62ee6a --- /dev/null +++ b/src/assets/images/add-image-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/add-profile-dark.svg b/src/assets/images/add-profile-dark.svg new file mode 100644 index 000000000..9fa4b8d43 --- /dev/null +++ b/src/assets/images/add-profile-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/change-profile-icon.svg b/src/assets/images/change-profile-icon.svg new file mode 100644 index 000000000..7d9b7d529 --- /dev/null +++ b/src/assets/images/change-profile-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/chat-plaaceholde-image.svg b/src/assets/images/chat-plaaceholde-image.svg new file mode 100644 index 000000000..2979ea475 --- /dev/null +++ b/src/assets/images/chat-plaaceholde-image.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/concierge-query-illustration-dark.svg b/src/assets/images/concierge-query-illustration-dark.svg index 1fc4c8f1b..e52d2c23a 100644 --- a/src/assets/images/concierge-query-illustration-dark.svg +++ b/src/assets/images/concierge-query-illustration-dark.svg @@ -2,29 +2,33 @@ - + - - - + - + - + - - + + + + + - + + + + @@ -40,7 +44,7 @@ - + @@ -55,12 +59,4 @@ - - - - - - - - diff --git a/src/assets/images/concierge-query-illustration-light.svg b/src/assets/images/concierge-query-illustration-light.svg index 9e89c0ee2..98d605297 100644 --- a/src/assets/images/concierge-query-illustration-light.svg +++ b/src/assets/images/concierge-query-illustration-light.svg @@ -2,29 +2,33 @@ - + - - - + - + - + - - + + + + + - + + + + @@ -40,7 +44,7 @@ - + @@ -55,12 +59,4 @@ - - - - - - - - diff --git a/src/assets/images/contact-add-icon.svg b/src/assets/images/contact-add-icon.svg new file mode 100644 index 000000000..dd8d8dcf2 --- /dev/null +++ b/src/assets/images/contact-add-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/images/contact-edit.svg b/src/assets/images/contact-edit.svg new file mode 100644 index 000000000..00d4d7114 --- /dev/null +++ b/src/assets/images/contact-edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/contact-footer-default.svg b/src/assets/images/contact-footer-default.svg new file mode 100644 index 000000000..fa7d0d116 --- /dev/null +++ b/src/assets/images/contact-footer-default.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/images/contact-header-dark.svg b/src/assets/images/contact-header-dark.svg new file mode 100644 index 000000000..7d11a07de --- /dev/null +++ b/src/assets/images/contact-header-dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/contact-header-light.svg b/src/assets/images/contact-header-light.svg new file mode 100644 index 000000000..ed2bd460c --- /dev/null +++ b/src/assets/images/contact-header-light.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/contact-placeholder-image.png b/src/assets/images/contact-placeholder-image.png new file mode 100644 index 000000000..c95d2e8c5 Binary files /dev/null and b/src/assets/images/contact-placeholder-image.png differ diff --git a/src/assets/images/edit-profile-dark.svg b/src/assets/images/edit-profile-dark.svg new file mode 100644 index 000000000..cd15b63d2 --- /dev/null +++ b/src/assets/images/edit-profile-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/assets/images/exportFile.svg b/src/assets/images/exportFile.svg new file mode 100644 index 000000000..d8ac04316 --- /dev/null +++ b/src/assets/images/exportFile.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/hire-advisor.svg b/src/assets/images/hire-advisor.svg index 86e206e66..088a35ad2 100644 --- a/src/assets/images/hire-advisor.svg +++ b/src/assets/images/hire-advisor.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/src/assets/images/keeper-new-logo-image.png b/src/assets/images/keeper-new-logo-image.png new file mode 100644 index 000000000..1aaddc59a Binary files /dev/null and b/src/assets/images/keeper-new-logo-image.png differ diff --git a/src/assets/images/placeholder-image-dark.png b/src/assets/images/placeholder-image-dark.png new file mode 100644 index 000000000..448ebcf95 Binary files /dev/null and b/src/assets/images/placeholder-image-dark.png differ diff --git a/src/assets/images/placeholder-whote-image.png b/src/assets/images/placeholder-whote-image.png new file mode 100644 index 000000000..67a10fe67 Binary files /dev/null and b/src/assets/images/placeholder-whote-image.png differ diff --git a/src/assets/images/profile-placeHolder.png b/src/assets/images/profile-placeHolder.png new file mode 100644 index 000000000..de14cd721 Binary files /dev/null and b/src/assets/images/profile-placeHolder.png differ diff --git a/src/assets/images/send-white-icon.svg b/src/assets/images/send-white-icon.svg new file mode 100644 index 000000000..67383db2d --- /dev/null +++ b/src/assets/images/send-white-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/share-green.svg b/src/assets/images/share-green.svg new file mode 100644 index 000000000..c429a1102 --- /dev/null +++ b/src/assets/images/share-green.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/share-white.svg b/src/assets/images/share-white.svg new file mode 100644 index 000000000..4c8ca35da --- /dev/null +++ b/src/assets/images/share-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/privateImages/Contact-footor-gold-icon.svg b/src/assets/privateImages/Contact-footor-gold-icon.svg new file mode 100644 index 000000000..978d2ff43 --- /dev/null +++ b/src/assets/privateImages/Contact-footor-gold-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/privateImages/acquire-header-white.svg b/src/assets/privateImages/acquire-header-white.svg new file mode 100644 index 000000000..32b9d48bc --- /dev/null +++ b/src/assets/privateImages/acquire-header-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/privateImages/add-profile-gold.svg b/src/assets/privateImages/add-profile-gold.svg new file mode 100644 index 000000000..dbcd45986 --- /dev/null +++ b/src/assets/privateImages/add-profile-gold.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/privateImages/contact-header-gold-icon.svg b/src/assets/privateImages/contact-header-gold-icon.svg new file mode 100644 index 000000000..0aaf8217f --- /dev/null +++ b/src/assets/privateImages/contact-header-gold-icon.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/privateImages/contact-header-white.svg b/src/assets/privateImages/contact-header-white.svg new file mode 100644 index 000000000..d5ff6ab78 --- /dev/null +++ b/src/assets/privateImages/contact-header-white.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/privateImages/edit-profile-gold.svg b/src/assets/privateImages/edit-profile-gold.svg new file mode 100644 index 000000000..039f0285b --- /dev/null +++ b/src/assets/privateImages/edit-profile-gold.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/privateImages/share-gold-icon.svg b/src/assets/privateImages/share-gold-icon.svg new file mode 100644 index 000000000..f71085654 --- /dev/null +++ b/src/assets/privateImages/share-gold-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/KeeperQRCode.tsx b/src/components/KeeperQRCode.tsx index 597260fef..1b4f1e29e 100644 --- a/src/components/KeeperQRCode.tsx +++ b/src/components/KeeperQRCode.tsx @@ -4,7 +4,7 @@ import { StyleSheet } from 'react-native'; import { Box, useColorMode } from 'native-base'; import { useSelector } from 'react-redux'; -import KeeperNewLogo from 'src/assets/images/keeper-new-logo.png'; +import KeeperNewLogo from 'src/assets/images/keeper-new-logo-image.png'; import KeeperPrivateNewLogo from 'src/assets/privateImages/keeper-private-new-logo.png'; function KeeperQRCode({ diff --git a/src/components/MenuFooter.tsx b/src/components/MenuFooter.tsx index 4af81ebcd..7c186fa31 100644 --- a/src/components/MenuFooter.tsx +++ b/src/components/MenuFooter.tsx @@ -4,7 +4,6 @@ import { Platform, StyleSheet } from 'react-native'; import { hp, wp } from 'src/constants/responsive'; import WalletIcon from 'src/assets/images/Wallet-grey.svg'; import KeyIcon from 'src/assets/images/key-grey.svg'; -import ConciergeIcon from 'src/assets/images/faq-grey.svg'; import MoreIcon from 'src/assets/images/more-grey.svg'; import { TouchableOpacity } from 'react-native-gesture-handler'; import Text from './KeeperText'; @@ -12,13 +11,14 @@ import { LocalizationContext } from 'src/context/Localization/LocContext'; import ThemedSvg from './ThemedSvg.tsx/ThemedSvg'; import ThemedColor from './ThemedColor/ThemedColor'; import BtcLogoGrey from 'src/assets/images/Btc-Logo-grey.svg'; +import ContactIcon from 'src/assets/images/contact-footer-default.svg'; import { useIsRampAvailable } from 'src/hooks/useIsRampAvailable'; const MenuFooter = ({ selectedOption, onOptionChange }) => { const { colorMode } = useColorMode(); const isDarkMode = colorMode === 'dark'; const { translations } = useContext(LocalizationContext); - const { wallet: walletTranslation, buyBTC: buyBTCTranslation } = translations; + const { wallet: walletTranslation, buyBTC: buyBTCTranslation, contactText } = translations; const selectedFooterColor = ThemedColor({ name: 'footer_selected_option' }); const { isRampAvailable } = useIsRampAvailable(); @@ -39,9 +39,9 @@ const MenuFooter = ({ selectedOption, onOptionChange }) => { selectedIcon: , }, { - name: walletTranslation.concierge, - defaultIcon: , - selectedIcon: , + name: contactText.contacts, + defaultIcon: , + selectedIcon: , }, { name: walletTranslation.more, diff --git a/src/components/ThemedSvg.tsx/ThemedIcons.js b/src/components/ThemedSvg.tsx/ThemedIcons.js index e01cd4c29..ad8d62346 100644 --- a/src/components/ThemedSvg.tsx/ThemedIcons.js +++ b/src/components/ThemedSvg.tsx/ThemedIcons.js @@ -316,6 +316,23 @@ import BuyAcquireArrowGold from 'src/assets/privateImages/acquire-buy-gold-arrow import CoinsIcon from 'src/assets/images/coins.svg'; import CoinIconLight from 'src/assets/images/coinsLight.svg'; import CoinsIconPrivate from 'src/assets/privateImages/coinsPrivate.svg'; +import ContactHeaderLight from 'src/assets/images/contact-header-light.svg'; +import ContactHeaderDark from 'src/assets/images/contact-header-dark.svg'; +import ContactFooterLight from 'src/assets/images/Contact-footer-light.svg'; +import ContactFooterDark from 'src/assets/images/Contact-footer-dark.svg'; +import ShareLight from 'src/assets/images/share-green.svg'; +import ShareDark from 'src/assets/images/share-white.svg'; +import ShareGold from 'src/assets/privateImages/share-gold-icon.svg'; +import ContactFooterGold from 'src/assets/privateImages/Contact-footor-gold-icon.svg'; +import ContactHeaderGold from 'src/assets/privateImages/contact-header-gold-icon.svg'; +import ContactHeaderWhite from 'src/assets/privateImages/contact-header-white.svg'; +import AddImageIcon from 'src/assets/images/add-image-icon.svg'; +import AddImageDark from 'src/assets/images/add-profile-dark.svg'; +import AddImageGold from 'src/assets/privateImages/add-profile-gold.svg'; +import ChangeProfileImageIcon from 'src/assets/images/change-profile-icon.svg'; +import ChangeProfileImageIconDark from 'src/assets/images/edit-profile-dark.svg'; +import PrivateChangeProfileImage from 'src/assets/privateImages/edit-profile-gold.svg'; +import AquireHeadrWhite from 'src/assets/privateImages/acquire-header-white.svg'; import SwitchLightLogo from 'src/assets/images/switch-light-logo.svg'; import SwitchDarkLogo from 'src/assets/images/switch-dark-logo.svg'; import SwapDownGreenIcon from 'src/assets/images/swap-down-green-icon.svg'; @@ -323,6 +340,36 @@ import SwapDownWhiteIcon from 'src/assets/images/swap-down-white-icon.svg'; import BuyBtcPRivate from 'src/assets/privateImages/buy-btc-header-private.svg'; const themeIcons = { + edit_profile_icons: { + DARK: ChangeProfileImageIconDark, + LIGHT: ChangeProfileImageIcon, + PRIVATE: ChangeProfileImageIconDark, + PRIVATE_LIGHT: PrivateChangeProfileImage, + }, + add_profile_icons: { + DARK: AddImageDark, + LIGHT: AddImageIcon, + PRIVATE: AddImageDark, + PRIVATE_LIGHT: AddImageGold, + }, + share_icons: { + DARK: ShareDark, + LIGHT: ShareLight, + PRIVATE: ShareDark, + PRIVATE_LIGHT: ShareGold, + }, + contact_footer: { + DARK: ContactFooterDark, + LIGHT: ContactFooterLight, + PRIVATE: ContactFooterGold, + PRIVATE_LIGHT: ContactFooterGold, + }, + contact_header: { + DARK: ContactHeaderDark, + LIGHT: ContactHeaderLight, + PRIVATE: ContactHeaderWhite, + PRIVATE_LIGHT: ContactHeaderGold, + }, swap_down_icon: { DARK: SwapDownWhiteIcon, LIGHT: SwapDownGreenIcon, diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index beac303b9..49b5ac294 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1725,7 +1725,7 @@ "noNotiSub": "You’re all set — we’ll let you know when there’s something new." }, "concierge": { - "welcomeToConcierge": "Welcome to Keeper Concierge!", + "welcomeToConcierge": "Welcome to Keeper Concierge", "getAnsweredWithConcierge": "Here you can find help and support to resolve all your questions.", "conciergeModalDesc": "Please remember to never share private information, such as your private seed words or passwords. Our support team will never ask for such information neither in chat nor via any other channels.", "technicalSupportDescription": "Submit a ticket for prompt troubleshooting by our team", @@ -2025,5 +2025,24 @@ "importInfoDesc1": "Bitcoin Keeper supports importing GasFree USDT wallets only. If you're using a compatible wallet provider that also supports gas-free transactions on the Tron network, you can import your wallet using your 12 or 24-word recovery phrase.", "importInfoDesc2": "Once imported, you’ll be able to access and manage your USDT without needing TRX.", "importInfoDescNote": "Note: Standard TRC-20 wallets that require TRX to move funds are not supported." + }, + "contactText": { + "contacts": "Contacts", + "recentChats": "Recent Chats", + "keeperSupport": "Keeper Support", + "hereToHelp": "We're here to help.", + "addContact": "Add Contact", + "editProfile": "Edit Your Profile", + "editProfileDesc": "Add a name/nym and an optional photo.", + "shareContact": "Share Contact Info", + "addNewContact": "Add a New Contact", + "chooseHowToAdd": "Choose how to add the contact", + "name": "Name", + "easyIdentification": "For easy identification", + "share": "Share", + "enterName": "Enter your name", + "editContactName": "Edit Contact Name", + "noContactYet": "No Contacts Yet", + "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } } diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index beac303b9..49b5ac294 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1725,7 +1725,7 @@ "noNotiSub": "You’re all set — we’ll let you know when there’s something new." }, "concierge": { - "welcomeToConcierge": "Welcome to Keeper Concierge!", + "welcomeToConcierge": "Welcome to Keeper Concierge", "getAnsweredWithConcierge": "Here you can find help and support to resolve all your questions.", "conciergeModalDesc": "Please remember to never share private information, such as your private seed words or passwords. Our support team will never ask for such information neither in chat nor via any other channels.", "technicalSupportDescription": "Submit a ticket for prompt troubleshooting by our team", @@ -2025,5 +2025,24 @@ "importInfoDesc1": "Bitcoin Keeper supports importing GasFree USDT wallets only. If you're using a compatible wallet provider that also supports gas-free transactions on the Tron network, you can import your wallet using your 12 or 24-word recovery phrase.", "importInfoDesc2": "Once imported, you’ll be able to access and manage your USDT without needing TRX.", "importInfoDescNote": "Note: Standard TRC-20 wallets that require TRX to move funds are not supported." + }, + "contactText": { + "contacts": "Contacts", + "recentChats": "Recent Chats", + "keeperSupport": "Keeper Support", + "hereToHelp": "We're here to help.", + "addContact": "Add Contact", + "editProfile": "Edit Your Profile", + "editProfileDesc": "Add a name/nym and an optional photo.", + "shareContact": "Share Contact Info", + "addNewContact": "Add a New Contact", + "chooseHowToAdd": "Choose how to add the contact", + "name": "Name", + "easyIdentification": "For easy identification", + "share": "Share", + "enterName": "Enter your name", + "editContactName": "Edit Contact Name", + "noContactYet": "No Contacts Yet", + "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } } diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts new file mode 100644 index 000000000..a819b4f2b --- /dev/null +++ b/src/hooks/useChatPeer.ts @@ -0,0 +1,400 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useQuery } from '@realm/react'; +import ChatPeerManager from '../services/p2p/ChatPeerManager'; +import { Message, Community, Contact, CommunityType } from '../services/p2p/interface'; +import { RealmSchema } from '../storage/realm/enum'; +import { captureError } from '../services/sentry'; +import dbManager from '../storage/realm/dbManager'; +import { getJSONFromRealmObject } from '../storage/realm/utils'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import Relay from 'src/services/backend/Relay'; + +export interface UseChatPeerOptions { + autoInit?: boolean; + enableMessageListener?: boolean; + enableConnectionListener?: boolean; +} + +export interface UseChatPeerReturn { + // State + isInitialized: boolean; + isLoading: boolean; + error: string | null; + communities: Community[]; + contacts: Contact[]; + peers: any[]; + + // Chat operations + initChatPeer: () => Promise; + sendMessage: (pubKey: string, message: string) => Promise; + joinPeer: (pubKey: string) => Promise; + loadPendingMessages: (lastBlock?: number) => Promise; + + // Data fetching + getPeers: () => Promise; + getKeys: () => { + [key: string]: string; + }; + getPeerMessages: (pubKey: string, lastBlock: number) => Promise; + + // Message operations (lazy loaded) + getMessagesByCommunity: (communityId: string) => Message[]; + getAllMessages: () => Message[]; + getUnreadMessages: () => Message[]; + getUnreadCount: (communityId?: string) => number; + markMessageAsRead: (messageId: string) => Promise; + markCommunityAsRead: (communityId: string) => Promise; + + // Community helpers + getPeerCommunities: () => Community[]; + getCommunityById: (communityId: string) => Community | null; + + // Contact helpers + getContactByKey: (contactKey: string) => Contact | null; + + // Listeners + setMessageListener: (callback: (data: any) => void) => void; + setConnectionListener: (callback: (data: any) => void) => void; +} + +export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn => { + const { + autoInit = false, + enableMessageListener = true, + enableConnectionListener = true, + } = options; + + // State + const [isInitialized, setIsInitialized] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const [peers, setPeers] = useState([]); + + // Realm queries - only for communities and contacts, not messages + const allCommunities = useQuery(RealmSchema.Community); + const allContacts = useQuery(RealmSchema.Contact); + const keeperApp: KeeperApp = useQuery(RealmSchema.KeeperApp).map( + getJSONFromRealmObject + )[0] as any; + + // Convert Realm objects to plain objects + const communities: Community[] = allCommunities.map((c) => (c.toJSON ? c.toJSON() : c)) as any; + const contacts: Contact[] = allContacts.map((c) => (c.toJSON ? c.toJSON() : c)) as any; + + const chatManager = ChatPeerManager.getInstance(); + + // Initialize chat peer + const initChatPeer = useCallback(async (): Promise => { + try { + setIsLoading(true); + setError(null); + + const success = await chatManager.init(keeperApp.primarySeed); + console.log('success', success); + setIsInitialized(success); + + if (success && enableMessageListener) { + chatManager.setOnMessageListener((data) => { + console.log('New message received:', data); + }); + } + + if (success && enableConnectionListener) { + chatManager.setOnConnectionListener((data) => { + console.log('New connection:', data); + }); + } + + // Get keys from ChatPeerManager and update KeeperApp if contactsKey is missing + if (!keeperApp?.contactsKey?.secretKey) { + const keys = await chatManager.getKeys(); + if (keys && keys.publicKey && keys.secretKey) { + const updated = await Relay.updateContactsKey(keeperApp.id, keys.secretKey); + console.log('updated', updated); + if (updated) { + await dbManager.updateObjectById(RealmSchema.KeeperApp, keeperApp.id, { + contactsKey: { + publicKey: keys.publicKey, + secretKey: keys.secretKey, + }, + }); + } + } + } + + return success; + } catch (err) { + console.log('err', err); + setError(err instanceof Error ? err.message : 'Failed to initialize chat peer'); + captureError(err); + return false; + } finally { + setIsLoading(false); + } + }, [keeperApp, enableMessageListener, enableConnectionListener]); + + // Send message + const sendMessage = useCallback( + async (pubKey: string, message: string): Promise => { + try { + setError(null); + if (!isInitialized) { + const initSuccess = await initChatPeer(); + if (!initSuccess) { + throw new Error('Failed to initialize chat peer before sending message'); + } + } + + return await chatManager.sendMessage(pubKey, message); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to send message'); + captureError(err); + throw err; + } + }, + [isInitialized, initChatPeer] + ); + + const joinPeer = useCallback( + async (pubKey: string): Promise => { + try { + setError(null); + if (!isInitialized) { + const initSuccess = await initChatPeer(); + if (!initSuccess) { + throw new Error('Failed to initialize chat peer before joining peer'); + } + } + + return await chatManager.joinPeers(pubKey); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to join peer'); + captureError(err); + throw err; + } + }, + [isInitialized, initChatPeer] + ); + + const loadPendingMessages = useCallback(async (lastBlock = 0): Promise => { + try { + setError(null); + await chatManager.loadPendingMessages(lastBlock); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load pending messages'); + captureError(err); + } + }, []); + + const getPeers = useCallback(async (): Promise => { + try { + setError(null); + if (!isInitialized) { + const initSuccess = await initChatPeer(); + if (!initSuccess) { + throw new Error('Failed to initialize chat peer before getting peers'); + } + } + + const result = await chatManager.getPeers(); + setPeers(result); + return result; + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get peers'); + captureError(err); + throw err; + } + }, [isInitialized, initChatPeer]); + + const getKeys = useCallback((): { + [key: string]: string; + } => { + try { + setError(null); + if (keeperApp.contactsKey) return keeperApp.contactsKey; + // chatPeer.getKeys() is only used during initialization for fetching the + // keys directly from the chat peer manager and storing them in KeeperApp + else throw new Error('Contacts key not found. Initialize chat peer first.'); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get keys'); + captureError(err); + throw err; + } + }, []); + + // Get peer messages + const getPeerMessages = useCallback(async (pubKey: string, lastBlock: number): Promise => { + try { + setError(null); + return await chatManager.getPeerMessages(pubKey, lastBlock); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get peer messages'); + captureError(err); + throw err; + } + }, []); + + // Lazy-loaded message operations + const getAllMessages = useCallback((): Message[] => { + try { + const allMessages = dbManager.getCollection(RealmSchema.Message); + return allMessages.map((m: any) => (m.toJSON ? m.toJSON() : m)) as Message[]; + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get messages'); + captureError(err); + return []; + } + }, []); + + const getMessagesByCommunity = useCallback( + (communityId: string): Message[] => { + try { + const allMessages = getAllMessages(); + return allMessages.filter((message) => message.communityId === communityId); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get messages by community'); + captureError(err); + return []; + } + }, + [getAllMessages] + ); + + const getUnreadMessages = useCallback((): Message[] => { + try { + const allMessages = getAllMessages(); + return allMessages.filter((message) => message.unread); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get unread messages'); + captureError(err); + return []; + } + }, [getAllMessages]); + + const getUnreadCount = useCallback( + (communityId?: string): number => { + try { + const unreadMessages = getUnreadMessages(); + if (communityId) { + return unreadMessages.filter((message) => message.communityId === communityId).length; + } + return unreadMessages.length; + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to get unread count'); + captureError(err); + return 0; + } + }, + [getUnreadMessages] + ); + + // Mark message as read + const markMessageAsRead = useCallback(async (messageId: string): Promise => { + try { + setError(null); + await dbManager.updateObjectById(RealmSchema.Message, messageId, { unread: false }); + return true; + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to mark message as read'); + captureError(err); + return false; + } + }, []); + + // Mark all messages in a community as read + const markCommunityAsRead = useCallback( + async (communityId: string): Promise => { + try { + setError(null); + const communityMessages = getMessagesByCommunity(communityId); + const unreadMessages = communityMessages.filter((msg) => msg.unread); + + for (const message of unreadMessages) { + await dbManager.updateObjectById(RealmSchema.Message, message.id, { unread: false }); + } + return true; + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to mark community as read'); + captureError(err); + return false; + } + }, + [getMessagesByCommunity] + ); + + // Community helpers + const getPeerCommunities = useCallback((): Community[] => { + return communities.filter((community) => community.type === CommunityType.Peer); + }, [communities]); + + const getCommunityById = useCallback( + (communityId: string): Community | null => { + return communities.find((community) => community.id === communityId) || null; + }, + [communities] + ); + + // Contact helpers + const getContactByKey = useCallback( + (contactKey: string): Contact | null => { + return contacts.find((contact) => contact.contactKey === contactKey) || null; + }, + [contacts] + ); + + // Set listeners + const setMessageListener = useCallback((callback: (data: any) => void): void => { + chatManager.setOnMessageListener(callback); + }, []); + + const setConnectionListener = useCallback((callback: (data: any) => void): void => { + chatManager.setOnConnectionListener(callback); + }, []); + + // Auto-initialize if requested + useEffect(() => { + if (autoInit && !isInitialized) { + initChatPeer(); + } + }, [autoInit, isInitialized, initChatPeer]); + + return { + // State + isInitialized, + isLoading, + error, + communities, + contacts, + peers, + + // Chat operations + initChatPeer, + sendMessage, + joinPeer, + loadPendingMessages, + + // Data fetching + getPeers, + getKeys, + getPeerMessages, + + // Message operations (lazy loaded) + getMessagesByCommunity, + getAllMessages, + getUnreadMessages, + getUnreadCount, + markMessageAsRead, + markCommunityAsRead, + + // Community helpers + getPeerCommunities, + getCommunityById, + + // Contact helpers + getContactByKey, + + // Listeners + setMessageListener, + setConnectionListener, + }; +}; diff --git a/src/models/interfaces/KeeperApp.ts b/src/models/interfaces/KeeperApp.ts index 73dcc1165..fe1081151 100644 --- a/src/models/interfaces/KeeperApp.ts +++ b/src/models/interfaces/KeeperApp.ts @@ -20,4 +20,6 @@ export interface KeeperApp { backup: AppBackup; subscription: SubScription; enableAnalytics: boolean; + contactsKey?: { [key: string]: string }; + profilePicture?: string; } diff --git a/src/navigation/AppStateHandler.tsx b/src/navigation/AppStateHandler.tsx index 342f2ab15..d439b608d 100644 --- a/src/navigation/AppStateHandler.tsx +++ b/src/navigation/AppStateHandler.tsx @@ -3,6 +3,7 @@ import { AppState, Linking } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { useDispatch } from 'react-redux'; import { setHasDeepLink } from 'src/store/reducers/login'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; const PASSCODE_TIMEOUT = 5 * 60 * 1000; @@ -33,6 +34,7 @@ const AppStateHandler = () => { Date.now() - lastBackgroundTime > PASSCODE_TIMEOUT ) { dispatch(setHasDeepLink(triggeredDeepLink)); + ChatPeerManager.resetInstance(); navigation.reset({ index: 0, routes: [ diff --git a/src/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index b6726cc9c..7e5a7b2b4 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -156,7 +156,9 @@ import UsdtTransactionHistory from 'src/screens/USDT/UsdtTransactionHistory'; import Usdtsetting from 'src/screens/USDT/Usdtsetting'; import { CloudBackupPassword } from 'src/screens/CloudBackup/CloudBackupPassword'; import { ImportedWalletSetup } from 'src/screens/Vault/ImportedWalletSetup'; -import KeeperSupport from 'src/screens/KeeperConcierge/KeeperSupport'; +import ChatRoomScreen from 'src/screens/Home/components/Contact/ChatRoomScreen'; +import KeeperSupport from 'src/screens/Concierge/KeeperSupport'; +import ContactShareQr from 'src/screens/Home/components/Contact/ContactShowQr'; import Advisors from 'src/screens/Advisors/Advisors'; import FilterAdvisor from 'src/screens/Advisors/FilterAdvisor'; import AdvisorDetail from 'src/screens/Advisors/AdvisorDetail'; @@ -285,6 +287,9 @@ function AppStack() { + + + diff --git a/src/navigation/privateTheme.js b/src/navigation/privateTheme.js index 9c1f0a8c4..eecab78fe 100644 --- a/src/navigation/privateTheme.js +++ b/src/navigation/privateTheme.js @@ -173,6 +173,10 @@ export const privateTheme = extendTheme({ activationFeeText: Colors.secondaryLightGrey, greybeige: Colors.graybeige, AcquireText: Colors.goldenGradient, + stoneGrey: Colors.stoneGrey, + coolGrey: Colors.coolGrey, + subchatText: Colors.primaryBrown, + SlateWhite: Colors.DarkSlateGray, }, dark: { modalWhiteContent: Colors.headerWhite, @@ -298,6 +302,10 @@ export const privateTheme = extendTheme({ activationFeeText: Colors.bodyText, greybeige: Colors.secondaryBlack, AcquireText: Colors.bodyText, + stoneGrey: Colors.lightstone, + coolGrey: Colors.bodyText, + subchatText: Colors.primaryCream, + SlateWhite: Colors.primaryCream, }, }, config: { diff --git a/src/navigation/themes.js b/src/navigation/themes.js index 755cc360c..bd04ecfe8 100644 --- a/src/navigation/themes.js +++ b/src/navigation/themes.js @@ -171,6 +171,10 @@ export const customTheme = extendTheme({ activationFeeText: Colors.secondaryLightGrey, greybeige: Colors.graybeige, AcquireText: Colors.primaryGreen, + stoneGrey: Colors.stoneGrey, + coolGrey: Colors.coolGrey, + subchatText: Colors.primaryBrown, + SlateWhite: Colors.DarkSlateGray, }, dark: { modalWhiteContent: Colors.headerWhite, @@ -294,6 +298,10 @@ export const customTheme = extendTheme({ activationFeeText: Colors.bodyText, greybeige: Colors.secondaryBlack, AcquireText: Colors.bodyText, + stoneGrey: Colors.lightstone, + coolGrey: Colors.bodyText, + subchatText: Colors.primaryCream, + SlateWhite: Colors.primaryCream, }, }, config: { diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 4f0e86a4f..3a6f23b2a 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -53,6 +53,7 @@ const Advisors = () => { { // // } /> - + {/* - + */} - + {/* {concierge.meetExperts} - + */} { + const { colorMode } = useColorMode(); + const navigation = useNavigation(); + + return ( + + + + {/* adjust the route accordingly */} + + + + ); +}; + +export default KeeperSupport; +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: wp(10), + }, +}); diff --git a/src/screens/Home/HomeScreen.tsx b/src/screens/Home/HomeScreen.tsx index bd93b1f04..ebaa6f18b 100644 --- a/src/screens/Home/HomeScreen.tsx +++ b/src/screens/Home/HomeScreen.tsx @@ -22,7 +22,7 @@ import TickIcon from 'src/assets/images/icon_tick.svg'; import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; import ThemedColor from 'src/components/ThemedColor/ThemedColor'; import BuyBtc from './components/buyBtc/BuyBtc'; -import ConciergeComponent from './components/ConciergeComponent'; +import Contact from './components/Contact/component/Contact'; function NewHomeScreen({ route }) { const { colorMode } = useColorMode(); @@ -37,7 +37,12 @@ function NewHomeScreen({ route }) { useAppSelector((state) => state.bhr); const { showToast } = useToastMessage(); const { translations } = useContext(LocalizationContext); - const { home: homeTranslation, wallet: walletText, buyBTC: buyBTCText } = translations; + const { + home: homeTranslation, + wallet: walletText, + buyBTC: buyBTCText, + contactText, + } = translations; const [selectedOption, setSelectedOption] = useState( selectedOptionFromRoute || walletText.homeWallets ); @@ -95,11 +100,11 @@ function NewHomeScreen({ route }) { /> ), }; - case walletText.concierge: + case contactText.contacts: return { content: ( - + ), icon: ( @@ -107,7 +112,7 @@ function NewHomeScreen({ route }) { width={wp(39)} icon={ { + const { colorMode } = useColorMode(); + const route = useRoute>(); + const { communityId } = route.params; + const [openEditModal, setOpenEditModal] = useState(false); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; + const app: KeeperApp = useQuery(RealmSchema.KeeperApp)[0]; + const community = useObject(RealmSchema.Community, communityId); + const messages = useQuery(RealmSchema.Message) + .filtered('communityId = $0', communityId) + .sorted('createdAt', true); + + const [editUserProfileImage, setEditUserProfileImage] = useState(''); + const [editReceiverProfileName, setEditReceiverProfileName] = useState(community.name); + + return ( + + + + setOpenEditModal(false)} + title={contactText?.editContactName} + textColor={`${colorMode}.textGreen`} + modalBackground={`${colorMode}.modalWhiteBackground`} + Content={() => ( + <> + + + )} + /> + + ); +}; + +export default ChatRoomScreen; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); diff --git a/src/screens/Home/components/Contact/ContactShowQr.tsx b/src/screens/Home/components/Contact/ContactShowQr.tsx new file mode 100644 index 000000000..5e5bd0978 --- /dev/null +++ b/src/screens/Home/components/Contact/ContactShowQr.tsx @@ -0,0 +1,45 @@ +import { Box, useColorMode } from 'native-base'; +import React, { useContext } from 'react'; +import { StyleSheet } from 'react-native'; +import KeeperQRCode from 'src/components/KeeperQRCode'; +import ScreenWrapper from 'src/components/ScreenWrapper'; +import WalletHeader from 'src/components/WalletHeader'; +import { windowWidth } from 'src/constants/responsive'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; +import ReceiveAddress from 'src/screens/Recieve/ReceiveAddress'; + +const ContactShareQr = ({ route }) => { + const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { vault: vaultText, settings } = translations; + const { data } = route.params; + + return ( + + + + + + + + + + ); +}; + +export default ContactShareQr; + +const styles = StyleSheet.create({ + qrContainer: { + alignItems: 'center', + alignSelf: 'center', + width: windowWidth * 0.8, + borderRadius: 10, + padding: 22, + }, +}); diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx new file mode 100644 index 000000000..b2f2f05b3 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -0,0 +1,249 @@ +import React, { useContext, useMemo } from 'react'; +import { FlatList, StyleSheet, TouchableOpacity } from 'react-native'; +import { Box, Image, useColorMode } from 'native-base'; +import Text from 'src/components/KeeperText'; +import { windowWidth, wp } from 'src/constants/responsive'; +import ChatPlaceHolderIcon from 'src/assets/images/chat-plaaceholde-image.svg'; +import { useNavigation } from '@react-navigation/native'; +import Fonts from 'src/constants/Fonts'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { CommunityType } from 'src/services/p2p/interface'; +import { useChatPeer } from 'src/hooks/useChatPeer'; + +interface ChatItemData { + id: string; + name: string; + lastMessage: string; + image: string; + date: string; + message_count: number; + communityId: string; + contactKey?: string; +} + +const PlaceHolderChat = () => { + const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; + return ( + + + {contactText.noContactYet} + + + {contactText.noContactDesc} + + + ); +}; + +const ChatItem = ({ item, userProfileImage }: { item: ChatItemData; userProfileImage: any }) => { + const { colorMode } = useColorMode(); + const navigation = useNavigation(); + const formatTime = (dateString) => { + const date = new Date(dateString); + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + }; + + const handlePress = () => { + navigation.navigate('ChatRoomScreen', { communityId: item.communityId }); + }; + + return ( + + + + {item.image ? ( + + ) : ( + + )} + + + + {item.name} + + + {item.lastMessage.length > 25 + ? item.lastMessage.slice(0, 25) + '...' + : item.lastMessage} + + + + + {formatTime(item.date)} + {item.message_count > 0 && item.lastMessage !== 'Start of conversation' && ( + + + {item.message_count} + + + )} + + + + ); +}; + +const ChatList = ({ + userProfileImage, + communities, +}: { + userProfileImage: any; + communities: any; +}) => { + const { colorMode } = useColorMode(); + const { getAllMessages, getMessagesByCommunity, getUnreadCount, getContactByKey } = useChatPeer(); + + // Transform communities into chat list data + const chatData = useMemo(() => { + const chatItems: ChatItemData[] = []; + + communities.forEach((community) => { + // Get messages for this community + const messages = getMessagesByCommunity(community.id); + + if (messages.length === 0) { + // No messages yet, show community anyway for Peer communities + if (community.type === CommunityType.Peer && community.with) { + const contact = getContactByKey(community.with); + chatItems.push({ + id: community.id, + name: contact?.name || community.name, + lastMessage: 'No messages yet', + image: contact?.imageUrl || '', + date: new Date(community.createdAt).toISOString(), + message_count: 0, + communityId: community.id, + contactKey: community.with, + }); + } else if (community.type !== CommunityType.Peer) { + // Group or Broadcast communities + chatItems.push({ + id: community.id, + name: community.name, + lastMessage: 'No messages yet', + image: '', + date: new Date(community.createdAt).toISOString(), + message_count: 0, + communityId: community.id, + }); + } + return; + } + + // Get the latest message + const latestMessage = messages.reduce((latest, current) => + current.createdAt > latest.createdAt ? current : latest + ); + + // Get unread count for this community + const unreadCount = getUnreadCount(community.id); + + // Determine display name and image based on community type + let displayName = community.name; + let displayImage = ''; + let contactKey: string | undefined; + + if (community.type === CommunityType.Peer && community.with) { + const contact = getContactByKey(community.with); + displayName = contact?.name || community.name; + displayImage = contact?.imageUrl || ''; + contactKey = community.with; + } + + chatItems.push({ + id: community.id, + name: displayName, + lastMessage: latestMessage.text, + image: displayImage, + date: new Date(latestMessage.createdAt).toISOString(), + message_count: unreadCount, + communityId: community.id, + contactKey, + }); + }); + + // Sort by latest message date + return chatItems.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + }, [communities, getAllMessages, getMessagesByCommunity, getUnreadCount, getContactByKey]); + + const renderItem = ({ item }: { item: ChatItemData }) => ( + + ); + + return ( + + item?.id} + renderItem={renderItem} + ListEmptyComponent={} + showsVerticalScrollIndicator={false} + /> + + ); +}; + +export default ChatList; + +const styles = StyleSheet.create({ + profile_container: { + flexDirection: 'row', + borderBottomWidth: 1, + borderRadius: wp(10), + paddingBottom: wp(18), + justifyContent: 'space-between', + width: '100%', + marginBottom: wp(20), + }, + profile_image_container: { + flexDirection: 'row', + gap: wp(10), + alignItems: 'center', + justifyContent: 'center', + }, + profile_image: { + width: wp(44), + height: wp(44), + borderRadius: wp(75), + }, + edit_icon: { + alignItems: 'flex-end', + gap: wp(10), + }, + nameText: { + fontSize: wp(15), + marginBottom: wp(8), + maxWidth: windowWidth * 0.8, + }, + messageText: { + fontSize: wp(12), + }, + message_count: { + width: wp(15), + height: wp(15), + borderRadius: wp(50), + justifyContent: 'center', + alignItems: 'center', + }, + placeHolderContainer: { + justifyContent: 'center', + alignItems: 'center', + marginTop: wp(20), + }, + placeHolderText: { + fontSize: wp(18), + marginBottom: wp(10), + fontFamily: Fonts.LoraMedium, + }, + placeholderDescription: { + fontSize: wp(13), + textAlign: 'center', + paddingHorizontal: wp(20), + }, +}); diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx new file mode 100644 index 000000000..1fc37e6ef --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -0,0 +1,340 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { + Box, + VStack, + HStack, + ScrollView, + Input, + Image, + useColorMode, + KeyboardAvoidingView, +} from 'native-base'; +import moment from 'moment'; +import Text from 'src/components/KeeperText'; +import { Platform, StyleSheet, TouchableOpacity } from 'react-native'; +import { hp, wp } from 'src/constants/responsive'; +// import PlusIcon from 'src/assets/images/plus-green-icon.svg'; +// import PlusWhiteIcon from 'src/assets/images/add-plus-white.svg'; +import SendWhiteIcon from 'src/assets/images/send-white-icon.svg'; +import dbManager from 'src/storage/realm/dbManager'; +import { RealmSchema } from 'src/storage/realm/enum'; +import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptionManager'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import { v4 as uuidv4 } from 'uuid'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; +import ChatPlaceHolderIcon from 'src/assets/images/chat-plaaceholde-image.svg'; + +const groupMessagesByDate = (msgs) => { + const groups = {}; + + const filteredMessages = msgs.filter((msg) => msg.type !== 'Alert'); + const sortedMessages = filteredMessages.sort((a, b) => a.createdAt - b.createdAt); + sortedMessages.forEach((msg) => { + const date = moment(msg.createdAt); + const label = moment().isSame(date, 'day') + ? 'Today' + : moment().subtract(1, 'day').isSame(date, 'day') + ? 'Yesterday' + : date.format('MMMM D, YYYY'); + + if (!groups[label]) groups[label] = []; + groups[label].push(msg); + }); + + return groups; +}; + +const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community }) => { + const [inputValue, setInputValue] = useState(''); + const groupedMessages = groupMessagesByDate(messages); + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + const scrollRef = useRef(null); + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp) as any as KeeperApp; + const chatManager = ChatPeerManager.getInstance(); + const handleSend = async () => { + if (!inputValue.trim()) return; + + const text = inputValue.trim(); + setInputValue(''); + + try { + const messageData = { + id: uuidv4(), + text, + createdAt: Date.now(), + type: 'TEXT', + sender: app.contactsKey.publicKey, + communityId: community.id, + unread: false, + fileUrl: '', + }; + + dbManager.createObject(RealmSchema.Message, messageData); + + const encryptedMessage = ChatEncryptionManager.encryptMessage( + JSON.stringify({ ...messageData }), + community.key + ); + + await chatManager.sendMessage( + community.with, + JSON.stringify({ ...encryptedMessage, communityId: community.id }) + ); + } catch (error) { + console.error('Error sending message:', error); + setInputValue(text); + } + + setTimeout(() => { + scrollRef?.current?.scrollToEnd({ animated: true }); + }, 0); + }; + + useEffect(() => { + setTimeout(() => { + scrollRef?.current?.scrollToEnd({ animated: true }); + }, 100); + }, [messages, scrollRef]); + + return ( + + + + + {Object.entries(groupedMessages).map(([dateLabel, msgs]) => ( + + {/* Date Label */} + + + + {dateLabel} + + + + {(msgs as any[]).map((msg, index) => { + const thisMoment = moment(msg.createdAt); + const nextMsg = (msgs as any[])[index + 1]; + const nextMoment = nextMsg ? moment(nextMsg.createdAt) : null; + const isMyMessage = msg.sender === app.contactsKey.publicKey; + + const isLastInMinuteAndSenderGroup = + !nextMoment || + !thisMoment.isSame(nextMoment, 'minute') || + msg.sender !== nextMsg.sender; + + const prevMsg = (msgs as any[])[index - 1]; + const prevMoment = prevMsg ? moment(prevMsg.createdAt) : null; + + const isFirstInGroup = + !prevMoment || + !thisMoment.isSame(prevMoment, 'minute') || + msg.sender !== prevMsg.sender; + + return ( + + {!isMyMessage && + isFirstInGroup && + (receiverProfileImage ? ( + + ) : ( + + ))} + + + + + {msg.text} + + + + {isLastInMinuteAndSenderGroup && ( + + {thisMoment.format('hh:mm A')} + + )} + + + {isMyMessage && + isLastInMinuteAndSenderGroup && + (userProfileImage ? ( + + ) : ( + + ))} + + ); + })} + + ))} + + + + + + {/* For the future if needed */} + {/* {isDarkMode ? ( + + ) : ( + + )} + */} + + + + + + + + + + + ); +}; + +export default ChatRoom; + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: wp(20), + paddingBottom: wp(10), + }, + scrollView: { + flex: 1, + }, + scrollContent: { + paddingHorizontal: wp(22), + paddingBottom: wp(20), + }, + dateLabel: { + textAlign: 'center', + fontSize: 12, + marginVertical: hp(12), + }, + messageRow: { + marginVertical: 2, + }, + messageBubble: { + maxWidth: '90%', + borderRadius: 12, + }, + timestamp: { + fontSize: 10, + marginTop: hp(4), + }, + avatar: { + borderRadius: 999, + width: wp(25), + height: wp(25), + }, + inputContainer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + inputBar: { + flexDirection: 'row', + // paddingLeft: wp(12), + paddingRight: wp(5), + paddingVertical: hp(5), + marginBottom: wp(10), + width: '90%', + borderTopWidth: 1, + alignItems: 'center', + justifyContent: 'center', + borderWidth: 1, + borderRadius: 15, + }, + dateLine: { + borderBottomWidth: 1, + flex: 1, + }, + messageText: { + fontSize: 13, + lineHeight: 19, + }, + messageContent: { + paddingVertical: wp(10), + paddingHorizontal: wp(14), + borderRadius: 12, + borderWidth: 1, + }, + seperator: { + height: 30, + borderLeftWidth: 0.2, + paddingHorizontal: 0.4, + opacity: 0.5, + marginLeft: wp(12), + }, + sendButton: { + width: wp(40), + height: wp(40), + borderRadius: 10, + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx new file mode 100644 index 000000000..f6bb30b85 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx @@ -0,0 +1,97 @@ +import { useNavigation } from '@react-navigation/native'; +import { Box, Image, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import Text from 'src/components/KeeperText'; +import { hp, wp } from 'src/constants/responsive'; +import ChatPlaceHolderIcon from 'src/assets/images/chat-plaaceholde-image.svg'; +import { StatusBar } from 'react-native'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; + +const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEditModal }) => { + const navigation = useNavigation(); + const { colorMode } = useColorMode(); + + return ( + + + + + + + + + + {receiverProfileImage ? ( + + ) : ( + + )} + + + + + {receiverProfileName} + + + + + + + ); +}; + +export default ChatRoomHeader; + +const styles = StyleSheet.create({ + container: { + width: '100%', + paddingTop: wp(15), + }, + backButton: { + height: hp(44), + width: wp(44), + justifyContent: 'center', + alignItems: 'center', + }, + wrapper: { + paddingHorizontal: wp(5), + height: hp(127), + width: '100%', + justifyContent: 'flex-end', + position: 'relative', + borderBottomWidth: 1, + }, + WrapperContainer: { + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + marginBottom: hp(24), + }, + profile_image_container: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + profile_image: { + width: wp(47), + height: wp(47), + borderRadius: wp(75), + }, + text: { + fontSize: wp(18), + lineHeight: 26, + fontFamily: 'Lora-Medium', + marginLeft: wp(14), + marginRight: wp(5), + }, +}); diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx new file mode 100644 index 000000000..981cae31d --- /dev/null +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -0,0 +1,287 @@ +import { Box, ScrollView, useColorMode } from 'native-base'; +import React, { useContext, useEffect, useState } from 'react'; +import { RefreshControl, StyleSheet, TouchableOpacity } from 'react-native'; +import Text from 'src/components/KeeperText'; +import { wp } from 'src/constants/responsive'; +import ContactHeader from './ContactHeader'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import ConciergeIcon from 'src/assets/images/faqWhiteIcon.svg'; +import Colors from 'src/theme/Colors'; +import PinIcon from 'src/assets/images/Pin.svg'; +import ChatList from './ChatList'; +import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; +import KeeperModal from 'src/components/KeeperModal'; +import ProfileContent from './ProfileContent'; +import { CommonActions, useNavigation } from '@react-navigation/native'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; +import useToastMessage from 'src/hooks/useToastMessage'; +import ToastErrorIcon from 'src/assets/images/toast_error.svg'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import { RealmSchema } from 'src/storage/realm/enum'; +import dbManager from 'src/storage/realm/dbManager'; +import { CommunityType, MessageType, Message } from 'src/services/p2p/interface'; +import { hash256 } from 'src/utils/service-utilities/encryption'; +import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptionManager'; +import { v4 as uuidv4 } from 'uuid'; +import { useQuery } from '@realm/react'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; +import { ContactsCta } from './ContactsCta'; +import { screenWidth } from 'react-native-gifted-charts/src/utils'; +import HireAdvisorIcon from 'src/assets/images/hire-advisor.svg'; +import ActivityIndicatorView from 'src/components/AppActivityIndicator/ActivityIndicatorView'; + +const Contact = () => { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + const [createProfile, setCreateProfile] = useState(false); + const [userProfileImage, setUserProfileImage] = useState(null); + const [userProfileName, setUserProfileName] = useState(''); + const navigation = useNavigation(); + const { translations } = useContext(LocalizationContext); + const { contactText, concierge } = translations; + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); + const lastBlock = useQuery(RealmSchema.Message).sorted('block', true)[0]?.block; + const communities = useQuery(RealmSchema.Community); + const { showToast } = useToastMessage(); + const chatManager = ChatPeerManager.getInstance(); + const [refreshing, setRefreshing] = useState(false); + const [loading, setLoading] = useState(!ChatPeerManager.isConnected); + + const initializeChat = async () => { + try { + if (ChatPeerManager.isConnected) return; + const chatPeerInitialized = await chatManager.init(app.primarySeed); + if (!chatPeerInitialized) { + throw new Error(); + } + setLoading(false); + } catch (error) { + console.error('Error initializing chat peer:', error); + showToast('Chat Peer initialization failed', ); + return; + } + }; + useEffect(() => { + if (app.profilePicture !== 'undefined' || app.appName !== 'undefined') { + setUserProfileImage(app.profilePicture); + setUserProfileName(app.appName); + } + }, [app.profilePicture, app.appName]); + + const onRefresh = () => { + setRefreshing(true); + chatManager.loadPendingMessages(lastBlock); + setRefreshing(false); + }; + + useEffect(() => { + if (!ChatPeerManager.isInitialized) initializeChat(); + else chatManager.loadPendingMessages(lastBlock); + }, [ChatPeerManager.isInitialized]); + + const onQrScan = (data) => { + if (data.startsWith('keeper://')) { + const urlParts = data.split('/'); + const path = urlParts[2]; + if (path === 'contact') { + initChat(urlParts[3], urlParts[4], urlParts[5]); + navigation.goBack(); + } + } else { + showToast('Invalid QR code', ); + } + }; + + const initChat = async (peerKey: string, publicKey: string, name: string) => { + try { + const communityId = hash256([app.contactsKey.publicKey, peerKey].sort().join('-')); + const sessionKeys = ChatEncryptionManager.generateSessionKeys(); + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + app.contactsKey.secretKey, + publicKey + ); + const pubKey = ChatEncryptionManager.derivePublicKey(app.contactsKey.secretKey); + const encryptedKeys = ChatEncryptionManager.encryptKeys(sessionKeys.aesKey, sharedSecret); + const community = dbManager.getObjectByPrimaryId(RealmSchema.Community, 'id', communityId); + if (!community) { + dbManager.createObject(RealmSchema.Community, { + id: communityId, + communityId: communityId, + name: name || `Unknown Contact (${peerKey.substring(0, 8)})`, + createdAt: Date.now(), + type: CommunityType.Peer, + with: peerKey, + key: sessionKeys.aesKey, + }); + const message = { + id: uuidv4(), + communityId: communityId, + type: MessageType.Alert, + text: `Start of conversation`, + createdAt: Date.now(), + sender: app.contactsKey.publicKey, + senderName: app.appName, + unread: false, + encryptedKeys: encryptedKeys, + pubKey: pubKey, + }; + + chatManager.sendMessage(peerKey, JSON.stringify({ ...message, communityId })); + dbManager.createObject(RealmSchema.Message, message); + showToast('New Contact added', false); + } + } catch (error) { + console.error('Error initializing chat:', error); + } + }; + + return ( + + + {/* can be used in the future */} + {/* + + + {contactText.recentChats} + + */} + } + showsVerticalScrollIndicator={false} + > + + { + navigation.dispatch(CommonActions.navigate({ name: 'keeperSupport' })); + }} + > + + } + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + + {contactText.keeperSupport} + + + {contactText.hereToHelp} + + + + + {/* place a time here */} + + 10:45 AM + + + + + + + + + { + const pubKey = ChatEncryptionManager.derivePublicKey(app?.contactsKey?.secretKey); + const contactShareLink = `keeper://contact/${app.contactsKey.publicKey}/${pubKey}/${app.appName}`; + navigation.dispatch( + CommonActions.navigate({ + name: 'ScanQR', + params: { + title: 'Scan QR', + subtitle: 'Scan QR', + onQrScan, + importOptions: false, + isSingning: true, + contactShareData: contactShareLink, + isPSBT: true, + }, + }) + ); + }} + text={contactText.addContact} + LeftIcon={ContactAddicon} + width={screenWidth * 0.43} + /> + navigation.dispatch(CommonActions.navigate('Advisors'))} + text={concierge.hireAdvisor} + LeftIcon={HireAdvisorIcon} + width={screenWidth * 0.43} + backgroundColor={isDarkMode ? Colors.headerWhite : 'transparent'} + borderColor={`${colorMode}.pantoneGreen`} + primaryTextColor={`${colorMode}.pantoneGreen`} + /> + + + setCreateProfile(false)} + title={contactText.editProfile} + subTitle={contactText.editProfileDesc} + textColor={`${colorMode}.textGreen`} + subTitleColor={`${colorMode}.modalSubtitleBlack`} + modalBackground={`${colorMode}.modalWhiteBackground`} + Content={() => ( + + )} + /> + + ); +}; + +export default Contact; + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingHorizontal: wp(20), + }, + + chat_heading: { + marginBottom: wp(20), + }, + concierge_container: { + marginBottom: wp(20), + borderBottomWidth: 1, + paddingBottom: wp(20), + }, + concierge: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + concierge_icon: { + flexDirection: 'row', + alignItems: 'center', + gap: wp(10), + }, + concierge_text: { + gap: wp(6), + }, + pinContainer: { + justifyContent: 'center', + alignItems: 'center', + gap: wp(6), + }, + bottomButton: { + marginTop: wp(10), + marginBottom: wp(15), + flexDirection: 'row', + width: '100%', + justifyContent: 'space-between', + }, +}); diff --git a/src/screens/Home/components/Contact/component/ContactHeader.tsx b/src/screens/Home/components/Contact/component/ContactHeader.tsx new file mode 100644 index 000000000..fd1964160 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -0,0 +1,112 @@ +import { Box, Image, useColorMode } from 'native-base'; +import React, { useContext } from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import Text from 'src/components/KeeperText'; +import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; +import EditIcon from 'src/assets/images/contact-edit.svg'; +import { windowWidth, wp } from 'src/constants/responsive'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; + +type Props = { + userProfileImage: string | null; + userProfileName: string; + setCreateProfile: (visible: boolean) => void; +}; + +const ContactHeader = ({ userProfileImage, userProfileName, setCreateProfile }: Props) => { + const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; + + return ( + + + + {userProfileImage ? ( + + ) : ( + + )} + + + {userProfileName || contactText.name} + + {!userProfileName && ( + + {contactText.easyIdentification} + + )} + + + { + setCreateProfile(true); + }} + style={styles.edit_icon} + > + + + + + ); +}; + +export default ContactHeader; + +const styles = StyleSheet.create({ + header: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + marginBottom: wp(20), + }, + profile_container: { + flexDirection: 'row', + borderWidth: 1, + borderRadius: wp(10), + padding: wp(15), + justifyContent: 'space-between', + width: windowWidth * 0.9, + }, + text: { + fontSize: 14, + lineHeight: 22, + }, + profile_image_container: { + flexDirection: 'row', + gap: wp(10), + alignItems: 'center', + justifyContent: 'center', + }, + profile_image: { + width: wp(47), + height: wp(47), + borderRadius: wp(75), + }, + edit_icon: { + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'row', + gap: wp(5), + + width: wp(40), + }, + share_icon: { + width: wp(73), + padding: wp(18), + borderWidth: 1, + borderRadius: wp(10), + justifyContent: 'center', + alignItems: 'center', + gap: wp(5), + }, +}); diff --git a/src/screens/Home/components/Contact/component/ContactModalData.tsx b/src/screens/Home/components/Contact/component/ContactModalData.tsx new file mode 100644 index 000000000..3e72e4668 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ContactModalData.tsx @@ -0,0 +1,220 @@ +import { Box, useColorMode } from 'native-base'; +import React, { useContext, useEffect, useState } from 'react'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import Text from 'src/components/KeeperText'; +import AirDropIcon from 'src/assets/images/airdrop-circle-icon.svg'; +import NFCIcon from 'src/assets/images/nfc-circle-icon.svg'; +import QR_Icon from 'src/assets/images/qr-scan-icon.svg'; +import { Platform, StyleSheet, Vibration } from 'react-native'; +import { hp, wp } from 'src/constants/responsive'; +import { TouchableOpacity } from 'react-native-gesture-handler'; +import useToastMessage from 'src/hooks/useToastMessage'; +import ExportFile from 'src/assets/images/exportFile.svg'; +import { exportFile } from 'src/services/fs'; +import { captureError } from 'src/services/sentry'; +import ToastErrorIcon from 'src/assets/images/toast_error.svg'; +import { RKInteractionMode } from 'src/services/wallets/enums'; +import NfcPrompt from 'src/components/NfcPromptAndroid'; +import NFC from 'src/services/nfc'; +import nfcManager, { NfcTech } from 'react-native-nfc-manager'; +import { HCESession, HCESessionContext } from 'react-native-hce'; +import { CommonActions } from '@react-navigation/native'; +import Clipboard from '@react-native-clipboard/clipboard'; + +function ContactModalData({ + isShareContact = false, + setContactModalVisible, + navigation, + data, + onQrScan, +}) { + const { colorMode } = useColorMode(); + const [nfcVisible, setNfcVisible] = useState(false); + const { showToast } = useToastMessage(); + const { session } = useContext(HCESessionContext); + + const isIos = Platform.OS === 'ios'; + const isAndroid = Platform.OS === 'android'; + + const cleanUp = () => { + setNfcVisible(false); + Vibration.cancel(); + if (isAndroid) { + NFC.stopTagSession(session); + } + }; + + useEffect(() => { + const unsubDisconnect = session.on(HCESession.Events.HCE_STATE_DISCONNECTED, () => { + cleanUp(); + }); + return () => { + cleanUp(); + unsubDisconnect(); + }; + }, [session]); + + useEffect(() => { + if (isAndroid) { + if (nfcVisible) { + } else { + NFC.stopTagSession(session); + } + } + return () => { + nfcManager.cancelTechnologyRequest(); + }; + }, [nfcVisible]); + + const shareWithNFC = async () => { + try { + if (isIos) { + if (!isIos) { + setNfcVisible(true); + } + Vibration.vibrate([700, 50, 100, 50], true); + const enc = NFC.encodeTextRecord(data); + await NFC.send([NfcTech.Ndef], enc); + cleanUp(); + } else { + setNfcVisible(true); + await NFC.startTagSession({ session, content: data }); + Vibration.vibrate([700, 50, 100, 50], true); + } + } catch (err) { + cleanUp(); + if (err.toString() === 'Error: Not even registered') { + console.log('NFC interaction cancelled.'); + return; + } + console.log('Error ', err); + } + }; + + const shareWithAirdrop = async () => { + const shareFileName = `contact-${Date.now()}.txt`; + + try { + await exportFile( + data, + shareFileName, + (error) => showToast(error.message, ), + 'utf8', + false + ); + } catch (err) { + console.log(err); + captureError(err); + } + }; + + const ContactOptions = [ + { + id: 0, + label: 'Share QR', + icon: , + onPress: () => { + setContactModalVisible(false); + navigation.navigate('ContactShareQr', { data }); + }, + }, + { + id: 1, + label: 'Scan QR', + icon: , + onPress: () => { + setContactModalVisible(false); + navigation.dispatch( + CommonActions.navigate({ + name: 'ScanQR', + params: { + title: 'Scan QR', + subtitle: 'Scan QR', + onQrScan, + importOptions: false, + isSingning: true, + }, + }) + ); + }, + }, + // Only share and scan option to be shownin current release + // { + // id: 2, + // label: `${isIos ? 'Airdrop / ' : ''}File `, + // icon: , + // onPress: async () => { + // setContactModalVisible(false); + // const clipboardData = await Clipboard.getString(); + // if (clipboardData) { + // onQrScan(clipboardData); + // } else { + // showToast('No data found in clipboard'); + // } + // }, + // }, + + // { + // id: 3, + // label: 'NFC', + // icon: , + // onPress: () => { + // setContactModalVisible(false); + // shareWithNFC(); + // }, + // }, + // ...(isShareContact + // ? [ + // { + // id: 4, + // label: 'Share Link', + // icon: , + // onPress: () => { + // setContactModalVisible(false); + // navigation.navigate('RemoteSharing', { + // psbt: data, + // mode: RKInteractionMode.SHARE_REMOTE_KEY, + // }); + // }, + // }, + // ] + // : []), + ]; + + return ( + + {ContactOptions.map((option) => ( + + + + {option.label} + + + ))} + + + ); +} + +export default ContactModalData; + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + gap: wp(20), + paddingHorizontal: wp(20), + paddingVertical: hp(16), + borderRadius: 10, + marginBottom: hp(10), + }, +}); diff --git a/src/screens/Home/components/Contact/component/ContactsCta.tsx b/src/screens/Home/components/Contact/component/ContactsCta.tsx new file mode 100644 index 000000000..82528446e --- /dev/null +++ b/src/screens/Home/components/Contact/component/ContactsCta.tsx @@ -0,0 +1,75 @@ +import { Box, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet } from 'react-native'; +import { TouchableOpacity } from 'react-native-gesture-handler'; +import Text from 'src/components/KeeperText'; +import { hp } from 'src/constants/responsive'; + +export const ContactsCta = ({ + onPress, + paddingHorizontal = 0, + paddingVertical = hp(15), + width = null, + borderColor = 'transparent', + backgroundColor = null, + LeftIcon = null, + primaryTextColor = null, + primaryFontWeight = 'medium', + text, +}) => { + const { colorMode } = useColorMode(); + return ( + + + <> + {LeftIcon && } + + {text} + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + createBtn: { + paddingVertical: hp(15), + }, + cancelBtn: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 5, + }, + btnText: { + fontSize: 14, + }, +}); diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx new file mode 100644 index 000000000..8beed10a3 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -0,0 +1,150 @@ +import { Box, Image, useColorMode } from 'native-base'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import KeeperTextInput from 'src/components/KeeperTextInput'; +import { wp } from 'src/constants/responsive'; +import PlaceHolderImage from 'src/assets/images/profile-placeHolder.png'; +import PlaceholderWhiteImage from 'src/assets/images/placeholder-image-dark.png'; +import { useContext, useEffect, useState } from 'react'; +import { launchImageLibrary } from 'react-native-image-picker'; +import Buttons from 'src/components/Buttons'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { RealmSchema } from 'src/storage/realm/enum'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import dbManager from 'src/storage/realm/dbManager'; +import { persistDocument } from 'src/services/documents'; + +const ProfileContent = ({ + setUserProfileImage, + setUserProfileName, + setCreateProfile, + userProfileImage, + userProfileName, +}) => { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + const [profileImage, setProfileImage] = useState(null); + const [profileName, setProfileName] = useState(''); + const { translations } = useContext(LocalizationContext); + const { contactText, common } = translations; + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); + + useEffect(() => { + if (userProfileImage) { + setProfileImage(userProfileImage); + } + if (userProfileName) { + setProfileName(userProfileName); + } + }, [userProfileImage, userProfileName]); + + const pickImage = () => { + launchImageLibrary( + { + mediaType: 'photo', + quality: 0.8, + }, + (response) => { + if (response.didCancel) { + console.log('User cancelled image picker'); + } else if (response.errorCode) { + console.log('ImagePicker Error: ', response.errorMessage); + } else if (response.assets && response.assets.length > 0) { + setProfileImage(response.assets[0].uri); + } + } + ); + }; + + const handleConfirm = async () => { + try { + const persistedImage = await persistDocument(profileImage); + dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { + appName: profileName, + profilePicture: persistedImage, + }); + setUserProfileImage(profileImage); + setUserProfileName(profileName); + setCreateProfile(false); + } catch (error) { + console.log('Error in handleConfirm', error); + } + }; + + return ( + <> + + + {profileImage ? ( + + ) : ( + + )} + + + + + + + setProfileName(text)} + backgroundColor={`${colorMode}.primaryBackground`} + /> + + handleConfirm()} + primaryDisable={!profileName} + /> + + ); +}; + +export default ProfileContent; + +const styles = StyleSheet.create({ + content_container: { + justifyContent: 'center', + alignItems: 'center', + marginBottom: wp(20), + }, + profile_container: { + width: wp(150), + height: wp(150), + borderRadius: wp(75), + borderWidth: wp(10), + alignItems: 'center', + justifyContent: 'center', + }, + profile_image: { + width: wp(130), + height: wp(130), + borderRadius: wp(75), + }, + input_container: { + marginBottom: wp(20), + }, + addImageIcon: { + position: 'absolute', + bottom: wp(-10), + right: wp(0), + }, +}); diff --git a/src/screens/KeeperConcierge/TechnicalSupport.tsx b/src/screens/KeeperConcierge/TechnicalSupport.tsx index 0a5546bd8..4417813e9 100644 --- a/src/screens/KeeperConcierge/TechnicalSupport.tsx +++ b/src/screens/KeeperConcierge/TechnicalSupport.tsx @@ -1,7 +1,7 @@ import { Box, useColorMode } from 'native-base'; import React, { useContext, useEffect, useState } from 'react'; import ContentWrapper from 'src/components/ContentWrapper'; -import { Image, StyleSheet } from 'react-native'; +import { Image, StyleSheet, TouchableOpacity } from 'react-native'; import { hp, wp } from 'src/constants/responsive'; import TicketHistory from './components/TicketHistory'; import { CommonActions, useNavigation } from '@react-navigation/native'; @@ -31,6 +31,10 @@ import { AccountManagerCard } from './components/AccountManagerCard'; import Relay from 'src/services/backend/Relay'; import useSubscriptionLevel from 'src/hooks/useSubscriptionLevel'; import { AppSubscriptionLevel } from 'src/models/enums/SubscriptionTier'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import ConciergeIcon from 'src/assets/images/faqWhiteIcon.svg'; +import Fonts from 'src/constants/Fonts'; import ActivityIndicatorView from 'src/components/AppActivityIndicator/ActivityIndicatorView'; type ScreenProps = NativeStackScreenProps; @@ -137,9 +141,26 @@ const TechnicalSupport = ({ route }: ScreenProps) => { }; return ( - + <> - + + { + navigation.goBack(); + }} + style={styles.backButton} + > + + + } + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + Keeper Support + + {isKeeperPrivate ? ( accountManagerDetails ? ( @@ -164,7 +185,7 @@ const TechnicalSupport = ({ route }: ScreenProps) => { textColor={`${colorMode}.modalWhiteContent`} Content={() => OnboardCallContent({ submitOnboardEmail })} /> - + ); }; @@ -192,6 +213,24 @@ const styles = StyleSheet.create({ alignSelf: 'center', marginBottom: hp(20), }, + header: { + flexDirection: 'row', + alignItems: 'center', + paddingBottom: hp(24), + marginBottom: hp(20), + borderBottomWidth: 1, + }, + headerText: { + fontSize: 18, + fontFamily: Fonts.LoraMedium, + marginLeft: wp(13), + }, + backButton: { + height: hp(40), + width: wp(40), + justifyContent: 'center', + alignItems: 'center', + }, }); const OnboardCallContent = ({ submitOnboardEmail }) => { diff --git a/src/screens/KeeperConcierge/components/ConciergeScreenWrapper.tsx b/src/screens/KeeperConcierge/components/ConciergeScreenWrapper.tsx index 1a22ad2f1..f48eb72ce 100644 --- a/src/screens/KeeperConcierge/components/ConciergeScreenWrapper.tsx +++ b/src/screens/KeeperConcierge/components/ConciergeScreenWrapper.tsx @@ -28,7 +28,7 @@ function ConciergeScreenWrapper({ backgroundColor={backgroundcolor} style={[styles.wrapper, { paddingHorizontal: paddingHorizontal }, wrapperStyle]} > - + {/* */} {children} {loading && ( - + )} diff --git a/src/screens/QRScreens/ScanQR.tsx b/src/screens/QRScreens/ScanQR.tsx index 62a59e793..4c8ce97c4 100644 --- a/src/screens/QRScreens/ScanQR.tsx +++ b/src/screens/QRScreens/ScanQR.tsx @@ -25,6 +25,8 @@ import { TouchableOpacity } from 'react-native-gesture-handler'; import { InteracationMode } from '../Vault/HardwareModalMap'; import Instruction from 'src/components/Instruction'; import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; +import Buttons from 'src/components/Buttons'; +import ShareDark from 'src/assets/images/share-white.svg'; const decoder = new URRegistryDecoder(); @@ -49,10 +51,11 @@ function ScanQR() { Illustration, Instructions, isSingning = false, + contactShareData = null, } = route.params as any; const { translations } = useContext(LocalizationContext); - const { common, signer: signerText, wallet: walletText } = translations; + const { common, signer: signerText, wallet: walletText, contactText } = translations; const { showToast } = useToastMessage(); const [inputText, setInputText] = useState(''); @@ -162,6 +165,18 @@ function ScanQR() { )} + {contactShareData && ( + + { + navigation.navigate('ContactShareQr', { data: contactShareData }); + }} + fullWidth + LeftIcon={ShareDark} + /> + + )} { diff --git a/src/screens/Recieve/ReceiveAddress.tsx b/src/screens/Recieve/ReceiveAddress.tsx index 6ab3ac956..9d682d1bf 100644 --- a/src/screens/Recieve/ReceiveAddress.tsx +++ b/src/screens/Recieve/ReceiveAddress.tsx @@ -42,7 +42,7 @@ function ReceiveAddress({ address }: Props) { > - {address} + {address.length > 50 ? address.slice(0, 50) + '...' : address} => { + let res; + try { + res = await RestClient.post(`${RELAY}updateContactsKey`, { + id, + contactsKey, + }); + } catch (err) { + console.log('err', err); + if (err.response) throw new Error(err.response.data.err); + if (err.code) throw new Error(err.code); + } + const { updated } = res.data || res.json; + return { + updated, + }; + }; + public static modifyLabels = async ( appId: string, addLabels: any[], diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts new file mode 100644 index 000000000..930707ea5 --- /dev/null +++ b/src/services/p2p/ChatPeerManager.ts @@ -0,0 +1,303 @@ +import RPC from 'bare-rpc'; +import b4a from 'b4a'; +import { Worklet } from 'react-native-bare-kit'; +import bundle from './app.bundle.mjs'; +import axios from 'axios'; +import { + GET_KEYS, + GET_PEERS, + JOIN_PEER, + ON_CONNECTION, + ON_MESSAGE, + RPC_KEY, + SEND_MESSAGE, +} from './rpc-commands.mjs'; +import { CommunityType } from './interface'; +import { RealmSchema } from 'src/storage/realm/enum'; +import dbManager from 'src/storage/realm/dbManager'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptionManager'; +import Relay from '../backend/Relay'; + +export default class ChatPeerManager { + static instance: ChatPeerManager; + static isInitialized: boolean = false; + static isConnected: boolean = false; + worklet: Worklet; + IPC: any; + rpc: any; + onMessageCallback: ((data: any) => void) | null = null; + onConnectionCallback: ((data: any) => void) | null = null; + app: KeeperApp; + + private constructor() { + this.worklet = new Worklet(); + this.IPC = this.worklet.IPC; + this.app = dbManager.getObjectByIndex(RealmSchema.KeeperApp) as any as KeeperApp; + } + + static getInstance(): ChatPeerManager { + if (!ChatPeerManager.instance) { + ChatPeerManager.instance = new ChatPeerManager(); + } + return ChatPeerManager.instance; + } + + static resetInstance() { + ChatPeerManager.instance = null; + ChatPeerManager.isConnected = false; + } + + async init(seed: string): Promise { + try { + await this.worklet.start('/app.bundle', bundle, [seed]); + + const connectionPromise = new Promise((resolve) => { + const originalCallback = this.onConnectionCallback; + this.onConnectionCallback = (data) => { + if (originalCallback) { + originalCallback(data); + } + resolve(); + }; + }); + + this.rpc = new RPC(this.IPC, async (req) => { + const data = b4a.toString(req.data); + if (req.command === RPC_KEY) { + } else if (req.command === ON_MESSAGE) { + this.storeMessage(JSON.parse(data)); + if (this.onMessageCallback) { + this.onMessageCallback(JSON.parse(data)); + } + } else if (req.command === ON_CONNECTION) { + console.log('ON_CONNECTION', data); + ChatPeerManager.isConnected = true; + if (this.onConnectionCallback) { + this.onConnectionCallback(JSON.parse(data)); + } + } + }); + ChatPeerManager.isInitialized = true; + + const keeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp) as any as KeeperApp; + if (!keeperApp?.contactsKey?.secretKey) { + const keys = await this.getKeys(); + if (keys && keys.publicKey && keys.secretKey) { + const updated = await Relay.updateContactsKey(keeperApp.id, keys.secretKey); + console.log('updated', updated); + if (updated) { + await dbManager.updateObjectById(RealmSchema.KeeperApp, keeperApp.id, { + contactsKey: { + publicKey: keys.publicKey, + secretKey: keys.secretKey, + }, + }); + } + } + } + await connectionPromise; + return true; + } catch (error) { + console.error('Error initializing chat peer manager:', error); + return false; + } + } + + async getKeys() { + if (!ChatPeerManager.isInitialized || !this.rpc) { + throw new Error('ChatPeerManager not initialized. Call init() first.'); + } + const request = this.rpc.request(GET_KEYS); + request.send(`${GET_KEYS}`); + const replyBuffer = await request.reply(); + const response = b4a.toString(replyBuffer); + return JSON.parse(response); + } + + async getPeers() { + if (!ChatPeerManager.isInitialized || !this.rpc) { + throw new Error('ChatPeerManager not initialized. Call init() first.'); + } + const request = this.rpc.request(GET_PEERS); + request.send(`${GET_PEERS}`); + const replyBuffer = await request.reply(); + const response = b4a.toString(replyBuffer); + return JSON.parse(response); + } + + async joinPeers(pubKey: string) { + if (!ChatPeerManager.isInitialized || !this.rpc) { + throw new Error('ChatPeerManager not initialized. Call init() first.'); + } + const request = this.rpc.request(JOIN_PEER); + request.send(pubKey); + const replyBuffer = await request.reply(); + const response = b4a.toString(replyBuffer); + return response; + } + + async sendMessage(pubKey: string, message: string) { + if (!ChatPeerManager.isInitialized || !this.rpc) { + throw new Error('ChatPeerManager not initialized. Call init() first.'); + } + const request = this.rpc.request(SEND_MESSAGE); + request.send(JSON.stringify({ pubKey, message })); + const replyBuffer = await request.reply(); + const response = b4a.toString(replyBuffer); + return response; + } + + setOnMessageListener(callback: (data: object) => void) { + this.onMessageCallback = callback; + } + + setOnConnectionListener(callback: (data: object) => void) { + this.onConnectionCallback = callback; + } + + async getPeerMessages(pubKey: string, lastBlock: number) { + try { + const response = await axios.get( + `https://dev-api.bitcointribe.app/api/v1/chat/getmessages?publicKey=${pubKey}&from=${lastBlock}` + ); + return response.data; + } catch (error) { + console.error('Error fetching peer messages:', error); + throw error; + } + } + + async loadPendingMessages(lastBlock = 0) { + try { + const response = await this.getPeerMessages( + this.app.contactsKey.publicKey, + lastBlock === 0 ? 0 : lastBlock - 1 + ); + if (response.messages.length > 0) { + for (const msg of response.messages) { + const message = JSON.parse(msg.message); + const communityId = message.communityId; + let community = dbManager.getObjectByPrimaryId(RealmSchema.Community, 'id', communityId); + if (!community) { + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + this.app.contactsKey.secretKey, + message.pubKey + ); + const decryptedKey = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); + const communityData = { + id: communityId, + communityId: communityId, + name: message.senderName || 'Unknown Contact', + type: CommunityType.Peer, + createdAt: msg.timestamp, + updatedAt: msg.timestamp, + with: message.sender, + key: decryptedKey.aesKey, + }; + dbManager.createObject(RealmSchema.Community, communityData); + + dbManager.createObject(RealmSchema.Message, { + id: message.id, + communityId: message.communityId, + type: message.type, + text: message.text, + createdAt: msg.timestamp, + sender: message.sender, + block: msg.blockNumber, + unread: true, + fileUrl: message?.fileUrl, + request: message?.request, + }); + } else { + const decryptedMessage = ChatEncryptionManager.decryptMessage( + message, + (community as any).key + ); + const messageData = JSON.parse(decryptedMessage); + dbManager.createObject(RealmSchema.Message, { + id: messageData.id, + communityId: messageData.communityId, + type: messageData.type, + text: messageData.text, + createdAt: msg.timestamp, + sender: messageData.sender, + block: msg.blockNumber, + unread: true, + fileUrl: message?.fileUrl, + request: message?.request, + }); + } + } + } + } catch (error) { + console.error('Error loading pending messages:', error); + } + } + + storeMessage = async (payload: any) => { + try { + const communities = dbManager.getCollection(RealmSchema.Community); + const data = JSON.parse(payload.data); + const message = JSON.parse(data.message); + console.log('message', message); + let community: any = communities.find((c) => c.id === message.communityId); + if (!community && message.encryptedKeys) { + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + this.app.contactsKey.secretKey, + message.pubKey + ); + console.log('sharedSecret', sharedSecret); + const decryptedKeys = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); + console.log('decryptedKeys', decryptedKeys); + + const communityData = { + id: message.communityId, + name: message.senderName || 'Unknown Contact', + type: CommunityType.Peer, + createdAt: data.timestamp, + updatedAt: data.timestamp, + with: message.sender, + key: decryptedKeys.aesKey, + }; + dbManager.createObject(RealmSchema.Community, communityData); + dbManager.createObject(RealmSchema.Message, { + id: message.id, + communityId: message.communityId, + type: message.type, + text: message.text, + createdAt: data.timestamp, + sender: message.sender, + block: data.blockNumber, + unread: true, + fileUrl: message?.fileUrl, + }); + } else { + const decryptedMessage = ChatEncryptionManager.decryptMessage( + message, + (community as any).key + ); + const messageData = JSON.parse(decryptedMessage); + dbManager.createObject(RealmSchema.Message, { + id: messageData.id, + communityId: messageData.communityId, + type: messageData.type, + text: messageData.text, + createdAt: data.timestamp, + sender: messageData.sender, + block: data.blockNumber, + unread: true, + fileUrl: messageData?.fileUrl, + }); + } + } catch (error) { + console.error('Error storing messages:', error); + } + }; +} diff --git a/src/services/p2p/app.bundle.mjs b/src/services/p2p/app.bundle.mjs new file mode 100644 index 000000000..2eacc2a34 --- /dev/null +++ b/src/services/p2p/app.bundle.mjs @@ -0,0 +1 @@ +export default "48482\n{\"version\":0,\"id\":\"d410894a15509106761ee7b8b4c2ede83148a04288b3294612ef795ee64ba628\",\"main\":\"/src/services/p2p/worklet.mjs\",\"imports\":{},\"resolutions\":{\"/node_modules/@hyperswarm/secret-stream/index.js\":{\"#package\":\"/node_modules/@hyperswarm/secret-stream/package.json\",\"./lib/bridge\":\"/node_modules/@hyperswarm/secret-stream/lib/bridge.js\",\"./lib/handshake\":\"/node_modules/@hyperswarm/secret-stream/lib/handshake.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"sodium-secretstream\":\"/node_modules/sodium-secretstream/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\",\"streamx\":\"/node_modules/streamx/index.js\",\"timeout-refresh\":\"/node_modules/timeout-refresh/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/@hyperswarm/secret-stream/lib/bridge.js\":{\"#package\":\"/node_modules/@hyperswarm/secret-stream/package.json\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/@hyperswarm/secret-stream/lib/handshake.js\":{\"#package\":\"/node_modules/@hyperswarm/secret-stream/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"noise-curve-ed\":\"/node_modules/noise-curve-ed/index.js\",\"noise-handshake\":\"/node_modules/noise-handshake/noise.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/@hyperswarm/secret-stream/package.json\":{},\"/node_modules/b4a/index.js\":{\"#package\":\"/node_modules/b4a/package.json\"},\"/node_modules/b4a/package.json\":{},\"/node_modules/bare-addon-resolve/index.js\":{\"#package\":\"/node_modules/bare-addon-resolve/package.json\",\"./lib/errors\":\"/node_modules/bare-addon-resolve/lib/errors.js\",\"bare-module-resolve\":\"/node_modules/bare-module-resolve/index.js\",\"bare-semver\":\"/node_modules/bare-semver/index.js\"},\"/node_modules/bare-addon-resolve/lib/errors.js\":{\"#package\":\"/node_modules/bare-addon-resolve/package.json\"},\"/node_modules/bare-addon-resolve/package.json\":{},\"/node_modules/bare-events/index.js\":{\"#package\":\"/node_modules/bare-events/package.json\",\"./lib/errors\":\"/node_modules/bare-events/lib/errors.js\"},\"/node_modules/bare-events/lib/errors.js\":{\"#package\":\"/node_modules/bare-events/package.json\"},\"/node_modules/bare-events/package.json\":{},\"/node_modules/bare-module-resolve/index.js\":{\"#package\":\"/node_modules/bare-module-resolve/package.json\",\"./lib/errors\":\"/node_modules/bare-module-resolve/lib/errors.js\",\"bare-semver\":\"/node_modules/bare-semver/index.js\"},\"/node_modules/bare-module-resolve/lib/errors.js\":{\"#package\":\"/node_modules/bare-module-resolve/package.json\"},\"/node_modules/bare-module-resolve/package.json\":{},\"/node_modules/bare-os/binding.js\":{\"#package\":\"/node_modules/bare-os/package.json\",\".\":{\"ios\":\"linked:bare-os.3.6.1.framework/bare-os.3.6.1\",\"android\":\"linked:libbare-os.3.6.1.so\"}},\"/node_modules/bare-os/index.js\":{\"#package\":\"/node_modules/bare-os/package.json\",\"./binding\":\"/node_modules/bare-os/binding.js\",\"./lib/constants\":\"/node_modules/bare-os/lib/constants.js\",\"./lib/errors\":\"/node_modules/bare-os/lib/errors.js\"},\"/node_modules/bare-os/lib/constants.js\":{\"#package\":\"/node_modules/bare-os/package.json\",\"../binding\":\"/node_modules/bare-os/binding.js\"},\"/node_modules/bare-os/lib/errors.js\":{\"#package\":\"/node_modules/bare-os/package.json\"},\"/node_modules/bare-os/package.json\":{},\"/node_modules/bare-path/index.js\":{\"#package\":\"/node_modules/bare-path/package.json\",\"./lib/posix\":\"/node_modules/bare-path/lib/posix.js\",\"./lib/win32\":\"/node_modules/bare-path/lib/win32.js\"},\"/node_modules/bare-path/lib/constants.js\":{\"#package\":\"/node_modules/bare-path/package.json\"},\"/node_modules/bare-path/lib/posix.js\":{\"#package\":\"/node_modules/bare-path/package.json\",\"./constants\":\"/node_modules/bare-path/lib/constants.js\",\"./shared\":\"/node_modules/bare-path/lib/shared.js\",\"./win32\":\"/node_modules/bare-path/lib/win32.js\",\"bare-os\":\"/node_modules/bare-os/index.js\"},\"/node_modules/bare-path/lib/shared.js\":{\"#package\":\"/node_modules/bare-path/package.json\",\"./constants\":\"/node_modules/bare-path/lib/constants.js\"},\"/node_modules/bare-path/lib/win32.js\":{\"#package\":\"/node_modules/bare-path/package.json\",\"./constants\":\"/node_modules/bare-path/lib/constants.js\",\"./posix\":\"/node_modules/bare-path/lib/posix.js\",\"./shared\":\"/node_modules/bare-path/lib/shared.js\",\"bare-os\":\"/node_modules/bare-os/index.js\"},\"/node_modules/bare-path/package.json\":{},\"/node_modules/bare-rpc/index.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./lib/command-router\":\"/node_modules/bare-rpc/lib/command-router.js\",\"./lib/constants\":\"/node_modules/bare-rpc/lib/constants.js\",\"./lib/incoming-request\":\"/node_modules/bare-rpc/lib/incoming-request.js\",\"./lib/incoming-stream\":\"/node_modules/bare-rpc/lib/incoming-stream.js\",\"./lib/messages\":\"/node_modules/bare-rpc/lib/messages.js\",\"./lib/outgoing-request\":\"/node_modules/bare-rpc/lib/outgoing-request.js\",\"./lib/outgoing-stream\":\"/node_modules/bare-rpc/lib/outgoing-stream.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\"},\"/node_modules/bare-rpc/lib/command-router.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/bare-rpc/lib/constants.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\"},\"/node_modules/bare-rpc/lib/errors.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\"},\"/node_modules/bare-rpc/lib/incoming-request.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./errors\":\"/node_modules/bare-rpc/lib/errors.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/bare-rpc/lib/incoming-stream.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./constants\":\"/node_modules/bare-rpc/lib/constants.js\",\"bare-stream\":\"/node_modules/bare-stream/index.js\"},\"/node_modules/bare-rpc/lib/messages.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./constants\":\"/node_modules/bare-rpc/lib/constants.js\",\"./errors\":\"/node_modules/bare-rpc/lib/errors.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/bare-rpc/lib/outgoing-request.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./errors\":\"/node_modules/bare-rpc/lib/errors.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/bare-rpc/lib/outgoing-stream.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./constants\":\"/node_modules/bare-rpc/lib/constants.js\",\"bare-stream\":\"/node_modules/bare-stream/index.js\"},\"/node_modules/bare-rpc/package.json\":{},\"/node_modules/bare-semver/index.js\":{\"#package\":\"/node_modules/bare-semver/package.json\",\"./lib/comparator\":\"/node_modules/bare-semver/lib/comparator.js\",\"./lib/constants\":\"/node_modules/bare-semver/lib/constants.js\",\"./lib/errors\":\"/node_modules/bare-semver/lib/errors.js\",\"./lib/range\":\"/node_modules/bare-semver/lib/range.js\",\"./lib/version\":\"/node_modules/bare-semver/lib/version.js\"},\"/node_modules/bare-semver/lib/comparator.js\":{\"#package\":\"/node_modules/bare-semver/package.json\",\"./constants\":\"/node_modules/bare-semver/lib/constants.js\"},\"/node_modules/bare-semver/lib/constants.js\":{\"#package\":\"/node_modules/bare-semver/package.json\"},\"/node_modules/bare-semver/lib/errors.js\":{\"#package\":\"/node_modules/bare-semver/package.json\"},\"/node_modules/bare-semver/lib/range.js\":{\"#package\":\"/node_modules/bare-semver/package.json\",\"./comparator\":\"/node_modules/bare-semver/lib/comparator.js\",\"./constants\":\"/node_modules/bare-semver/lib/constants.js\",\"./errors\":\"/node_modules/bare-semver/lib/errors.js\",\"./version\":\"/node_modules/bare-semver/lib/version.js\"},\"/node_modules/bare-semver/lib/version.js\":{\"#package\":\"/node_modules/bare-semver/package.json\",\"./errors\":\"/node_modules/bare-semver/lib/errors.js\"},\"/node_modules/bare-semver/package.json\":{},\"/node_modules/bare-stream/index.js\":{\"#package\":\"/node_modules/bare-stream/package.json\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/bare-stream/package.json\":{},\"/node_modules/bare-url/binding.js\":{\"#package\":\"/node_modules/bare-url/package.json\",\".\":{\"ios\":\"linked:bare-url.2.1.6.framework/bare-url.2.1.6\",\"android\":\"linked:libbare-url.2.1.6.so\"}},\"/node_modules/bare-url/index.js\":{\"#package\":\"/node_modules/bare-url/package.json\",\"./binding\":\"/node_modules/bare-url/binding.js\",\"./lib/errors\":\"/node_modules/bare-url/lib/errors.js\",\"bare-path\":\"/node_modules/bare-path/index.js\"},\"/node_modules/bare-url/lib/errors.js\":{\"#package\":\"/node_modules/bare-url/package.json\"},\"/node_modules/bare-url/package.json\":{},\"/node_modules/base64-js/index.js\":{\"#package\":\"/node_modules/base64-js/package.json\"},\"/node_modules/base64-js/package.json\":{},\"/node_modules/bits-to-bytes/index.js\":{\"#package\":\"/node_modules/bits-to-bytes/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/bits-to-bytes/package.json\":{},\"/node_modules/blind-relay/index.js\":{\"#package\":\"/node_modules/blind-relay/package.json\",\"./lib/errors\":\"/node_modules/blind-relay/lib/errors.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"bits-to-bytes\":\"/node_modules/bits-to-bytes/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"compact-encoding-bitfield\":\"/node_modules/compact-encoding-bitfield/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"protomux\":\"/node_modules/protomux/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/blind-relay/lib/errors.js\":{\"#package\":\"/node_modules/blind-relay/package.json\"},\"/node_modules/blind-relay/package.json\":{},\"/node_modules/bogon/index.js\":{\"#package\":\"/node_modules/bogon/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"compact-encoding-net\":\"/node_modules/compact-encoding-net/index.js\"},\"/node_modules/bogon/package.json\":{},\"/node_modules/buffer/index.js\":{\"#package\":\"/node_modules/buffer/package.json\",\"base64-js\":\"/node_modules/base64-js/index.js\",\"ieee754\":\"/node_modules/ieee754/index.js\"},\"/node_modules/buffer/package.json\":{},\"/node_modules/compact-encoding-bitfield/index.js\":{\"#package\":\"/node_modules/compact-encoding-bitfield/package.json\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/compact-encoding-bitfield/package.json\":{},\"/node_modules/compact-encoding-net/index.js\":{\"#package\":\"/node_modules/compact-encoding-net/package.json\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/compact-encoding-net/package.json\":{},\"/node_modules/compact-encoding/endian.js\":{\"#package\":\"/node_modules/compact-encoding/package.json\"},\"/node_modules/compact-encoding/index.js\":{\"#package\":\"/node_modules/compact-encoding/package.json\",\"./endian\":\"/node_modules/compact-encoding/endian.js\",\"./lexint\":\"/node_modules/compact-encoding/lexint.js\",\"./raw\":\"/node_modules/compact-encoding/raw.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/compact-encoding/lexint.js\":{\"#package\":\"/node_modules/compact-encoding/package.json\"},\"/node_modules/compact-encoding/package.json\":{},\"/node_modules/compact-encoding/raw.js\":{\"#package\":\"/node_modules/compact-encoding/package.json\",\"./endian\":\"/node_modules/compact-encoding/endian.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/dht-rpc/index.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\",\"./lib/commands\":\"/node_modules/dht-rpc/lib/commands.js\",\"./lib/errors\":\"/node_modules/dht-rpc/lib/errors.js\",\"./lib/io\":\"/node_modules/dht-rpc/lib/io.js\",\"./lib/peer\":\"/node_modules/dht-rpc/lib/peer.js\",\"./lib/query\":\"/node_modules/dht-rpc/lib/query.js\",\"./lib/session\":\"/node_modules/dht-rpc/lib/session.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"kademlia-routing-table\":\"/node_modules/kademlia-routing-table/index.js\",\"nat-sampler\":\"/node_modules/nat-sampler/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\",\"time-ordered-set\":\"/node_modules/time-ordered-set/index.js\",\"udx-native\":\"/node_modules/udx-native/lib/udx.js\"},\"/node_modules/dht-rpc/lib/commands.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\"},\"/node_modules/dht-rpc/lib/errors.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\"},\"/node_modules/dht-rpc/lib/io.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\",\"./errors\":\"/node_modules/dht-rpc/lib/errors.js\",\"./peer\":\"/node_modules/dht-rpc/lib/peer.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"fast-fifo\":\"/node_modules/fast-fifo/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/dht-rpc/lib/peer.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"compact-encoding-net\":\"/node_modules/compact-encoding-net/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/dht-rpc/lib/query.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\",\"./commands\":\"/node_modules/dht-rpc/lib/commands.js\",\"./peer\":\"/node_modules/dht-rpc/lib/peer.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/dht-rpc/lib/session.js\":{\"#package\":\"/node_modules/dht-rpc/package.json\"},\"/node_modules/dht-rpc/package.json\":{},\"/node_modules/fast-fifo/fixed-size.js\":{\"#package\":\"/node_modules/fast-fifo/package.json\"},\"/node_modules/fast-fifo/index.js\":{\"#package\":\"/node_modules/fast-fifo/package.json\",\"./fixed-size\":\"/node_modules/fast-fifo/fixed-size.js\"},\"/node_modules/fast-fifo/package.json\":{},\"/node_modules/hypercore-crypto/index.js\":{\"#package\":\"/node_modules/hypercore-crypto/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hypercore-crypto/package.json\":{},\"/node_modules/hypercore-id-encoding/index.js\":{\"#package\":\"/node_modules/hypercore-id-encoding/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"z32\":\"/node_modules/z32/index.js\"},\"/node_modules/hypercore-id-encoding/package.json\":{},\"/node_modules/hyperdht/index.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./lib/connect\":\"/node_modules/hyperdht/lib/connect.js\",\"./lib/connection-pool\":\"/node_modules/hyperdht/lib/connection-pool.js\",\"./lib/constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./lib/crypto\":\"/node_modules/hyperdht/lib/crypto.js\",\"./lib/errors\":\"/node_modules/hyperdht/lib/errors.js\",\"./lib/messages\":\"/node_modules/hyperdht/lib/messages.js\",\"./lib/persistent\":\"/node_modules/hyperdht/lib/persistent.js\",\"./lib/raw-stream-set\":\"/node_modules/hyperdht/lib/raw-stream-set.js\",\"./lib/router\":\"/node_modules/hyperdht/lib/router.js\",\"./lib/server\":\"/node_modules/hyperdht/lib/server.js\",\"./lib/socket-pool\":\"/node_modules/hyperdht/lib/socket-pool.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"dht-rpc\":\"/node_modules/dht-rpc/index.js\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hyperdht/lib/announcer.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./encode\":\"/node_modules/hyperdht/lib/encode.js\",\"./messages\":\"/node_modules/hyperdht/lib/messages.js\",\"./persistent\":\"/node_modules/hyperdht/lib/persistent.js\",\"./sleeper\":\"/node_modules/hyperdht/lib/sleeper.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"signal-promise\":\"/node_modules/signal-promise/index.js\"},\"/node_modules/hyperdht/lib/connect.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./crypto\":\"/node_modules/hyperdht/lib/crypto.js\",\"./errors\":\"/node_modules/hyperdht/lib/errors.js\",\"./holepuncher\":\"/node_modules/hyperdht/lib/holepuncher.js\",\"./noise-wrap\":\"/node_modules/hyperdht/lib/noise-wrap.js\",\"./secure-payload\":\"/node_modules/hyperdht/lib/secure-payload.js\",\"./semaphore\":\"/node_modules/hyperdht/lib/semaphore.js\",\"./sleeper\":\"/node_modules/hyperdht/lib/sleeper.js\",\"@hyperswarm/secret-stream\":\"/node_modules/@hyperswarm/secret-stream/index.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"blind-relay\":\"/node_modules/blind-relay/index.js\",\"bogon\":\"/node_modules/bogon/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hyperdht/lib/connection-pool.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./errors\":\"/node_modules/hyperdht/lib/errors.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/hyperdht/lib/constants.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\"},\"/node_modules/hyperdht/lib/crypto.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hyperdht/lib/encode.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/hyperdht/lib/errors.js\":{\"#package\":\"/node_modules/hyperdht/package.json\"},\"/node_modules/hyperdht/lib/holepuncher.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./nat\":\"/node_modules/hyperdht/lib/nat.js\",\"./sleeper\":\"/node_modules/hyperdht/lib/sleeper.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hyperdht/lib/messages.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"compact-encoding-net\":\"/node_modules/compact-encoding-net/index.js\"},\"/node_modules/hyperdht/lib/nat.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"../lib/constants\":\"/node_modules/hyperdht/lib/constants.js\"},\"/node_modules/hyperdht/lib/noise-wrap.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./errors\":\"/node_modules/hyperdht/lib/errors.js\",\"./messages\":\"/node_modules/hyperdht/lib/messages.js\",\"@hyperswarm/secret-stream\":\"/node_modules/@hyperswarm/secret-stream/index.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"noise-curve-ed\":\"/node_modules/noise-curve-ed/index.js\",\"noise-handshake\":\"/node_modules/noise-handshake/noise.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hyperdht/lib/persistent.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./encode\":\"/node_modules/hyperdht/lib/encode.js\",\"./messages\":\"/node_modules/hyperdht/lib/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"record-cache\":\"/node_modules/record-cache/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\",\"unslab\":\"/node_modules/unslab/index.js\",\"xache\":\"/node_modules/xache/index.js\"},\"/node_modules/hyperdht/lib/raw-stream-set.js\":{\"#package\":\"/node_modules/hyperdht/package.json\"},\"/node_modules/hyperdht/lib/router.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./errors\":\"/node_modules/hyperdht/lib/errors.js\",\"./messages\":\"/node_modules/hyperdht/lib/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"xache\":\"/node_modules/xache/index.js\"},\"/node_modules/hyperdht/lib/secure-payload.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./messages\":\"/node_modules/hyperdht/lib/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hyperdht/lib/semaphore.js\":{\"#package\":\"/node_modules/hyperdht/package.json\"},\"/node_modules/hyperdht/lib/server.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"./announcer\":\"/node_modules/hyperdht/lib/announcer.js\",\"./constants\":\"/node_modules/hyperdht/lib/constants.js\",\"./crypto\":\"/node_modules/hyperdht/lib/crypto.js\",\"./errors\":\"/node_modules/hyperdht/lib/errors.js\",\"./holepuncher\":\"/node_modules/hyperdht/lib/holepuncher.js\",\"./noise-wrap\":\"/node_modules/hyperdht/lib/noise-wrap.js\",\"./secure-payload\":\"/node_modules/hyperdht/lib/secure-payload.js\",\"@hyperswarm/secret-stream\":\"/node_modules/@hyperswarm/secret-stream/index.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"blind-relay\":\"/node_modules/blind-relay/index.js\",\"bogon\":\"/node_modules/bogon/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\"},\"/node_modules/hyperdht/lib/sleeper.js\":{\"#package\":\"/node_modules/hyperdht/package.json\"},\"/node_modules/hyperdht/lib/socket-pool.js\":{\"#package\":\"/node_modules/hyperdht/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hyperdht/package.json\":{},\"/node_modules/hyperswarm/index.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\",\"./lib/connection-set\":\"/node_modules/hyperswarm/lib/connection-set.js\",\"./lib/peer-discovery\":\"/node_modules/hyperswarm/lib/peer-discovery.js\",\"./lib/peer-info\":\"/node_modules/hyperswarm/lib/peer-info.js\",\"./lib/retry-timer\":\"/node_modules/hyperswarm/lib/retry-timer.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"hyperdht\":\"/node_modules/hyperdht/index.js\",\"shuffled-priority-queue\":\"/node_modules/shuffled-priority-queue/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hyperswarm/lib/bulk-timer.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\"},\"/node_modules/hyperswarm/lib/connection-set.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hyperswarm/lib/peer-discovery.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\"},\"/node_modules/hyperswarm/lib/peer-info.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hyperswarm/lib/retry-timer.js\":{\"#package\":\"/node_modules/hyperswarm/package.json\",\"./bulk-timer\":\"/node_modules/hyperswarm/lib/bulk-timer.js\"},\"/node_modules/hyperswarm/package.json\":{},\"/node_modules/ieee754/index.js\":{\"#package\":\"/node_modules/ieee754/package.json\"},\"/node_modules/ieee754/package.json\":{},\"/node_modules/kademlia-routing-table/index.js\":{\"#package\":\"/node_modules/kademlia-routing-table/package.json\",\"events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/kademlia-routing-table/package.json\":{},\"/node_modules/nanoassert/index.js\":{\"#package\":\"/node_modules/nanoassert/package.json\"},\"/node_modules/nanoassert/package.json\":{},\"/node_modules/nat-sampler/index.js\":{\"#package\":\"/node_modules/nat-sampler/package.json\"},\"/node_modules/nat-sampler/package.json\":{},\"/node_modules/noise-curve-ed/index.js\":{\"#package\":\"/node_modules/noise-curve-ed/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"nanoassert\":\"/node_modules/nanoassert/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/noise-curve-ed/package.json\":{},\"/node_modules/noise-handshake/cipher.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/noise-handshake/dh.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"nanoassert\":\"/node_modules/nanoassert/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/noise-handshake/hkdf.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"./hmac\":\"/node_modules/noise-handshake/hmac.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/noise-handshake/hmac.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/noise-handshake/noise.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"./hkdf\":\"/node_modules/noise-handshake/hkdf.js\",\"./symmetric-state\":\"/node_modules/noise-handshake/symmetric-state.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"nanoassert\":\"/node_modules/nanoassert/index.js\"},\"/node_modules/noise-handshake/package.json\":{},\"/node_modules/noise-handshake/symmetric-state.js\":{\"#package\":\"/node_modules/noise-handshake/package.json\",\"./cipher\":\"/node_modules/noise-handshake/cipher.js\",\"./dh\":\"/node_modules/noise-handshake/dh.js\",\"./hkdf\":\"/node_modules/noise-handshake/hkdf.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"nanoassert\":\"/node_modules/nanoassert/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/protomux/index.js\":{\"#package\":\"/node_modules/protomux/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"queue-tick\":\"/node_modules/queue-tick/process-next-tick.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/protomux/package.json\":{},\"/node_modules/queue-tick/package.json\":{},\"/node_modules/queue-tick/process-next-tick.js\":{\"#package\":\"/node_modules/queue-tick/package.json\",\"./queue-microtask\":\"/node_modules/queue-tick/queue-microtask.js\"},\"/node_modules/queue-tick/queue-microtask.js\":{\"#package\":\"/node_modules/queue-tick/package.json\"},\"/node_modules/record-cache/index.js\":{\"#package\":\"/node_modules/record-cache/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/record-cache/package.json\":{},\"/node_modules/require-addon/index.js\":{\"#package\":\"/node_modules/require-addon/package.json\",\"./lib/runtime\":\"/node_modules/require-addon/lib/runtime.js\",\"./lib/runtime/bare\":\"/node_modules/require-addon/lib/runtime/bare.js\",\"./lib/runtime/default\":\"/node_modules/require-addon/lib/runtime/default.js\",\"./lib/runtime/node\":\"/node_modules/require-addon/lib/runtime/node.js\"},\"/node_modules/require-addon/lib/runtime.js\":{\"#package\":\"/node_modules/require-addon/package.json\"},\"/node_modules/require-addon/lib/runtime/bare.js\":{\"#package\":\"/node_modules/require-addon/package.json\"},\"/node_modules/require-addon/lib/runtime/default.js\":{\"#package\":\"/node_modules/require-addon/package.json\"},\"/node_modules/require-addon/lib/runtime/node.js\":{\"#package\":\"/node_modules/require-addon/package.json\",\"bare-addon-resolve\":\"/node_modules/bare-addon-resolve/index.js\",\"url\":\"/node_modules/bare-url/index.js\"},\"/node_modules/require-addon/package.json\":{},\"/node_modules/safety-catch/index.js\":{\"#package\":\"/node_modules/safety-catch/package.json\"},\"/node_modules/safety-catch/package.json\":{},\"/node_modules/shuffled-priority-queue/index.js\":{\"#package\":\"/node_modules/shuffled-priority-queue/package.json\",\"unordered-set\":\"/node_modules/unordered-set/index.js\"},\"/node_modules/shuffled-priority-queue/package.json\":{},\"/node_modules/signal-promise/index.js\":{\"#package\":\"/node_modules/signal-promise/package.json\"},\"/node_modules/signal-promise/package.json\":{},\"/node_modules/sodium-native/binding.js\":{\"#package\":\"/node_modules/sodium-native/package.json\",\".\":{\"ios\":\"linked:sodium-native.5.0.6.framework/sodium-native.5.0.6\",\"android\":\"linked:libsodium-native.5.0.6.so\"},\"require-addon\":\"/node_modules/require-addon/index.js\"},\"/node_modules/sodium-native/index.js\":{\"#package\":\"/node_modules/sodium-native/package.json\",\"./binding\":\"/node_modules/sodium-native/binding.js\",\"which-runtime\":\"/node_modules/which-runtime/index.js\"},\"/node_modules/sodium-native/package.json\":{},\"/node_modules/sodium-secretstream/index.js\":{\"#package\":\"/node_modules/sodium-secretstream/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/sodium-secretstream/package.json\":{},\"/node_modules/sodium-universal/index.js\":{\"#package\":\"/node_modules/sodium-universal/package.json\",\"sodium-native\":\"/node_modules/sodium-native/index.js\"},\"/node_modules/sodium-universal/package.json\":{},\"/node_modules/streamx/index.js\":{\"#package\":\"/node_modules/streamx/package.json\",\"events\":\"/node_modules/bare-events/index.js\",\"fast-fifo\":\"/node_modules/fast-fifo/index.js\",\"text-decoder\":\"/node_modules/text-decoder/index.js\"},\"/node_modules/streamx/package.json\":{},\"/node_modules/text-decoder/index.js\":{\"#package\":\"/node_modules/text-decoder/package.json\",\"./lib/pass-through-decoder\":\"/node_modules/text-decoder/lib/pass-through-decoder.js\",\"./lib/utf8-decoder\":\"/node_modules/text-decoder/lib/utf8-decoder.js\"},\"/node_modules/text-decoder/lib/pass-through-decoder.js\":{\"#package\":\"/node_modules/text-decoder/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/text-decoder/lib/utf8-decoder.js\":{\"#package\":\"/node_modules/text-decoder/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/text-decoder/package.json\":{},\"/node_modules/time-ordered-set/index.js\":{\"#package\":\"/node_modules/time-ordered-set/package.json\"},\"/node_modules/time-ordered-set/package.json\":{},\"/node_modules/timeout-refresh/browser.js\":{\"#package\":\"/node_modules/timeout-refresh/package.json\"},\"/node_modules/timeout-refresh/index.js\":{\"#package\":\"/node_modules/timeout-refresh/package.json\",\"./browser\":\"/node_modules/timeout-refresh/browser.js\",\"./node\":\"/node_modules/timeout-refresh/node.js\"},\"/node_modules/timeout-refresh/node.js\":{\"#package\":\"/node_modules/timeout-refresh/package.json\"},\"/node_modules/timeout-refresh/package.json\":{},\"/node_modules/udx-native/binding.js\":{\"#package\":\"/node_modules/udx-native/package.json\",\".\":{\"ios\":\"linked:udx-native.1.18.2.framework/udx-native.1.18.2\",\"android\":\"linked:libudx-native.1.18.2.so\"},\"require-addon\":\"/node_modules/require-addon/index.js\"},\"/node_modules/udx-native/lib/ip.js\":{\"#package\":\"/node_modules/udx-native/package.json\"},\"/node_modules/udx-native/lib/network-interfaces.js\":{\"#package\":\"/node_modules/udx-native/package.json\",\"../binding\":\"/node_modules/udx-native/binding.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/udx-native/lib/socket.js\":{\"#package\":\"/node_modules/udx-native/package.json\",\"../binding\":\"/node_modules/udx-native/binding.js\",\"./ip\":\"/node_modules/udx-native/lib/ip.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/udx-native/lib/stream.js\":{\"#package\":\"/node_modules/udx-native/package.json\",\"../binding\":\"/node_modules/udx-native/binding.js\",\"./ip\":\"/node_modules/udx-native/lib/ip.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/udx-native/lib/udx.js\":{\"#package\":\"/node_modules/udx-native/package.json\",\"../binding\":\"/node_modules/udx-native/binding.js\",\"./ip\":\"/node_modules/udx-native/lib/ip.js\",\"./network-interfaces\":\"/node_modules/udx-native/lib/network-interfaces.js\",\"./socket\":\"/node_modules/udx-native/lib/socket.js\",\"./stream\":\"/node_modules/udx-native/lib/stream.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/udx-native/package.json\":{},\"/node_modules/unordered-set/index.js\":{\"#package\":\"/node_modules/unordered-set/package.json\"},\"/node_modules/unordered-set/package.json\":{},\"/node_modules/unslab/index.js\":{\"#package\":\"/node_modules/unslab/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/unslab/package.json\":{},\"/node_modules/which-runtime/index.js\":{\"#package\":\"/node_modules/which-runtime/package.json\"},\"/node_modules/which-runtime/package.json\":{},\"/node_modules/xache/index.js\":{\"#package\":\"/node_modules/xache/package.json\"},\"/node_modules/xache/package.json\":{},\"/node_modules/z32/index.js\":{\"#package\":\"/node_modules/z32/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/z32/package.json\":{},\"/package.json\":{},\"/src/services/p2p/rpc-commands.mjs\":{\"#package\":\"/package.json\"},\"/src/services/p2p/worklet.mjs\":{\"#package\":\"/package.json\",\"./rpc-commands.mjs\":\"/src/services/p2p/rpc-commands.mjs\",\"b4a\":\"/node_modules/b4a/index.js\",\"bare-rpc\":\"/node_modules/bare-rpc/index.js\",\"buffer\":\"/node_modules/buffer/index.js\",\"hyperswarm\":\"/node_modules/hyperswarm/index.js\"}},\"addons\":[\"linked:bare-os.3.6.1.framework/bare-os.3.6.1\",\"linked:bare-url.2.1.6.framework/bare-url.2.1.6\",\"linked:libbare-os.3.6.1.so\",\"linked:libbare-url.2.1.6.so\",\"linked:libsodium-native.5.0.6.so\",\"linked:libudx-native.1.18.2.so\",\"linked:sodium-native.5.0.6.framework/sodium-native.5.0.6\",\"linked:udx-native.1.18.2.framework/udx-native.1.18.2\"],\"assets\":[],\"files\":{\"/node_modules/@hyperswarm/secret-stream/index.js\":{\"offset\":0,\"length\":16646,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/lib/bridge.js\":{\"offset\":16646,\"length\":1285,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/lib/handshake.js\":{\"offset\":17931,\"length\":1959,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/package.json\":{\"offset\":19890,\"length\":1809,\"mode\":420},\"/node_modules/b4a/index.js\":{\"offset\":21699,\"length\":4092,\"mode\":420},\"/node_modules/b4a/package.json\":{\"offset\":25791,\"length\":1489,\"mode\":420},\"/node_modules/bare-addon-resolve/index.js\":{\"offset\":27280,\"length\":10801,\"mode\":420},\"/node_modules/bare-addon-resolve/lib/errors.js\":{\"offset\":38081,\"length\":642,\"mode\":420},\"/node_modules/bare-addon-resolve/package.json\":{\"offset\":38723,\"length\":1952,\"mode\":420},\"/node_modules/bare-events/index.js\":{\"offset\":40675,\"length\":7322,\"mode\":420},\"/node_modules/bare-events/lib/errors.js\":{\"offset\":47997,\"length\":711,\"mode\":420},\"/node_modules/bare-events/package.json\":{\"offset\":48708,\"length\":1721,\"mode\":420},\"/node_modules/bare-module-resolve/index.js\":{\"offset\":50429,\"length\":21131,\"mode\":420},\"/node_modules/bare-module-resolve/lib/errors.js\":{\"offset\":71560,\"length\":1195,\"mode\":420},\"/node_modules/bare-module-resolve/package.json\":{\"offset\":72755,\"length\":1920,\"mode\":420},\"/node_modules/bare-os/binding.js\":{\"offset\":74675,\"length\":33,\"mode\":420},\"/node_modules/bare-os/index.js\":{\"offset\":74708,\"length\":2433,\"mode\":420},\"/node_modules/bare-os/lib/constants.js\":{\"offset\":77141,\"length\":113,\"mode\":420},\"/node_modules/bare-os/lib/errors.js\":{\"offset\":77254,\"length\":479,\"mode\":420},\"/node_modules/bare-os/package.json\":{\"offset\":77733,\"length\":1800,\"mode\":420},\"/node_modules/bare-path/index.js\":{\"offset\":79533,\"length\":306,\"mode\":420},\"/node_modules/bare-path/lib/constants.js\":{\"offset\":79839,\"length\":247,\"mode\":420},\"/node_modules/bare-path/lib/posix.js\":{\"offset\":80086,\"length\":5991,\"mode\":420},\"/node_modules/bare-path/lib/shared.js\":{\"offset\":86077,\"length\":1888,\"mode\":420},\"/node_modules/bare-path/lib/win32.js\":{\"offset\":87965,\"length\":13427,\"mode\":420},\"/node_modules/bare-path/package.json\":{\"offset\":101392,\"length\":1573,\"mode\":420},\"/node_modules/bare-rpc/index.js\":{\"offset\":102965,\"length\":9588,\"mode\":420},\"/node_modules/bare-rpc/lib/command-router.js\":{\"offset\":112553,\"length\":1073,\"mode\":420},\"/node_modules/bare-rpc/lib/constants.js\":{\"offset\":113626,\"length\":270,\"mode\":420},\"/node_modules/bare-rpc/lib/errors.js\":{\"offset\":113896,\"length\":597,\"mode\":420},\"/node_modules/bare-rpc/lib/incoming-request.js\":{\"offset\":114493,\"length\":1106,\"mode\":420},\"/node_modules/bare-rpc/lib/incoming-stream.js\":{\"offset\":115599,\"length\":1177,\"mode\":420},\"/node_modules/bare-rpc/lib/messages.js\":{\"offset\":116776,\"length\":4149,\"mode\":420},\"/node_modules/bare-rpc/lib/outgoing-request.js\":{\"offset\":120925,\"length\":1488,\"mode\":420},\"/node_modules/bare-rpc/lib/outgoing-stream.js\":{\"offset\":122413,\"length\":2114,\"mode\":420},\"/node_modules/bare-rpc/package.json\":{\"offset\":124527,\"length\":1984,\"mode\":420},\"/node_modules/bare-semver/index.js\":{\"offset\":126511,\"length\":469,\"mode\":420},\"/node_modules/bare-semver/lib/comparator.js\":{\"offset\":126980,\"length\":705,\"mode\":420},\"/node_modules/bare-semver/lib/constants.js\":{\"offset\":127685,\"length\":67,\"mode\":420},\"/node_modules/bare-semver/lib/errors.js\":{\"offset\":127752,\"length\":529,\"mode\":420},\"/node_modules/bare-semver/lib/range.js\":{\"offset\":128281,\"length\":2429,\"mode\":420},\"/node_modules/bare-semver/lib/version.js\":{\"offset\":130710,\"length\":3824,\"mode\":420},\"/node_modules/bare-semver/package.json\":{\"offset\":134534,\"length\":1645,\"mode\":420},\"/node_modules/bare-stream/index.js\":{\"offset\":136179,\"length\":7649,\"mode\":420},\"/node_modules/bare-stream/package.json\":{\"offset\":143828,\"length\":2014,\"mode\":420},\"/node_modules/bare-url/binding.js\":{\"offset\":145842,\"length\":33,\"mode\":420},\"/node_modules/bare-url/index.js\":{\"offset\":145875,\"length\":7928,\"mode\":420},\"/node_modules/bare-url/lib/errors.js\":{\"offset\":153803,\"length\":881,\"mode\":420},\"/node_modules/bare-url/package.json\":{\"offset\":154684,\"length\":1881,\"mode\":420},\"/node_modules/base64-js/index.js\":{\"offset\":156565,\"length\":3932,\"mode\":420},\"/node_modules/base64-js/package.json\":{\"offset\":160497,\"length\":1892,\"mode\":420},\"/node_modules/bits-to-bytes/index.js\":{\"offset\":162389,\"length\":3083,\"mode\":420},\"/node_modules/bits-to-bytes/package.json\":{\"offset\":165472,\"length\":1485,\"mode\":420},\"/node_modules/blind-relay/index.js\":{\"offset\":166957,\"length\":10851,\"mode\":420},\"/node_modules/blind-relay/lib/errors.js\":{\"offset\":177808,\"length\":1043,\"mode\":420},\"/node_modules/blind-relay/package.json\":{\"offset\":178851,\"length\":1823,\"mode\":420},\"/node_modules/bogon/index.js\":{\"offset\":180674,\"length\":3961,\"mode\":420},\"/node_modules/bogon/package.json\":{\"offset\":184635,\"length\":1434,\"mode\":420},\"/node_modules/buffer/index.js\":{\"offset\":186069,\"length\":58353,\"mode\":420},\"/node_modules/buffer/package.json\":{\"offset\":244422,\"length\":3323,\"mode\":420},\"/node_modules/compact-encoding-bitfield/index.js\":{\"offset\":247745,\"length\":1963,\"mode\":420},\"/node_modules/compact-encoding-bitfield/package.json\":{\"offset\":249708,\"length\":1604,\"mode\":420},\"/node_modules/compact-encoding-net/index.js\":{\"offset\":251312,\"length\":4109,\"mode\":420},\"/node_modules/compact-encoding-net/package.json\":{\"offset\":255421,\"length\":1511,\"mode\":420},\"/node_modules/compact-encoding/endian.js\":{\"offset\":256932,\"length\":103,\"mode\":420},\"/node_modules/compact-encoding/index.js\":{\"offset\":257035,\"length\":19082,\"mode\":420},\"/node_modules/compact-encoding/lexint.js\":{\"offset\":276117,\"length\":2824,\"mode\":420},\"/node_modules/compact-encoding/package.json\":{\"offset\":278941,\"length\":1490,\"mode\":420},\"/node_modules/compact-encoding/raw.js\":{\"offset\":280431,\"length\":4118,\"mode\":420},\"/node_modules/dht-rpc/index.js\":{\"offset\":284549,\"length\":24906,\"mode\":420},\"/node_modules/dht-rpc/lib/commands.js\":{\"offset\":309455,\"length\":82,\"mode\":420},\"/node_modules/dht-rpc/lib/errors.js\":{\"offset\":309537,\"length\":724,\"mode\":420},\"/node_modules/dht-rpc/lib/io.js\":{\"offset\":310261,\"length\":15421,\"mode\":420},\"/node_modules/dht-rpc/lib/peer.js\":{\"offset\":325682,\"length\":645,\"mode\":420},\"/node_modules/dht-rpc/lib/query.js\":{\"offset\":326327,\"length\":9454,\"mode\":420},\"/node_modules/dht-rpc/lib/session.js\":{\"offset\":335781,\"length\":1084,\"mode\":420},\"/node_modules/dht-rpc/package.json\":{\"offset\":336865,\"length\":1907,\"mode\":420},\"/node_modules/fast-fifo/fixed-size.js\":{\"offset\":338772,\"length\":875,\"mode\":420},\"/node_modules/fast-fifo/index.js\":{\"offset\":339647,\"length\":972,\"mode\":420},\"/node_modules/fast-fifo/package.json\":{\"offset\":340619,\"length\":1459,\"mode\":420},\"/node_modules/hypercore-crypto/index.js\":{\"offset\":342078,\"length\":5040,\"mode\":420},\"/node_modules/hypercore-crypto/package.json\":{\"offset\":347118,\"length\":1548,\"mode\":420},\"/node_modules/hypercore-id-encoding/index.js\":{\"offset\":348666,\"length\":923,\"mode\":420},\"/node_modules/hypercore-id-encoding/package.json\":{\"offset\":349589,\"length\":1576,\"mode\":420},\"/node_modules/hyperdht/index.js\":{\"offset\":351165,\"length\":15422,\"mode\":420},\"/node_modules/hyperdht/lib/announcer.js\":{\"offset\":366587,\"length\":7471,\"mode\":420},\"/node_modules/hyperdht/lib/connect.js\":{\"offset\":374058,\"length\":22683,\"mode\":420},\"/node_modules/hyperdht/lib/connection-pool.js\":{\"offset\":396741,\"length\":3120,\"mode\":420},\"/node_modules/hyperdht/lib/constants.js\":{\"offset\":399861,\"length\":1207,\"mode\":420},\"/node_modules/hyperdht/lib/crypto.js\":{\"offset\":401068,\"length\":635,\"mode\":420},\"/node_modules/hyperdht/lib/encode.js\":{\"offset\":401703,\"length\":447,\"mode\":420},\"/node_modules/hyperdht/lib/errors.js\":{\"offset\":402150,\"length\":3713,\"mode\":420},\"/node_modules/hyperdht/lib/holepuncher.js\":{\"offset\":405863,\"length\":10013,\"mode\":420},\"/node_modules/hyperdht/lib/messages.js\":{\"offset\":415876,\"length\":10440,\"mode\":420},\"/node_modules/hyperdht/lib/nat.js\":{\"offset\":426316,\"length\":4694,\"mode\":420},\"/node_modules/hyperdht/lib/noise-wrap.js\":{\"offset\":431010,\"length\":1673,\"mode\":420},\"/node_modules/hyperdht/lib/persistent.js\":{\"offset\":432683,\"length\":7857,\"mode\":420},\"/node_modules/hyperdht/lib/raw-stream-set.js\":{\"offset\":440540,\"length\":1427,\"mode\":420},\"/node_modules/hyperdht/lib/router.js\":{\"offset\":441967,\"length\":6411,\"mode\":420},\"/node_modules/hyperdht/lib/secure-payload.js\":{\"offset\":448378,\"length\":1495,\"mode\":420},\"/node_modules/hyperdht/lib/semaphore.js\":{\"offset\":449873,\"length\":995,\"mode\":420},\"/node_modules/hyperdht/lib/server.js\":{\"offset\":450868,\"length\":20077,\"mode\":420},\"/node_modules/hyperdht/lib/sleeper.js\":{\"offset\":470945,\"length\":693,\"mode\":420},\"/node_modules/hyperdht/lib/socket-pool.js\":{\"offset\":471638,\"length\":4371,\"mode\":420},\"/node_modules/hyperdht/package.json\":{\"offset\":476009,\"length\":2817,\"mode\":420},\"/node_modules/hyperswarm/index.js\":{\"offset\":478826,\"length\":16508,\"mode\":420},\"/node_modules/hyperswarm/lib/bulk-timer.js\":{\"offset\":495334,\"length\":732,\"mode\":420},\"/node_modules/hyperswarm/lib/connection-set.js\":{\"offset\":496066,\"length\":775,\"mode\":420},\"/node_modules/hyperswarm/lib/peer-discovery.js\":{\"offset\":496841,\"length\":8460,\"mode\":420},\"/node_modules/hyperswarm/lib/peer-info.js\":{\"offset\":505301,\"length\":2703,\"mode\":420},\"/node_modules/hyperswarm/lib/retry-timer.js\":{\"offset\":508004,\"length\":1821,\"mode\":420},\"/node_modules/hyperswarm/package.json\":{\"offset\":509825,\"length\":1879,\"mode\":420},\"/node_modules/ieee754/index.js\":{\"offset\":511704,\"length\":2154,\"mode\":420},\"/node_modules/ieee754/package.json\":{\"offset\":513858,\"length\":1971,\"mode\":420},\"/node_modules/kademlia-routing-table/index.js\":{\"offset\":515829,\"length\":4145,\"mode\":420},\"/node_modules/kademlia-routing-table/package.json\":{\"offset\":519974,\"length\":1744,\"mode\":420},\"/node_modules/nanoassert/index.js\":{\"offset\":521718,\"length\":438,\"mode\":420},\"/node_modules/nanoassert/package.json\":{\"offset\":522156,\"length\":1424,\"mode\":420},\"/node_modules/nat-sampler/index.js\":{\"offset\":523580,\"length\":1550,\"mode\":420},\"/node_modules/nat-sampler/package.json\":{\"offset\":525130,\"length\":1385,\"mode\":420},\"/node_modules/noise-curve-ed/index.js\":{\"offset\":526515,\"length\":1642,\"mode\":420},\"/node_modules/noise-curve-ed/package.json\":{\"offset\":528157,\"length\":1585,\"mode\":420},\"/node_modules/noise-handshake/cipher.js\":{\"offset\":529742,\"length\":2817,\"mode\":420},\"/node_modules/noise-handshake/dh.js\":{\"offset\":532559,\"length\":1439,\"mode\":420},\"/node_modules/noise-handshake/hkdf.js\":{\"offset\":533998,\"length\":1092,\"mode\":420},\"/node_modules/noise-handshake/hmac.js\":{\"offset\":535090,\"length\":1273,\"mode\":420},\"/node_modules/noise-handshake/noise.js\":{\"offset\":536363,\"length\":6345,\"mode\":420},\"/node_modules/noise-handshake/package.json\":{\"offset\":542708,\"length\":1415,\"mode\":420},\"/node_modules/noise-handshake/symmetric-state.js\":{\"offset\":544123,\"length\":2206,\"mode\":420},\"/node_modules/protomux/index.js\":{\"offset\":546329,\"length\":18996,\"mode\":420},\"/node_modules/protomux/package.json\":{\"offset\":565325,\"length\":1593,\"mode\":420},\"/node_modules/queue-tick/package.json\":{\"offset\":566918,\"length\":1517,\"mode\":420},\"/node_modules/queue-tick/process-next-tick.js\":{\"offset\":568435,\"length\":160,\"mode\":420},\"/node_modules/queue-tick/queue-microtask.js\":{\"offset\":568595,\"length\":108,\"mode\":420},\"/node_modules/record-cache/index.js\":{\"offset\":568703,\"length\":3668,\"mode\":420},\"/node_modules/record-cache/package.json\":{\"offset\":572371,\"length\":1389,\"mode\":420},\"/node_modules/require-addon/index.js\":{\"offset\":573760,\"length\":262,\"mode\":420},\"/node_modules/require-addon/lib/runtime.js\":{\"offset\":574022,\"length\":130,\"mode\":420},\"/node_modules/require-addon/lib/runtime/bare.js\":{\"offset\":574152,\"length\":45,\"mode\":420},\"/node_modules/require-addon/lib/runtime/default.js\":{\"offset\":574197,\"length\":260,\"mode\":420},\"/node_modules/require-addon/lib/runtime/node.js\":{\"offset\":574457,\"length\":1073,\"mode\":420},\"/node_modules/require-addon/package.json\":{\"offset\":575530,\"length\":2112,\"mode\":420},\"/node_modules/safety-catch/index.js\":{\"offset\":577642,\"length\":506,\"mode\":420},\"/node_modules/safety-catch/package.json\":{\"offset\":578148,\"length\":1324,\"mode\":420},\"/node_modules/shuffled-priority-queue/index.js\":{\"offset\":579472,\"length\":2607,\"mode\":420},\"/node_modules/shuffled-priority-queue/package.json\":{\"offset\":582079,\"length\":1468,\"mode\":420},\"/node_modules/signal-promise/index.js\":{\"offset\":583547,\"length\":1252,\"mode\":420},\"/node_modules/signal-promise/package.json\":{\"offset\":584799,\"length\":1280,\"mode\":420},\"/node_modules/sodium-native/binding.js\":{\"offset\":586079,\"length\":89,\"mode\":420},\"/node_modules/sodium-native/index.js\":{\"offset\":586168,\"length\":65423,\"mode\":420},\"/node_modules/sodium-native/package.json\":{\"offset\":651591,\"length\":2080,\"mode\":420},\"/node_modules/sodium-secretstream/index.js\":{\"offset\":653671,\"length\":2257,\"mode\":420},\"/node_modules/sodium-secretstream/package.json\":{\"offset\":655928,\"length\":1434,\"mode\":420},\"/node_modules/sodium-universal/index.js\":{\"offset\":657362,\"length\":42,\"mode\":420},\"/node_modules/sodium-universal/package.json\":{\"offset\":657404,\"length\":1932,\"mode\":420},\"/node_modules/streamx/index.js\":{\"offset\":659336,\"length\":33340,\"mode\":420},\"/node_modules/streamx/package.json\":{\"offset\":692676,\"length\":1689,\"mode\":420},\"/node_modules/text-decoder/index.js\":{\"offset\":694365,\"length\":1378,\"mode\":420},\"/node_modules/text-decoder/lib/pass-through-decoder.js\":{\"offset\":695743,\"length\":273,\"mode\":420},\"/node_modules/text-decoder/lib/utf8-decoder.js\":{\"offset\":696016,\"length\":2529,\"mode\":420},\"/node_modules/text-decoder/package.json\":{\"offset\":698545,\"length\":1723,\"mode\":420},\"/node_modules/time-ordered-set/index.js\":{\"offset\":700268,\"length\":1444,\"mode\":420},\"/node_modules/time-ordered-set/package.json\":{\"offset\":701712,\"length\":1443,\"mode\":420},\"/node_modules/timeout-refresh/browser.js\":{\"offset\":703155,\"length\":1098,\"mode\":420},\"/node_modules/timeout-refresh/index.js\":{\"offset\":704253,\"length\":184,\"mode\":420},\"/node_modules/timeout-refresh/node.js\":{\"offset\":704437,\"length\":928,\"mode\":420},\"/node_modules/timeout-refresh/package.json\":{\"offset\":705365,\"length\":1431,\"mode\":420},\"/node_modules/udx-native/binding.js\":{\"offset\":706796,\"length\":90,\"mode\":420},\"/node_modules/udx-native/lib/ip.js\":{\"offset\":706886,\"length\":2179,\"mode\":420},\"/node_modules/udx-native/lib/network-interfaces.js\":{\"offset\":709065,\"length\":1335,\"mode\":420},\"/node_modules/udx-native/lib/socket.js\":{\"offset\":710400,\"length\":7445,\"mode\":420},\"/node_modules/udx-native/lib/stream.js\":{\"offset\":717845,\"length\":12546,\"mode\":420},\"/node_modules/udx-native/lib/udx.js\":{\"offset\":730391,\"length\":2883,\"mode\":420},\"/node_modules/udx-native/package.json\":{\"offset\":733274,\"length\":2304,\"mode\":420},\"/node_modules/unordered-set/index.js\":{\"offset\":735578,\"length\":677,\"mode\":420},\"/node_modules/unordered-set/package.json\":{\"offset\":736255,\"length\":1431,\"mode\":420},\"/node_modules/unslab/index.js\":{\"offset\":737686,\"length\":913,\"mode\":420},\"/node_modules/unslab/package.json\":{\"offset\":738599,\"length\":1390,\"mode\":420},\"/node_modules/which-runtime/index.js\":{\"offset\":739989,\"length\":1248,\"mode\":420},\"/node_modules/which-runtime/package.json\":{\"offset\":741237,\"length\":1379,\"mode\":420},\"/node_modules/xache/index.js\":{\"offset\":742616,\"length\":2378,\"mode\":420},\"/node_modules/xache/package.json\":{\"offset\":744994,\"length\":1361,\"mode\":420},\"/node_modules/z32/index.js\":{\"offset\":746355,\"length\":2654,\"mode\":420},\"/node_modules/z32/package.json\":{\"offset\":749009,\"length\":1478,\"mode\":420},\"/package.json\":{\"offset\":750487,\"length\":8013,\"mode\":420},\"/src/services/p2p/rpc-commands.mjs\":{\"offset\":758500,\"length\":291,\"mode\":420},\"/src/services/p2p/worklet.mjs\":{\"offset\":758791,\"length\":3392,\"mode\":420}}}\nconst { Pull, Push, HEADERBYTES, KEYBYTES, ABYTES } = require('sodium-secretstream')\nconst sodium = require('sodium-universal')\nconst crypto = require('hypercore-crypto')\nconst { Duplex, Writable, getStreamError } = require('streamx')\nconst b4a = require('b4a')\nconst Timeout = require('timeout-refresh')\nconst unslab = require('unslab')\nconst Bridge = require('./lib/bridge')\nconst Handshake = require('./lib/handshake')\n\nconst IDHEADERBYTES = HEADERBYTES + 32\nconst [NS_INITIATOR, NS_RESPONDER, NS_SEND] = crypto.namespace('hyperswarm/secret-stream', 3)\nconst MAX_ATOMIC_WRITE = 256 * 256 * 256 - 1\n\nmodule.exports = class NoiseSecretStream extends Duplex {\n constructor (isInitiator, rawStream, opts = {}) {\n super({ mapWritable: toBuffer })\n\n if (typeof isInitiator !== 'boolean') {\n throw new Error('isInitiator should be a boolean')\n }\n\n this.noiseStream = this\n this.isInitiator = isInitiator\n this.rawStream = null\n\n this.publicKey = opts.publicKey || null\n this.remotePublicKey = opts.remotePublicKey || null\n this.handshakeHash = null\n this.connected = false\n this.keepAlive = opts.keepAlive || 0\n this.timeout = 0\n this.enableSend = opts.enableSend !== false\n\n // pointer for upstream to set data here if they want\n this.userData = null\n\n let openedDone = null\n this.opened = new Promise((resolve) => { openedDone = resolve })\n\n this.rawBytesWritten = 0\n this.rawBytesRead = 0\n\n // metadata used by 'hyperdht'\n this.relay = null\n this.puncher = null\n\n // unwrapped raw stream\n this._rawStream = null\n\n // handshake state\n this._handshake = null\n this._handshakePattern = opts.pattern || null\n this._handshakeDone = null\n\n // message parsing state\n this._state = 0\n this._len = 0\n this._tmp = 1\n this._message = null\n\n this._openedDone = openedDone\n this._startDone = null\n this._drainDone = null\n this._outgoingPlain = null\n this._outgoingWrapped = null\n this._utp = null\n this._setup = true\n this._ended = 2\n this._encrypt = null\n this._decrypt = null\n this._timeoutTimer = null\n this._keepAliveTimer = null\n this._sendState = null\n\n if (opts.autoStart !== false) this.start(rawStream, opts)\n\n // wiggle it to trigger open immediately (TODO add streamx option for this)\n this.resume()\n this.pause()\n }\n\n static keyPair (seed) {\n return Handshake.keyPair(seed)\n }\n\n static id (handshakeHash, isInitiator, id) {\n return streamId(handshakeHash, isInitiator, id)\n }\n\n setTimeout (ms) {\n if (!ms) ms = 0\n\n this._clearTimeout()\n this.timeout = ms\n\n if (!ms || this.rawStream === null) return\n\n this._timeoutTimer = Timeout.once(ms, destroyTimeout, this)\n this._timeoutTimer.unref()\n }\n\n setKeepAlive (ms) {\n if (!ms) ms = 0\n\n this._clearKeepAlive()\n\n this.keepAlive = ms\n\n if (!ms || this.rawStream === null) return\n\n this._keepAliveTimer = Timeout.on(ms, sendKeepAlive, this)\n this._keepAliveTimer.unref()\n }\n\n sendKeepAlive () {\n const empty = this.alloc(0)\n this.write(empty)\n }\n\n start (rawStream, opts = {}) {\n if (rawStream) {\n this.rawStream = rawStream\n this._rawStream = rawStream\n if (typeof this.rawStream.setContentSize === 'function') {\n this._utp = rawStream\n }\n } else {\n this.rawStream = new Bridge(this)\n this._rawStream = this.rawStream.reverse\n }\n\n this.rawStream.on('error', this._onrawerror.bind(this))\n this.rawStream.on('close', this._onrawclose.bind(this))\n\n this._startHandshake(opts.handshake, opts.keyPair || null)\n this._continueOpen(null)\n\n if (this.destroying) return\n\n if (opts.data) this._onrawdata(opts.data)\n if (opts.ended) this._onrawend()\n\n if (this.keepAlive > 0 && this._keepAliveTimer === null) {\n this.setKeepAlive(this.keepAlive)\n }\n\n if (this.timeout > 0 && this._timeoutTimer === null) {\n this.setTimeout(this.timeout)\n }\n }\n\n async flush () {\n if ((await this.opened) === false) return false\n if ((await Writable.drained(this)) === false) return false\n if (this.destroying) return false\n\n if (this.rawStream !== null && this.rawStream.flush) {\n return await this.rawStream.flush()\n }\n\n return true\n }\n\n _continueOpen (err) {\n if (err) this.destroy(err)\n if (this._startDone === null) return\n const done = this._startDone\n this._startDone = null\n this._open(done)\n }\n\n _onkeypairpromise (p) {\n const self = this\n const cont = this._continueOpen.bind(this)\n\n p.then(onkeypair, cont)\n\n function onkeypair (kp) {\n self._onkeypair(kp)\n cont(null)\n }\n }\n\n _onkeypair (keyPair) {\n const pattern = this._handshakePattern || 'XX'\n const remotePublicKey = this.remotePublicKey\n\n this._handshake = new Handshake(this.isInitiator, keyPair, remotePublicKey, pattern)\n this.publicKey = this._handshake.keyPair.publicKey\n }\n\n _startHandshake (handshake, keyPair) {\n if (handshake) {\n const { tx, rx, hash, publicKey, remotePublicKey } = handshake\n this._setupSecretStream(tx, rx, hash, publicKey, remotePublicKey)\n return\n }\n\n if (!keyPair) keyPair = Handshake.keyPair()\n\n if (typeof keyPair.then === 'function') {\n this._onkeypairpromise(keyPair)\n } else {\n this._onkeypair(keyPair)\n }\n }\n\n _onrawerror (err) {\n this.destroy(err)\n }\n\n _onrawclose () {\n if (this._ended !== 0) this.destroy()\n }\n\n _onrawdata (data) {\n let offset = 0\n\n if (this._timeoutTimer !== null) {\n this._timeoutTimer.refresh()\n }\n\n do {\n switch (this._state) {\n case 0: {\n while (this._tmp !== 0x1000000 && offset < data.byteLength) {\n const v = data[offset++]\n this._len += this._tmp * v\n this._tmp *= 256\n }\n\n if (this._tmp === 0x1000000) {\n this._tmp = 0\n this._state = 1\n const unprocessed = data.byteLength - offset\n if (unprocessed < this._len && this._utp !== null) this._utp.setContentSize(this._len - unprocessed)\n }\n\n break\n }\n\n case 1: {\n const missing = this._len - this._tmp\n const end = missing + offset\n\n if (this._message === null && end <= data.byteLength) {\n this._message = data.subarray(offset, end)\n offset += missing\n this._incoming()\n break\n }\n\n const unprocessed = data.byteLength - offset\n\n if (this._message === null) {\n this._message = b4a.allocUnsafe(this._len)\n }\n\n b4a.copy(data, this._message, this._tmp, offset)\n this._tmp += unprocessed\n\n if (end <= data.byteLength) {\n offset += missing\n this._incoming()\n } else {\n offset += unprocessed\n }\n\n break\n }\n }\n } while (offset < data.byteLength && !this.destroying)\n }\n\n _onrawend () {\n this._ended--\n this.push(null)\n }\n\n _onrawdrain () {\n const drain = this._drainDone\n if (drain === null) return\n this._drainDone = null\n drain()\n }\n\n _read (cb) {\n this.rawStream.resume()\n cb(null)\n }\n\n _incoming () {\n const message = this._message\n\n this._state = 0\n this._len = 0\n this._tmp = 1\n this._message = null\n\n if (this._setup === true) {\n if (this._handshake) {\n this._onhandshakert(this._handshake.recv(message))\n } else {\n if (message.byteLength !== IDHEADERBYTES) {\n this.destroy(new Error('Invalid header message received'))\n return\n }\n\n const remoteId = message.subarray(0, 32)\n const expectedId = streamId(this.handshakeHash, !this.isInitiator)\n const header = message.subarray(32)\n\n if (!b4a.equals(expectedId, remoteId)) {\n this.destroy(new Error('Invalid header received'))\n return\n }\n\n this._decrypt.init(header)\n this._setup = false // setup is now done\n }\n return\n }\n\n if (message.byteLength < ABYTES) {\n this.destroy(new Error('Invalid message received'))\n return\n }\n\n this.rawBytesRead += message.byteLength\n\n const plain = message.subarray(1, message.byteLength - ABYTES + 1)\n\n try {\n this._decrypt.next(message, plain)\n } catch (err) {\n this.destroy(err)\n return\n }\n\n // If keep alive is selective, eat the empty buffers (ie assume the other side has it enabled also)\n if (plain.byteLength === 0 && this.keepAlive !== 0) return\n\n if (this.push(plain) === false) {\n this.rawStream.pause()\n }\n }\n\n _onhandshakert (h) {\n if (this._handshakeDone === null) return\n\n if (h !== null) {\n if (h.data) this._rawStream.write(h.data)\n if (!h.tx) return\n }\n\n const done = this._handshakeDone\n const publicKey = this._handshake.keyPair.publicKey\n\n this._handshakeDone = null\n this._handshake = null\n\n if (h === null) return done(new Error('Noise handshake failed'))\n\n this._setupSecretStream(h.tx, h.rx, h.hash, publicKey, h.remotePublicKey)\n this._resolveOpened(true)\n done(null)\n }\n\n _setupSecretStream (tx, rx, handshakeHash, publicKey, remotePublicKey) {\n const buf = b4a.allocUnsafeSlow(3 + IDHEADERBYTES)\n writeUint24le(IDHEADERBYTES, buf)\n\n this._encrypt = new Push(unslab(tx.subarray(0, KEYBYTES)), undefined, buf.subarray(3 + 32))\n this._decrypt = new Pull(unslab(rx.subarray(0, KEYBYTES)))\n\n this.publicKey = publicKey\n this.remotePublicKey = remotePublicKey\n this.handshakeHash = handshakeHash\n\n const id = buf.subarray(3, 3 + 32)\n streamId(handshakeHash, this.isInitiator, id)\n\n // initialize secretbox state for unordered messages\n this._setupSecretSend(handshakeHash)\n\n this.emit('handshake')\n // if rawStream is a bridge, also emit it there\n if (this.rawStream !== this._rawStream) this.rawStream.emit('handshake')\n\n if (this.destroying) return\n\n this._rawStream.write(buf)\n }\n\n _setupSecretSend (handshakeHash) {\n this._sendState = b4a.allocUnsafeSlow(32 + 32 + 8 + 8)\n const encrypt = this._sendState.subarray(0, 32) // secrets\n const decrypt = this._sendState.subarray(32, 64)\n const counter = this._sendState.subarray(64, 72) // nonce\n const initial = this._sendState.subarray(72)\n\n const inputs = this.isInitiator\n ? [[NS_INITIATOR, NS_SEND], [NS_RESPONDER, NS_SEND]]\n : [[NS_RESPONDER, NS_SEND], [NS_INITIATOR, NS_SEND]]\n\n sodium.crypto_generichash_batch(encrypt, inputs[0], handshakeHash)\n sodium.crypto_generichash_batch(decrypt, inputs[1], handshakeHash)\n\n sodium.randombytes_buf(initial)\n counter.set(initial)\n }\n\n _open (cb) {\n // no autostart or no handshake yet\n if (this._rawStream === null || (this._handshake === null && this._encrypt === null)) {\n this._startDone = cb\n return\n }\n\n this._rawStream.on('data', this._onrawdata.bind(this))\n this._rawStream.on('end', this._onrawend.bind(this))\n this._rawStream.on('drain', this._onrawdrain.bind(this))\n\n if (this.enableSend) this._rawStream.on('message', this._onmessage.bind(this))\n\n if (this._encrypt !== null) {\n this._resolveOpened(true)\n return cb(null)\n }\n\n this._handshakeDone = cb\n\n if (this.isInitiator) this._onhandshakert(this._handshake.send())\n }\n\n _predestroy () {\n if (this.rawStream) {\n const error = getStreamError(this)\n this.rawStream.destroy(error)\n }\n\n if (this._startDone !== null) {\n const done = this._startDone\n this._startDone = null\n done(new Error('Stream destroyed'))\n }\n\n if (this._handshakeDone !== null) {\n const done = this._handshakeDone\n this._handshakeDone = null\n done(new Error('Stream destroyed'))\n }\n\n if (this._drainDone !== null) {\n const done = this._drainDone\n this._drainDone = null\n done(new Error('Stream destroyed'))\n }\n }\n\n _write (data, cb) {\n let wrapped = this._outgoingWrapped\n\n if (data !== this._outgoingPlain) {\n wrapped = b4a.allocUnsafe(data.byteLength + 3 + ABYTES)\n wrapped.set(data, 4)\n } else {\n this._outgoingWrapped = this._outgoingPlain = null\n }\n\n if (wrapped.byteLength - 3 > MAX_ATOMIC_WRITE) {\n return cb(new Error('Message is too large for an atomic write. Max size is ' + MAX_ATOMIC_WRITE + ' bytes.'))\n }\n this.rawBytesWritten += wrapped.byteLength\n\n writeUint24le(wrapped.byteLength - 3, wrapped)\n // offset 4 so we can do it in-place\n this._encrypt.next(wrapped.subarray(4, 4 + data.byteLength), wrapped.subarray(3))\n\n if (this._keepAliveTimer !== null) this._keepAliveTimer.refresh()\n\n if (this._rawStream.write(wrapped) === false) {\n this._drainDone = cb\n } else {\n cb(null)\n }\n }\n\n _final (cb) {\n this._clearKeepAlive()\n this._ended--\n this._rawStream.end()\n cb(null)\n }\n\n _resolveOpened (val) {\n if (this._openedDone === null) return\n const opened = this._openedDone\n this._openedDone = null\n opened(val)\n if (!val) return\n this.connected = true\n this.emit('connect')\n }\n\n _clearTimeout () {\n if (this._timeoutTimer === null) return\n this._timeoutTimer.destroy()\n this._timeoutTimer = null\n this.timeout = 0\n }\n\n _clearKeepAlive () {\n if (this._keepAliveTimer === null) return\n this._keepAliveTimer.destroy()\n this._keepAliveTimer = null\n this.keepAlive = 0\n }\n\n _destroy (cb) {\n this._clearKeepAlive()\n this._clearTimeout()\n this._resolveOpened(false)\n cb(null)\n }\n\n _boxMessage (buffer) {\n const MB = sodium.crypto_secretbox_MACBYTES // 16\n const NB = sodium.crypto_secretbox_NONCEBYTES // 24\n\n const counter = this._sendState.subarray(64, 72)\n sodium.sodium_increment(counter)\n if (b4a.equals(counter, this._sendState.subarray(72))) {\n this.destroy(new Error('udp send nonce exchausted'))\n return\n }\n\n const secret = this._sendState.subarray(0, 32)\n const envelope = b4a.allocUnsafe(8 + MB + buffer.byteLength)\n const nonce = envelope.subarray(0, NB)\n const ciphertext = envelope.subarray(8)\n\n b4a.fill(nonce, 0) // pad suffix\n nonce.set(counter)\n\n sodium.crypto_secretbox_easy(ciphertext, buffer, nonce, secret)\n return envelope\n }\n\n send (buffer) {\n if (!this._sendState) return\n if (!this.rawStream?.send) return // udx-stream expected\n\n const message = this._boxMessage(buffer)\n return this.rawStream.send(message)\n }\n\n trySend (buffer) {\n if (!this._sendState) return\n if (!this.rawStream?.trySend) return // udx-stream expected\n\n const message = this._boxMessage(buffer)\n this.rawStream.trySend(message)\n }\n\n _onmessage (buffer) {\n if (!this._sendState) return // messages before handshake are dropped\n\n const MB = sodium.crypto_secretbox_MACBYTES // 16\n const NB = sodium.crypto_secretbox_NONCEBYTES // 24\n\n if (buffer.byteLength < NB) return // Invalid message\n\n const nonce = b4a.allocUnsafe(NB)\n b4a.fill(nonce, 0)\n nonce.set(buffer.subarray(0, 8))\n\n const secret = this._sendState.subarray(32, 64)\n const ciphertext = buffer.subarray(8)\n const plain = buffer.subarray(8, buffer.byteLength - MB)\n\n if (ciphertext.byteLength < MB) return // invalid message\n\n const success = sodium.crypto_secretbox_open_easy(plain, ciphertext, nonce, secret)\n\n if (success) this.emit('message', plain)\n }\n\n alloc (len) {\n const buf = b4a.allocUnsafe(len + 3 + ABYTES)\n this._outgoingWrapped = buf\n this._outgoingPlain = buf.subarray(4, buf.byteLength - ABYTES + 1)\n return this._outgoingPlain\n }\n\n toJSON () {\n return {\n isInitiator: this.isInitiator,\n publicKey: this.publicKey && b4a.toString(this.publicKey, 'hex'),\n remotePublicKey: this.remotePublicKey && b4a.toString(this.remotePublicKey, 'hex'),\n connected: this.connected,\n destroying: this.destroying,\n destroyed: this.destroyed,\n rawStream: this.rawStream && this.rawStream.toJSON ? this.rawStream.toJSON() : null\n }\n }\n}\n\nfunction writeUint24le (n, buf) {\n buf[0] = (n & 255)\n buf[1] = (n >>> 8) & 255\n buf[2] = (n >>> 16) & 255\n}\n\nfunction streamId (handshakeHash, isInitiator, out = b4a.allocUnsafe(32)) {\n sodium.crypto_generichash(out, isInitiator ? NS_INITIATOR : NS_RESPONDER, handshakeHash)\n return out\n}\n\nfunction toBuffer (data) {\n return typeof data === 'string' ? b4a.from(data) : data\n}\n\nfunction destroyTimeout () {\n this.destroy(new Error('Stream timed out'))\n}\n\nfunction sendKeepAlive () {\n const empty = this.alloc(0)\n this.write(empty)\n}\nconst { Duplex, Writable } = require('streamx')\n\nclass ReversePassThrough extends Duplex {\n constructor (s) {\n super()\n this._stream = s\n this._ondrain = null\n }\n\n _write (data, cb) {\n if (this._stream.push(data) === false) {\n this._stream._ondrain = cb\n } else {\n cb(null)\n }\n }\n\n _final (cb) {\n this._stream.push(null)\n cb(null)\n }\n\n _read (cb) {\n const ondrain = this._ondrain\n this._ondrain = null\n if (ondrain) ondrain()\n cb(null)\n }\n}\n\nmodule.exports = class Bridge extends Duplex {\n constructor (noiseStream) {\n super()\n\n this.noiseStream = noiseStream\n\n this._ondrain = null\n this.reverse = new ReversePassThrough(this)\n }\n\n get publicKey () {\n return this.noiseStream.publicKey\n }\n\n get remotePublicKey () {\n return this.noiseStream.remotePublicKey\n }\n\n get handshakeHash () {\n return this.noiseStream.handshakeHash\n }\n\n flush () {\n return Writable.drained(this)\n }\n\n _read (cb) {\n const ondrain = this._ondrain\n this._ondrain = null\n if (ondrain) ondrain()\n cb(null)\n }\n\n _write (data, cb) {\n if (this.reverse.push(data) === false) {\n this.reverse._ondrain = cb\n } else {\n cb(null)\n }\n }\n\n _final (cb) {\n this.reverse.push(null)\n cb(null)\n }\n}\nconst sodium = require('sodium-universal')\nconst curve = require('noise-curve-ed')\nconst Noise = require('noise-handshake')\nconst b4a = require('b4a')\n\nconst EMPTY = b4a.alloc(0)\n\nmodule.exports = class Handshake {\n constructor (isInitiator, keyPair, remotePublicKey, pattern) {\n this.isInitiator = isInitiator\n this.keyPair = keyPair\n this.noise = new Noise(pattern, isInitiator, keyPair, { curve })\n this.noise.initialise(EMPTY, remotePublicKey)\n this.destroyed = false\n }\n\n static keyPair (seed) {\n const publicKey = b4a.alloc(32)\n const secretKey = b4a.alloc(64)\n if (seed) sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed)\n else sodium.crypto_sign_keypair(publicKey, secretKey)\n return { publicKey, secretKey }\n }\n\n recv (data) {\n try {\n this.noise.recv(data)\n if (this.noise.complete) return this._return(null)\n return this.send()\n } catch {\n this.destroy()\n return null\n }\n }\n\n // note that the data returned here is framed so we don't have to do an extra copy\n // when sending it...\n send () {\n try {\n const data = this.noise.send()\n const wrap = b4a.allocUnsafe(data.byteLength + 3)\n\n writeUint24le(data.byteLength, wrap)\n wrap.set(data, 3)\n\n return this._return(wrap)\n } catch {\n this.destroy()\n return null\n }\n }\n\n destroy () {\n if (this.destroyed) return\n this.destroyed = true\n }\n\n _return (data) {\n const tx = this.noise.complete ? b4a.toBuffer(this.noise.tx) : null\n const rx = this.noise.complete ? b4a.toBuffer(this.noise.rx) : null\n const hash = this.noise.complete ? b4a.toBuffer(this.noise.hash) : null\n const remotePublicKey = this.noise.complete ? b4a.toBuffer(this.noise.rs) : null\n\n return {\n data,\n remotePublicKey,\n hash,\n tx,\n rx\n }\n }\n}\n\nfunction writeUint24le (n, buf) {\n buf[0] = (n & 255)\n buf[1] = (n >>> 8) & 255\n buf[2] = (n >>> 16) & 255\n}\n{\n \"name\": \"@hyperswarm/secret-stream\",\n \"version\": \"6.8.1\",\n \"description\": \"Secret stream backed by Noise and libsodium's secretstream\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/**.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.1.0\",\n \"hypercore-crypto\": \"^3.3.1\",\n \"noise-curve-ed\": \"^2.0.1\",\n \"noise-handshake\": \"^4.0.0\",\n \"sodium-secretstream\": \"^1.1.0\",\n \"sodium-universal\": \"^5.0.0\",\n \"streamx\": \"^2.14.0\",\n \"timeout-refresh\": \"^2.0.0\",\n \"unslab\": \"^1.3.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.0\",\n \"standard\": \"^17.1.0\",\n \"udx-native\": \"^1.13.2\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/hyperswarm-secret-stream.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperswarm-secret-stream/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperswarm-secret-stream\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nfunction isBuffer (value) {\n return Buffer.isBuffer(value) || value instanceof Uint8Array\n}\n\nfunction isEncoding (encoding) {\n return Buffer.isEncoding(encoding)\n}\n\nfunction alloc (size, fill, encoding) {\n return Buffer.alloc(size, fill, encoding)\n}\n\nfunction allocUnsafe (size) {\n return Buffer.allocUnsafe(size)\n}\n\nfunction allocUnsafeSlow (size) {\n return Buffer.allocUnsafeSlow(size)\n}\n\nfunction byteLength (string, encoding) {\n return Buffer.byteLength(string, encoding)\n}\n\nfunction compare (a, b) {\n return Buffer.compare(a, b)\n}\n\nfunction concat (buffers, totalLength) {\n return Buffer.concat(buffers, totalLength)\n}\n\nfunction copy (source, target, targetStart, start, end) {\n return toBuffer(source).copy(target, targetStart, start, end)\n}\n\nfunction equals (a, b) {\n return toBuffer(a).equals(b)\n}\n\nfunction fill (buffer, value, offset, end, encoding) {\n return toBuffer(buffer).fill(value, offset, end, encoding)\n}\n\nfunction from (value, encodingOrOffset, length) {\n return Buffer.from(value, encodingOrOffset, length)\n}\n\nfunction includes (buffer, value, byteOffset, encoding) {\n return toBuffer(buffer).includes(value, byteOffset, encoding)\n}\n\nfunction indexOf (buffer, value, byfeOffset, encoding) {\n return toBuffer(buffer).indexOf(value, byfeOffset, encoding)\n}\n\nfunction lastIndexOf (buffer, value, byteOffset, encoding) {\n return toBuffer(buffer).lastIndexOf(value, byteOffset, encoding)\n}\n\nfunction swap16 (buffer) {\n return toBuffer(buffer).swap16()\n}\n\nfunction swap32 (buffer) {\n return toBuffer(buffer).swap32()\n}\n\nfunction swap64 (buffer) {\n return toBuffer(buffer).swap64()\n}\n\nfunction toBuffer (buffer) {\n if (Buffer.isBuffer(buffer)) return buffer\n return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n}\n\nfunction toString (buffer, encoding, start, end) {\n return toBuffer(buffer).toString(encoding, start, end)\n}\n\nfunction write (buffer, string, offset, length, encoding) {\n return toBuffer(buffer).write(string, offset, length, encoding)\n}\n\nfunction writeDoubleLE (buffer, value, offset) {\n return toBuffer(buffer).writeDoubleLE(value, offset)\n}\n\nfunction writeFloatLE (buffer, value, offset) {\n return toBuffer(buffer).writeFloatLE(value, offset)\n}\n\nfunction writeUInt32LE (buffer, value, offset) {\n return toBuffer(buffer).writeUInt32LE(value, offset)\n}\n\nfunction writeInt32LE (buffer, value, offset) {\n return toBuffer(buffer).writeInt32LE(value, offset)\n}\n\nfunction readDoubleLE (buffer, offset) {\n return toBuffer(buffer).readDoubleLE(offset)\n}\n\nfunction readFloatLE (buffer, offset) {\n return toBuffer(buffer).readFloatLE(offset)\n}\n\nfunction readUInt32LE (buffer, offset) {\n return toBuffer(buffer).readUInt32LE(offset)\n}\n\nfunction readInt32LE (buffer, offset) {\n return toBuffer(buffer).readInt32LE(offset)\n}\n\nfunction writeDoubleBE (buffer, value, offset) {\n return toBuffer(buffer).writeDoubleBE(value, offset)\n}\n\nfunction writeFloatBE (buffer, value, offset) {\n return toBuffer(buffer).writeFloatBE(value, offset)\n}\n\nfunction writeUInt32BE (buffer, value, offset) {\n return toBuffer(buffer).writeUInt32BE(value, offset)\n}\n\nfunction writeInt32BE (buffer, value, offset) {\n return toBuffer(buffer).writeInt32BE(value, offset)\n}\n\nfunction readDoubleBE (buffer, offset) {\n return toBuffer(buffer).readDoubleBE(offset)\n}\n\nfunction readFloatBE (buffer, offset) {\n return toBuffer(buffer).readFloatBE(offset)\n}\n\nfunction readUInt32BE (buffer, offset) {\n return toBuffer(buffer).readUInt32BE(offset)\n}\n\nfunction readInt32BE (buffer, offset) {\n return toBuffer(buffer).readInt32BE(offset)\n}\n\nmodule.exports = {\n isBuffer,\n isEncoding,\n alloc,\n allocUnsafe,\n allocUnsafeSlow,\n byteLength,\n compare,\n concat,\n copy,\n equals,\n fill,\n from,\n includes,\n indexOf,\n lastIndexOf,\n swap16,\n swap32,\n swap64,\n toBuffer,\n toString,\n write,\n writeDoubleLE,\n writeFloatLE,\n writeUInt32LE,\n writeInt32LE,\n readDoubleLE,\n readFloatLE,\n readUInt32LE,\n readInt32LE,\n writeDoubleBE,\n writeFloatBE,\n writeUInt32BE,\n writeInt32BE,\n readDoubleBE,\n readFloatBE,\n readUInt32BE,\n readInt32BE\n\n}\n{\n \"name\": \"b4a\",\n \"version\": \"1.6.7\",\n \"description\": \"Bridging the gap between buffers and typed arrays\",\n \"main\": \"index.js\",\n \"files\": [\n \"browser.js\",\n \"index.js\",\n \"lib\"\n ],\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\",\n \"index.js\": \"./browser.js\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test/*.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/b4a.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/b4a/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/b4a#readme\",\n \"devDependencies\": {\n \"brittle\": \"^3.5.2\",\n \"nanobench\": \"^3.0.0\",\n \"standard\": \"^17.1.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\",\n \"index.js\": \"./browser.js\"\n }\n}\nconst resolve = require('bare-module-resolve')\nconst { Version } = require('bare-semver')\nconst errors = require('./lib/errors')\n\nmodule.exports = exports = function resolve(\n specifier,\n parentURL,\n opts,\n readPackage\n) {\n if (typeof opts === 'function') {\n readPackage = opts\n opts = {}\n } else if (typeof readPackage !== 'function') {\n readPackage = defaultReadPackage\n }\n\n return {\n *[Symbol.iterator]() {\n const generator = exports.addon(specifier, parentURL, opts)\n\n let next = generator.next()\n\n while (next.done !== true) {\n const value = next.value\n\n if (value.package) {\n next = generator.next(readPackage(value.package))\n } else {\n next = generator.next(yield value.resolution)\n }\n }\n\n return next.value\n },\n\n async *[Symbol.asyncIterator]() {\n const generator = exports.addon(specifier, parentURL, opts)\n\n let next = generator.next()\n\n while (next.done !== true) {\n const value = next.value\n\n if (value.package) {\n next = generator.next(await readPackage(value.package))\n } else {\n next = generator.next(yield value.resolution)\n }\n }\n\n return next.value\n }\n }\n}\n\nfunction defaultReadPackage() {\n return null\n}\n\nconst { UNRESOLVED, YIELDED, RESOLVED } = resolve.constants\n\nexports.constants = {\n UNRESOLVED,\n YIELDED,\n RESOLVED\n}\n\nexports.addon = function* (specifier, parentURL, opts = {}) {\n const { resolutions = null } = opts\n\n if (exports.startsWithWindowsDriveLetter(specifier)) {\n specifier = '/' + specifier\n }\n\n let status\n\n if (resolutions) {\n status = yield* resolve.preresolved(specifier, resolutions, parentURL, opts)\n\n if (status) return status\n }\n\n status = yield* exports.url(specifier, parentURL, opts)\n\n if (status) return status\n\n let version = null\n\n const i = specifier.lastIndexOf('@')\n\n if (i > 0) {\n version = specifier.substring(i + 1)\n\n try {\n Version.parse(version)\n\n specifier = specifier.substring(0, i)\n } catch {\n version = null\n }\n }\n\n if (\n specifier === '.' ||\n specifier === '..' ||\n specifier[0] === '/' ||\n specifier[0] === '\\\\' ||\n specifier.startsWith('./') ||\n specifier.startsWith('.\\\\') ||\n specifier.startsWith('../') ||\n specifier.startsWith('..\\\\')\n ) {\n return yield* exports.directory(specifier, version, parentURL, opts)\n }\n\n return yield* exports.package(specifier, version, parentURL, opts)\n}\n\nexports.url = function* (url, parentURL, opts = {}) {\n let resolution\n try {\n resolution = new URL(url)\n } catch {\n return UNRESOLVED\n }\n\n const resolved = yield { resolution }\n\n return resolved ? RESOLVED : YIELDED\n}\n\nexports.package = function* (\n packageSpecifier,\n packageVersion,\n parentURL,\n opts = {}\n) {\n if (packageSpecifier === '') {\n throw errors.INVALID_ADDON_SPECIFIER(\n `Addon specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n let packageName\n\n if (packageSpecifier[0] !== '@') {\n packageName = packageSpecifier.split('/', 1).join()\n } else {\n if (!packageSpecifier.includes('/')) {\n throw errors.INVALID_ADDON_SPECIFIER(\n `Addon specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n packageName = packageSpecifier.split('/', 2).join('/')\n }\n\n if (\n packageName[0] === '.' ||\n packageName.includes('\\\\') ||\n packageName.includes('%')\n ) {\n throw errors.INVALID_ADDON_SPECIFIER(\n `Addon specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n const packageSubpath = '.' + packageSpecifier.substring(packageName.length)\n\n const status = yield* exports.packageSelf(\n packageName,\n packageSubpath,\n packageVersion,\n parentURL,\n opts\n )\n\n if (status) return status\n\n parentURL = new URL(parentURL.href)\n\n do {\n const packageURL = new URL('node_modules/' + packageName + '/', parentURL)\n\n parentURL.pathname = parentURL.pathname.substring(\n 0,\n parentURL.pathname.lastIndexOf('/')\n )\n\n const info = yield { package: new URL('package.json', packageURL) }\n\n if (info) {\n return yield* exports.directory(\n packageSubpath,\n packageVersion,\n packageURL,\n opts\n )\n }\n } while (parentURL.pathname !== '' && parentURL.pathname !== '/')\n\n return UNRESOLVED\n}\n\nexports.packageSelf = function* (\n packageName,\n packageSubpath,\n packageVersion,\n parentURL,\n opts = {}\n) {\n for (const packageURL of resolve.lookupPackageScope(parentURL, opts)) {\n const info = yield { package: packageURL }\n\n if (info) {\n if (info.name === packageName) {\n return yield* exports.directory(\n packageSubpath,\n packageVersion,\n packageURL,\n opts\n )\n }\n\n break\n }\n }\n\n return UNRESOLVED\n}\n\nexports.lookupPrebuildsScope = function* lookupPrebuildsScope(url, opts = {}) {\n const { resolutions = null } = opts\n\n if (resolutions) {\n for (const { resolution } of resolve.preresolved(\n '#prebuilds',\n resolutions,\n url,\n opts\n )) {\n if (resolution) return yield resolution\n }\n }\n\n const scopeURL = new URL(url.href)\n\n do {\n yield new URL('prebuilds/', scopeURL)\n\n scopeURL.pathname = scopeURL.pathname.substring(\n 0,\n scopeURL.pathname.lastIndexOf('/')\n )\n\n if (\n scopeURL.pathname.length === 3 &&\n exports.isWindowsDriveLetter(scopeURL.pathname.substring(1))\n ) {\n break\n }\n } while (scopeURL.pathname !== '' && scopeURL.pathname !== '/')\n}\n\nexports.file = function* (filename, parentURL, opts = {}) {\n if (parentURL.protocol === 'file:' && /%2f|%5c/i.test(filename)) {\n throw errors.INVALID_ADDON_SPECIFIER(\n `Addon specifier '${filename}' is invalid`\n )\n }\n\n const { extensions = [] } = opts\n\n let status = UNRESOLVED\n\n for (const ext of extensions) {\n if (yield { resolution: new URL(filename + ext, parentURL) }) {\n return RESOLVED\n }\n\n status = YIELDED\n }\n\n return status\n}\n\nexports.directory = function* (dirname, version, parentURL, opts = {}) {\n const {\n resolutions = null,\n host = null, // Shorthand for single host resolution\n hosts = host !== null ? [host] : [],\n builtins = [],\n matchedConditions = []\n } = opts\n\n let directoryURL\n\n if (\n dirname[dirname.length - 1] === '/' ||\n dirname[dirname.length - 1] === '\\\\'\n ) {\n directoryURL = new URL(dirname, parentURL)\n } else {\n directoryURL = new URL(dirname + '/', parentURL)\n }\n\n // Internal preresolution path, do not depend on this! It will be removed without\n // warning.\n if (resolutions) {\n const status = yield* resolve.preresolved(\n 'bare:addon',\n resolutions,\n directoryURL,\n opts\n )\n\n if (status) return status\n }\n\n const unversioned = version === null\n\n let name = null\n\n const info = yield { package: new URL('package.json', directoryURL) }\n\n if (info) {\n if (typeof info.name === 'string' && info.name !== '') {\n if (info.name.includes('__')) {\n throw errors.INVALID_PACKAGE_NAME(\n `Package name '${info.name}' is invalid`\n )\n }\n\n name = info.name.replace(/\\//g, '__').replace(/^@/, '')\n } else {\n return UNRESOLVED\n }\n\n if (typeof info.version === 'string' && info.version !== '') {\n if (version !== null && info.version !== version) return UNRESOLVED\n\n version = info.version\n }\n } else {\n return UNRESOLVED\n }\n\n let status\n\n status = yield* resolve.builtinTarget(name, version, builtins, opts)\n\n if (status) return status\n\n for (const prebuildsURL of exports.lookupPrebuildsScope(directoryURL, opts)) {\n status = UNRESOLVED\n\n for (const host of hosts) {\n const conditions = host.split('-')\n\n matchedConditions.push(...conditions)\n\n if (version !== null) {\n status |= yield* exports.file(\n host + '/' + name + '@' + version,\n prebuildsURL,\n opts\n )\n }\n\n if (unversioned) {\n status |= yield* exports.file(host + '/' + name, prebuildsURL, opts)\n }\n\n for (const _ of conditions) matchedConditions.pop()\n }\n\n if (status === RESOLVED) return status\n }\n\n return yield* exports.linked(name, version, opts)\n}\n\nexports.linked = function* (name, version = null, opts = {}) {\n const {\n linked = true,\n host = null, // Shorthand for single host resolution\n hosts = host !== null ? [host] : [],\n matchedConditions = []\n } = opts\n\n if (linked === false || hosts.length === 0) return UNRESOLVED\n\n let status = UNRESOLVED\n\n for (const host of hosts) {\n const [platform = null] = host.split('-', 1)\n\n if (platform === null) continue\n\n matchedConditions.push(platform)\n\n status |= yield* platformArtefact(name, version, platform, opts)\n\n matchedConditions.pop()\n }\n\n return status\n}\n\nfunction* platformArtefact(name, version = null, platform, opts = {}) {\n const { linkedProtocol = 'linked:' } = opts\n\n if (platform === 'darwin' || platform === 'ios') {\n if (version !== null) {\n if (\n yield {\n resolution: new URL(\n `${linkedProtocol}${name}.${version}.framework/${name}.${version}`\n )\n }\n ) {\n return RESOLVED\n }\n\n if (platform === 'darwin') {\n if (\n yield {\n resolution: new URL(`${linkedProtocol}lib${name}.${version}.dylib`)\n }\n ) {\n return RESOLVED\n }\n }\n }\n\n if (\n yield {\n resolution: new URL(`${linkedProtocol}${name}.framework/${name}`)\n }\n ) {\n return RESOLVED\n }\n\n if (platform === 'darwin') {\n if (\n yield {\n resolution: new URL(`${linkedProtocol}lib${name}.dylib`)\n }\n ) {\n return RESOLVED\n }\n }\n\n return YIELDED\n }\n\n if (platform === 'linux' || platform === 'android') {\n if (version !== null) {\n if (\n yield {\n resolution: new URL(`${linkedProtocol}lib${name}.${version}.so`)\n }\n ) {\n return RESOLVED\n }\n }\n\n if (\n yield {\n resolution: new URL(`${linkedProtocol}lib${name}.so`)\n }\n ) {\n return RESOLVED\n }\n\n return YIELDED\n }\n\n if (platform === 'win32') {\n if (version !== null) {\n if (\n yield {\n resolution: new URL(`${linkedProtocol}${name}-${version}.dll`)\n }\n ) {\n return RESOLVED\n }\n }\n\n if (\n yield {\n resolution: new URL(`${linkedProtocol}${name}.dll`)\n }\n ) {\n return RESOLVED\n }\n }\n\n return UNRESOLVED\n}\n\nexports.isWindowsDriveLetter = resolve.isWindowsDriveLetter\n\nexports.startsWithWindowsDriveLetter = resolve.startsWithWindowsDriveLetter\nmodule.exports = class AddonResolveError extends Error {\n constructor(msg, code, fn = AddonResolveError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'AddonResolveError'\n }\n\n static INVALID_ADDON_SPECIFIER(msg) {\n return new AddonResolveError(\n msg,\n 'INVALID_ADDON_SPECIFIER',\n AddonResolveError.INVALID_ADDON_SPECIFIER\n )\n }\n\n static INVALID_PACKAGE_NAME(msg) {\n return new AddonResolveError(\n msg,\n 'INVALID_PACKAGE_NAME',\n AddonResolveError.INVALID_PACKAGE_NAME\n )\n }\n}\n{\n \"name\": \"bare-addon-resolve\",\n \"version\": \"1.9.4\",\n \"description\": \"Low-level addon resolution algorithm for Bare\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./errors\": {\n \"types\": \"./lib/errors.d.ts\",\n \"default\": \"./lib/errors.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-addon-resolve.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-addon-resolve/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-addon-resolve#readme\",\n \"dependencies\": {\n \"bare-module-resolve\": \"^1.10.0\",\n \"bare-semver\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"bare-url\": \"^2.1.3\",\n \"brittle\": \"^3.2.1\",\n \"prettier\": \"^3.3.3\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"peerDependencies\": {\n \"bare-url\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-url\": {\n \"optional\": true\n }\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst errors = require('./lib/errors')\n\nclass EventListener {\n constructor() {\n this.list = []\n this.count = 0\n }\n\n append(ctx, name, fn, once) {\n this.count++\n ctx.emit('newListener', name, fn) // Emit BEFORE adding\n this.list.push([fn, once])\n }\n\n prepend(ctx, name, fn, once) {\n this.count++\n ctx.emit('newListener', name, fn) // Emit BEFORE adding\n this.list.unshift([fn, once])\n }\n\n remove(ctx, name, fn) {\n for (let i = 0, n = this.list.length; i < n; i++) {\n const l = this.list[i]\n\n if (l[0] === fn) {\n this.list.splice(i, 1)\n\n if (this.count === 1) delete ctx._events[name]\n\n ctx.emit('removeListener', name, fn) // Emit AFTER removing\n\n this.count--\n return\n }\n }\n }\n\n removeAll(ctx, name) {\n const list = [...this.list]\n this.list = []\n\n if (this.count === list.length) delete ctx._events[name]\n\n for (let i = list.length - 1; i >= 0; i--) {\n ctx.emit('removeListener', name, list[i][0]) // Emit AFTER removing\n }\n\n this.count -= list.length\n }\n\n emit(ctx, name, ...args) {\n const list = [...this.list]\n\n for (let i = 0, n = list.length; i < n; i++) {\n const l = list[i]\n\n if (l[1] === true) this.remove(ctx, name, l[0])\n\n l[0].call(ctx, ...args)\n }\n\n return list.length > 0\n }\n}\n\nfunction appendListener(ctx, name, fn, once) {\n const e = ctx._events[name] || (ctx._events[name] = new EventListener())\n e.append(ctx, name, fn, once)\n return ctx\n}\n\nfunction prependListener(ctx, name, fn, once) {\n const e = ctx._events[name] || (ctx._events[name] = new EventListener())\n e.prepend(ctx, name, fn, once)\n return ctx\n}\n\nfunction removeListener(ctx, name, fn) {\n const e = ctx._events[name]\n if (e !== undefined) e.remove(ctx, name, fn)\n return ctx\n}\n\nfunction throwUnhandledError(...args) {\n let err\n\n if (args.length > 0) err = args[0]\n\n if (err instanceof Error === false) err = errors.UNHANDLED_ERROR(err)\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(err, exports.prototype.emit)\n }\n\n queueMicrotask(() => {\n throw err\n })\n}\n\nmodule.exports = exports = class EventEmitter {\n constructor() {\n this._events = Object.create(null)\n }\n\n addListener(name, fn) {\n return appendListener(this, name, fn, false)\n }\n\n addOnceListener(name, fn) {\n return appendListener(this, name, fn, true)\n }\n\n prependListener(name, fn) {\n return prependListener(this, name, fn, false)\n }\n\n prependOnceListener(name, fn) {\n return prependListener(this, name, fn, true)\n }\n\n removeListener(name, fn) {\n return removeListener(this, name, fn)\n }\n\n on(name, fn) {\n return appendListener(this, name, fn, false)\n }\n\n once(name, fn) {\n return appendListener(this, name, fn, true)\n }\n\n off(name, fn) {\n return removeListener(this, name, fn)\n }\n\n emit(name, ...args) {\n if (name === 'error' && this._events.error === undefined) {\n throwUnhandledError(...args)\n }\n\n const e = this._events[name]\n return e === undefined ? false : e.emit(this, name, ...args)\n }\n\n listeners(name) {\n const e = this._events[name]\n return e === undefined ? [] : [...e.list]\n }\n\n listenerCount(name) {\n const e = this._events[name]\n return e === undefined ? 0 : e.list.length\n }\n\n getMaxListeners() {\n return EventEmitter.defaultMaxListeners\n }\n\n setMaxListeners(n) {}\n\n removeAllListeners(name) {\n if (arguments.length === 0) {\n for (const key of Reflect.ownKeys(this._events)) {\n if (key === 'removeListener') continue\n this.removeAllListeners(key)\n }\n this.removeAllListeners('removeListener')\n } else {\n const e = this._events[name]\n if (e !== undefined) e.removeAll(this, name)\n }\n return this\n }\n}\n\nexports.EventEmitter = exports\n\nexports.errors = errors\n\nexports.defaultMaxListeners = 10\n\nexports.on = function on(emitter, name, opts = {}) {\n const { signal } = opts\n\n if (signal && signal.aborted) {\n throw errors.OPERATION_ABORTED(signal.reason)\n }\n\n let error = null\n let done = false\n\n const events = []\n const promises = []\n\n emitter.on(name, onevent)\n\n if (name !== 'error') emitter.on('error', onerror)\n\n if (signal) signal.addEventListener('abort', onabort)\n\n return {\n next() {\n if (events.length) {\n return Promise.resolve({ value: events.shift(), done: false })\n }\n\n if (error) {\n const err = error\n\n error = null\n\n return Promise.reject(err)\n }\n\n if (done) return onclose()\n\n return new Promise((resolve, reject) =>\n promises.push({ resolve, reject })\n )\n },\n\n return() {\n return onclose()\n },\n\n throw(err) {\n return onerror(err)\n },\n\n [Symbol.asyncIterator]() {\n return this\n }\n }\n\n function onevent(...args) {\n if (promises.length) {\n promises.shift().resolve({ value: args, done: false })\n } else {\n events.push(args)\n }\n }\n\n function onerror(err) {\n if (promises.length) {\n promises.shift().reject(err)\n } else {\n error = err\n }\n\n return Promise.resolve({ done: true })\n }\n\n function onabort() {\n onerror(errors.OPERATION_ABORTED(signal.reason))\n }\n\n function onclose() {\n emitter.off(name, onevent)\n\n if (name !== 'error') emitter.off('error', onerror)\n\n if (signal) signal.removeEventListener('abort', onabort)\n\n done = true\n\n if (promises.length) promises.shift().resolve({ done: true })\n\n return Promise.resolve({ done: true })\n }\n}\n\nexports.once = function once(emitter, name, opts = {}) {\n const { signal } = opts\n\n if (signal && signal.aborted) {\n throw errors.OPERATION_ABORTED(signal.reason)\n }\n\n return new Promise((resolve, reject) => {\n if (name !== 'error') emitter.on('error', onerror)\n\n if (signal) signal.addEventListener('abort', onabort)\n\n emitter.once(name, (...args) => {\n if (name !== 'error') emitter.off('error', onerror)\n\n if (signal) signal.removeEventListener('abort', onabort)\n\n resolve(args)\n })\n\n function onerror(err) {\n emitter.off('error', onerror)\n\n reject(err)\n }\n\n function onabort() {\n signal.removeEventListener('abort', onabort)\n\n onerror(errors.OPERATION_ABORTED(signal.reason))\n }\n })\n}\n\nexports.forward = function forward(from, to, names, opts = {}) {\n if (typeof names === 'string') names = [names]\n\n const { emit = to.emit.bind(to) } = opts\n\n const listeners = names.map(\n (name) =>\n function onevent(...args) {\n emit(name, ...args)\n }\n )\n\n to.on('newListener', (name) => {\n const i = names.indexOf(name)\n\n if (i !== -1 && to.listenerCount(name) === 0) {\n from.on(name, listeners[i])\n }\n }).on('removeListener', (name) => {\n const i = names.indexOf(name)\n\n if (i !== -1 && to.listenerCount(name) === 0) {\n from.off(name, listeners[i])\n }\n })\n}\n\nexports.listenerCount = function listenerCount(emitter, name) {\n return emitter.listenerCount(name)\n}\n\nexports.getMaxListeners = function getMaxListeners(emitter) {\n return emitter.getMaxListeners()\n}\n\nexports.setMaxListeners = function setMaxListeners(n, ...emitters) {\n if (emitters.length === 0) exports.defaultMaxListeners = n\n else {\n for (const emitter of emitters) {\n emitter.setMaxListeners(n)\n }\n }\n}\nmodule.exports = class EventEmitterError extends Error {\n constructor(msg, code, fn = EventEmitterError, opts) {\n super(`${code}: ${msg}`, opts)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'EventEmitterError'\n }\n\n static OPERATION_ABORTED(cause, msg = 'Operation aborted') {\n return new EventEmitterError(\n msg,\n 'OPERATION_ABORTED',\n EventEmitterError.OPERATION_ABORTED,\n { cause }\n )\n }\n\n static UNHANDLED_ERROR(cause, msg = 'Unhandled error') {\n return new EventEmitterError(\n msg,\n 'UNHANDLED_ERROR',\n EventEmitterError.UNHANDLED_ERROR,\n { cause }\n )\n }\n}\n{\n \"name\": \"bare-events\",\n \"version\": \"2.6.0\",\n \"description\": \"Event emitters for JavaScript\",\n \"exports\": {\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./package\": \"./package.json\",\n \"./errors\": \"./lib/errors.js\"\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:bare\": \"bare test.js\",\n \"test:node\": \"node test.js\",\n \"lint\": \"prettier . --check\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-events.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-events/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-events#readme\",\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"prettier\": \"^3.4.2\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst { satisfies } = require('bare-semver')\nconst errors = require('./lib/errors')\n\nmodule.exports = exports = function resolve(\n specifier,\n parentURL,\n opts,\n readPackage\n) {\n if (typeof opts === 'function') {\n readPackage = opts\n opts = {}\n } else if (typeof readPackage !== 'function') {\n readPackage = defaultReadPackage\n }\n\n return {\n *[Symbol.iterator]() {\n const generator = exports.module(specifier, parentURL, opts)\n\n let next = generator.next()\n\n while (next.done !== true) {\n const value = next.value\n\n if (value.package) {\n next = generator.next(readPackage(value.package))\n } else {\n next = generator.next(yield value.resolution)\n }\n }\n\n return next.value\n },\n\n async *[Symbol.asyncIterator]() {\n const generator = exports.module(specifier, parentURL, opts)\n\n let next = generator.next()\n\n while (next.done !== true) {\n const value = next.value\n\n if (value.package) {\n next = generator.next(await readPackage(value.package))\n } else {\n next = generator.next(yield value.resolution)\n }\n }\n\n return next.value\n }\n }\n}\n\nfunction defaultReadPackage() {\n return null\n}\n\n// No resolution candidate was yielded\nconst UNRESOLVED = 0x0\n// At least 1 resolution candidate was yielded\nconst YIELDED = 0x1\n// At least 1 resolution candidate was yielded and resolved\nconst RESOLVED = YIELDED | 0x2\n\nexports.constants = {\n UNRESOLVED,\n YIELDED,\n RESOLVED\n}\n\nexports.module = function* (specifier, parentURL, opts = {}) {\n const { resolutions = null, imports = null } = opts\n\n if (exports.startsWithWindowsDriveLetter(specifier)) {\n specifier = '/' + specifier\n }\n\n let status\n\n if (resolutions) {\n status = yield* exports.preresolved(specifier, resolutions, parentURL, opts)\n\n if (status) return status\n }\n\n status = yield* exports.url(specifier, parentURL, opts)\n\n if (status) return status\n\n status = yield* exports.packageImports(specifier, parentURL, opts)\n\n if (status) return status\n\n if (\n specifier === '.' ||\n specifier === '..' ||\n specifier[0] === '/' ||\n specifier[0] === '\\\\' ||\n specifier.startsWith('./') ||\n specifier.startsWith('.\\\\') ||\n specifier.startsWith('../') ||\n specifier.startsWith('..\\\\')\n ) {\n if (imports) {\n status = yield* exports.packageImportsExports(\n specifier,\n imports,\n parentURL,\n true,\n opts\n )\n\n if (status) return status\n }\n\n status = yield* exports.deferred(specifier, opts)\n\n if (status) return status\n\n status = yield* exports.file(specifier, parentURL, false, opts)\n\n if (status === RESOLVED) return status\n\n return yield* exports.directory(specifier, parentURL, opts)\n }\n\n return yield* exports.package(specifier, parentURL, opts)\n}\n\nexports.url = function* (url, parentURL, opts = {}) {\n const { imports = null, deferredProtocol = 'deferred:' } = opts\n\n let resolution\n try {\n resolution = new URL(url)\n } catch {\n return UNRESOLVED\n }\n\n if (imports) {\n const status = yield* exports.packageImportsExports(\n resolution.href,\n imports,\n parentURL,\n true,\n opts\n )\n\n if (status) return status\n }\n\n if (resolution.protocol === deferredProtocol) {\n const specifier = resolution.pathname\n\n return yield* exports.module(specifier, parentURL, opts)\n }\n\n if (resolution.protocol === 'node:') {\n const specifier = resolution.pathname\n\n if (\n specifier === '.' ||\n specifier === '..' ||\n specifier[0] === '/' ||\n specifier.startsWith('./') ||\n specifier.startsWith('../')\n ) {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${url}' is not a valid package name`\n )\n }\n\n return yield* exports.package(specifier, parentURL, opts)\n }\n\n const resolved = yield { resolution }\n\n return resolved ? RESOLVED : YIELDED\n}\n\nexports.preresolved = function* (specifier, resolutions, parentURL, opts = {}) {\n const imports = resolutions[parentURL.href]\n\n if (typeof imports === 'object' && imports !== null) {\n return yield* exports.packageImportsExports(\n specifier,\n imports,\n parentURL,\n true,\n opts\n )\n }\n\n return UNRESOLVED\n}\n\nexports.deferred = function* (specifier, opts = {}) {\n const { deferredProtocol = 'deferred:', defer = [] } = opts\n\n if (defer.includes(specifier)) {\n const resolved = yield { resolution: new URL(deferredProtocol + specifier) }\n\n return resolved ? RESOLVED : YIELDED\n }\n\n return UNRESOLVED\n}\n\nexports.package = function* (packageSpecifier, parentURL, opts = {}) {\n const { builtins = [] } = opts\n\n if (packageSpecifier === '') {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n let packageName\n\n if (packageSpecifier[0] !== '@') {\n packageName = packageSpecifier.split('/', 1).join()\n } else {\n if (!packageSpecifier.includes('/')) {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n packageName = packageSpecifier.split('/', 2).join('/')\n }\n\n if (\n packageName[0] === '.' ||\n packageName.includes('\\\\') ||\n packageName.includes('%')\n ) {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${packageSpecifier}' is not a valid package name`\n )\n }\n\n let status\n\n status = yield* exports.builtinTarget(packageSpecifier, null, builtins, opts)\n\n if (status) return status\n\n status = yield* exports.deferred(packageSpecifier, opts)\n\n if (status) return status\n\n let packageSubpath = '.' + packageSpecifier.substring(packageName.length)\n\n status = yield* exports.packageSelf(\n packageName,\n packageSubpath,\n parentURL,\n opts\n )\n\n if (status) return status\n\n parentURL = new URL(parentURL.href)\n\n do {\n const packageURL = new URL('node_modules/' + packageName + '/', parentURL)\n\n parentURL.pathname = parentURL.pathname.substring(\n 0,\n parentURL.pathname.lastIndexOf('/')\n )\n\n const info = yield { package: new URL('package.json', packageURL) }\n\n if (info) {\n if (info.engines) exports.validateEngines(packageURL, info.engines, opts)\n\n if (info.exports) {\n return yield* exports.packageExports(\n packageURL,\n packageSubpath,\n info.exports,\n opts\n )\n }\n\n if (packageSubpath === '.') {\n if (typeof info.main === 'string' && info.main !== '') {\n packageSubpath = info.main\n } else {\n return yield* exports.file('index', packageURL, true, opts)\n }\n }\n\n status = yield* exports.file(packageSubpath, packageURL, false, opts)\n\n if (status === RESOLVED) return status\n\n return yield* exports.directory(packageSubpath, packageURL, opts)\n }\n } while (parentURL.pathname !== '' && parentURL.pathname !== '/')\n\n return UNRESOLVED\n}\n\nexports.packageSelf = function* (\n packageName,\n packageSubpath,\n parentURL,\n opts = {}\n) {\n for (const packageURL of exports.lookupPackageScope(parentURL, opts)) {\n const info = yield { package: packageURL }\n\n if (info) {\n if (info.name !== packageName) return false\n\n if (info.exports) {\n return yield* exports.packageExports(\n packageURL,\n packageSubpath,\n info.exports,\n opts\n )\n }\n\n if (packageSubpath === '.') {\n if (typeof info.main === 'string' && info.main !== '') {\n packageSubpath = info.main\n } else {\n return yield* exports.file('index', packageURL, true, opts)\n }\n }\n\n const status = yield* exports.file(\n packageSubpath,\n packageURL,\n false,\n opts\n )\n\n if (status === RESOLVED) return status\n\n return yield* exports.directory(packageSubpath, packageURL, opts)\n }\n }\n\n return UNRESOLVED\n}\n\nexports.packageExports = function* (\n packageURL,\n subpath,\n packageExports,\n opts = {}\n) {\n if (subpath === '.') {\n let mainExport\n\n if (typeof packageExports === 'string' || Array.isArray(packageExports)) {\n mainExport = packageExports\n } else if (typeof packageExports === 'object' && packageExports !== null) {\n const keys = Object.keys(packageExports)\n\n if (keys.some((key) => key.startsWith('.'))) {\n if ('.' in packageExports) mainExport = packageExports['.']\n } else {\n mainExport = packageExports\n }\n }\n\n if (mainExport) {\n const status = yield* exports.packageTarget(\n packageURL,\n mainExport,\n null,\n false,\n opts\n )\n\n if (status) return status\n }\n } else if (typeof packageExports === 'object' && packageExports !== null) {\n const keys = Object.keys(packageExports)\n\n if (keys.every((key) => key.startsWith('.'))) {\n const status = yield* exports.packageImportsExports(\n subpath,\n packageExports,\n packageURL,\n false,\n opts\n )\n\n if (status) return status\n }\n }\n\n packageURL = new URL('package.json', packageURL)\n\n throw errors.PACKAGE_PATH_NOT_EXPORTED(\n `Package subpath '${subpath}' is not defined by \"exports\" in '${packageURL}'`\n )\n}\n\nexports.packageImports = function* (specifier, parentURL, opts = {}) {\n const { imports = null } = opts\n\n if (specifier === '#' || specifier.startsWith('#/')) {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${specifier}' is not a valid internal imports specifier`\n )\n }\n\n for (const packageURL of exports.lookupPackageScope(parentURL, opts)) {\n const info = yield { package: packageURL }\n\n if (info) {\n if (info.imports) {\n const status = yield* exports.packageImportsExports(\n specifier,\n info.imports,\n packageURL,\n true,\n opts\n )\n\n if (status) return status\n }\n\n if (specifier.startsWith('#')) {\n throw errors.PACKAGE_IMPORT_NOT_DEFINED(\n `Package import specifier '${specifier}' is not defined by \"imports\" in '${packageURL}'`\n )\n }\n\n break\n }\n }\n\n if (imports) {\n const status = yield* exports.packageImportsExports(\n specifier,\n imports,\n parentURL,\n true,\n opts\n )\n\n if (status) return status\n }\n\n return UNRESOLVED\n}\n\nexports.packageImportsExports = function* (\n matchKey,\n matchObject,\n packageURL,\n isImports,\n opts = {}\n) {\n if (matchKey in matchObject && !matchKey.includes('*')) {\n const target = matchObject[matchKey]\n\n return yield* exports.packageTarget(\n packageURL,\n target,\n null,\n isImports,\n opts\n )\n }\n\n const expansionKeys = Object.keys(matchObject)\n .filter((key) => key.includes('*'))\n .sort(exports.patternKeyCompare)\n\n for (const expansionKey of expansionKeys) {\n const patternIndex = expansionKey.indexOf('*')\n const patternBase = expansionKey.substring(0, patternIndex)\n\n if (matchKey.startsWith(patternBase) && matchKey !== patternBase) {\n const patternTrailer = expansionKey.substring(patternIndex + 1)\n\n if (\n patternTrailer === '' ||\n (matchKey.endsWith(patternTrailer) &&\n matchKey.length >= expansionKey.length)\n ) {\n const target = matchObject[expansionKey]\n\n const patternMatch = matchKey.substring(\n patternBase.length,\n matchKey.length - patternTrailer.length\n )\n\n return yield* exports.packageTarget(\n packageURL,\n target,\n patternMatch,\n isImports,\n opts\n )\n }\n }\n }\n\n return UNRESOLVED\n}\n\nexports.validateEngines = function validateEngines(\n packageURL,\n packageEngines,\n opts = {}\n) {\n const { engines = {} } = opts\n\n for (const [engine, range] of Object.entries(packageEngines)) {\n if (engine in engines) {\n const version = engines[engine]\n\n if (!satisfies(version, range)) {\n packageURL = new URL('package.json', packageURL)\n\n throw errors.UNSUPPORTED_ENGINE(\n `Package not compatible with engine '${engine}' ${version}, requires range '${range}' defined by \"engines\" in '${packageURL}'`\n )\n }\n }\n }\n}\n\nexports.patternKeyCompare = function patternKeyCompare(keyA, keyB) {\n const patternIndexA = keyA.indexOf('*')\n const patternIndexB = keyB.indexOf('*')\n const baseLengthA = patternIndexA === -1 ? keyA.length : patternIndexA + 1\n const baseLengthB = patternIndexB === -1 ? keyB.length : patternIndexB + 1\n if (baseLengthA > baseLengthB) return -1\n if (baseLengthB > baseLengthA) return 1\n if (patternIndexA === -1) return 1\n if (patternIndexB === -1) return -1\n if (keyA.length > keyB.length) return -1\n if (keyB.length > keyA.length) return 1\n return 0\n}\n\nexports.packageTarget = function* (\n packageURL,\n target,\n patternMatch,\n isImports,\n opts = {}\n) {\n const { conditions = [], matchedConditions = [] } = opts\n\n if (typeof target === 'string') {\n if (!target.startsWith('./') && !isImports) {\n packageURL = new URL('package.json', packageURL)\n\n throw errors.INVALID_PACKAGE_TARGET(\n `Invalid target '${target}' defined by \"exports\" in '${packageURL}'`\n )\n }\n\n if (patternMatch !== null) {\n target = target.replaceAll('*', patternMatch)\n }\n\n const status = yield* exports.url(target, packageURL, opts)\n\n if (status) return status\n\n if (\n target === '.' ||\n target === '..' ||\n target[0] === '/' ||\n target.startsWith('./') ||\n target.startsWith('../')\n ) {\n const resolved = yield { resolution: new URL(target, packageURL) }\n\n return resolved ? RESOLVED : YIELDED\n }\n\n return yield* exports.package(target, packageURL, opts)\n }\n\n if (Array.isArray(target)) {\n for (const targetValue of target) {\n const status = yield* exports.packageTarget(\n packageURL,\n targetValue,\n patternMatch,\n isImports,\n opts\n )\n\n if (status) return status\n }\n } else if (typeof target === 'object' && target !== null) {\n let status = UNRESOLVED\n\n for (const [condition, targetValue, subset] of exports.conditionMatches(\n target,\n conditions,\n opts\n )) {\n matchedConditions.push(condition)\n\n status |= yield* exports.packageTarget(\n packageURL,\n targetValue,\n patternMatch,\n isImports,\n { ...opts, conditions: subset }\n )\n\n matchedConditions.pop()\n }\n\n if (status) return status\n }\n\n return UNRESOLVED\n}\n\nexports.builtinTarget = function* (\n packageSpecifier,\n packageVersion,\n target,\n opts = {}\n) {\n const {\n builtinProtocol = 'builtin:',\n conditions = [],\n matchedConditions = []\n } = opts\n\n if (typeof target === 'string') {\n const targetParts = target.split('@')\n\n let targetName\n let targetVersion\n\n if (target[0] !== '@') {\n targetName = targetParts[0]\n targetVersion = targetParts[1] || null\n } else {\n targetName = targetParts.slice(0, 2).join('@')\n targetVersion = targetParts[2] || null\n }\n\n if (packageSpecifier === targetName) {\n if (packageVersion === null && targetVersion === null) {\n const resolved = yield {\n resolution: new URL(builtinProtocol + packageSpecifier)\n }\n\n return resolved ? RESOLVED : YIELDED\n }\n\n let version = null\n\n if (packageVersion === null) {\n version = targetVersion\n } else if (targetVersion === null || packageVersion === targetVersion) {\n version = packageVersion\n }\n\n if (version !== null) {\n const resolved = yield {\n resolution: new URL(\n builtinProtocol + packageSpecifier + '@' + version\n )\n }\n\n return resolved ? RESOLVED : YIELDED\n }\n }\n } else if (Array.isArray(target)) {\n for (const targetValue of target) {\n const status = yield* exports.builtinTarget(\n packageSpecifier,\n packageVersion,\n targetValue,\n opts\n )\n\n if (status) return status\n }\n } else if (typeof target === 'object' && target !== null) {\n let status = UNRESOLVED\n\n for (const [condition, targetValue, subset] of exports.conditionMatches(\n target,\n conditions,\n opts\n )) {\n matchedConditions.push(condition)\n\n status |= yield* exports.builtinTarget(\n packageSpecifier,\n packageVersion,\n targetValue,\n { ...opts, conditions: subset }\n )\n\n matchedConditions.pop()\n }\n\n if (status) return status\n }\n\n return UNRESOLVED\n}\n\nexports.conditionMatches = function* conditionMatches(\n target,\n conditions,\n opts = {}\n) {\n if (conditions.every((condition) => typeof condition === 'string')) {\n const keys = Object.keys(target)\n\n for (const condition of keys) {\n if (condition === 'default' || conditions.includes(condition)) {\n yield [condition, target[condition], conditions]\n\n return true\n }\n }\n\n return false\n }\n\n let yielded = false\n\n for (const subset of conditions) {\n if (yield* conditionMatches(target, subset, opts)) {\n yielded = true\n }\n }\n\n return yielded\n}\n\nexports.lookupPackageScope = function* lookupPackageScope(url, opts = {}) {\n const { resolutions = null } = opts\n\n if (resolutions) {\n for (const { resolution } of exports.preresolved(\n '#package',\n resolutions,\n url,\n opts\n )) {\n if (resolution) return yield resolution\n }\n\n // Internal preresolution path, do not depend on this! It will be removed without\n // warning.\n for (const { resolution } of exports.preresolved(\n 'bare:package',\n resolutions,\n url,\n opts\n )) {\n if (resolution) return yield resolution\n }\n }\n\n const scopeURL = new URL(url.href)\n\n do {\n if (scopeURL.pathname.endsWith('/node_modules')) break\n\n yield new URL('package.json', scopeURL)\n\n scopeURL.pathname = scopeURL.pathname.substring(\n 0,\n scopeURL.pathname.lastIndexOf('/')\n )\n\n if (\n scopeURL.pathname.length === 3 &&\n exports.isWindowsDriveLetter(scopeURL.pathname.substring(1))\n ) {\n break\n }\n } while (scopeURL.pathname !== '' && scopeURL.pathname !== '/')\n}\n\nexports.file = function* (filename, parentURL, isIndex, opts = {}) {\n if (\n filename === '.' ||\n filename === '..' ||\n filename[filename.length - 1] === '/' ||\n filename[filename.length - 1] === '\\\\'\n ) {\n return UNRESOLVED\n }\n\n if (parentURL.protocol === 'file:' && /%2f|%5c/i.test(filename)) {\n throw errors.INVALID_MODULE_SPECIFIER(\n `Module specifier '${filename}' is invalid`\n )\n }\n\n const { extensions = [] } = opts\n\n let status = UNRESOLVED\n\n if (!isIndex) {\n if (yield { resolution: new URL(filename, parentURL) }) {\n return RESOLVED\n }\n\n status = YIELDED\n }\n\n for (const ext of extensions) {\n if (yield { resolution: new URL(filename + ext, parentURL) }) {\n return RESOLVED\n }\n\n status = YIELDED\n }\n\n return status\n}\n\nexports.directory = function* (dirname, parentURL, opts = {}) {\n let directoryURL\n\n if (\n dirname[dirname.length - 1] === '/' ||\n dirname[dirname.length - 1] === '\\\\'\n ) {\n directoryURL = new URL(dirname, parentURL)\n } else {\n directoryURL = new URL(dirname + '/', parentURL)\n }\n\n const info = yield { package: new URL('package.json', directoryURL) }\n\n if (info) {\n if (info.exports) {\n return yield* exports.packageExports(\n directoryURL,\n '.',\n info.exports,\n opts\n )\n }\n\n if (typeof info.main === 'string' && info.main !== '') {\n const status = yield* exports.file(info.main, directoryURL, false, opts)\n\n if (status === RESOLVED) return status\n\n return yield* exports.directory(info.main, directoryURL, opts)\n }\n }\n\n return yield* exports.file('index', directoryURL, true, opts)\n}\n\n// https://infra.spec.whatwg.org/#ascii-upper-alpha\nfunction isASCIIUpperAlpha(c) {\n return c >= 0x41 && c <= 0x5a\n}\n\n// https://infra.spec.whatwg.org/#ascii-lower-alpha\nfunction isASCIILowerAlpha(c) {\n return c >= 0x61 && c <= 0x7a\n}\n\n// https://infra.spec.whatwg.org/#ascii-alpha\nfunction isASCIIAlpha(c) {\n return isASCIIUpperAlpha(c) || isASCIILowerAlpha(c)\n}\n\n// https://url.spec.whatwg.org/#windows-drive-letter\nexports.isWindowsDriveLetter = function isWindowsDriveLetter(input) {\n return (\n input.length >= 2 &&\n isASCIIAlpha(input.charCodeAt(0)) &&\n (input.charCodeAt(1) === 0x3a || input.charCodeAt(1) === 0x7c)\n )\n}\n\n// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter\nexports.startsWithWindowsDriveLetter = function startsWithWindowsDriveLetter(\n input\n) {\n return (\n input.length >= 2 &&\n exports.isWindowsDriveLetter(input) &&\n (input.length === 2 ||\n input.charCodeAt(2) === 0x2f ||\n input.charCodeAt(2) === 0x5c ||\n input.charCodeAt(2) === 0x3f ||\n input.charCodeAt(2) === 0x23)\n )\n}\nmodule.exports = class ModuleResolveError extends Error {\n constructor(msg, code, fn = ModuleResolveError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'ModuleResolveError'\n }\n\n static INVALID_MODULE_SPECIFIER(msg) {\n return new ModuleResolveError(\n msg,\n 'INVALID_MODULE_SPECIFIER',\n ModuleResolveError.INVALID_MODULE_SPECIFIER\n )\n }\n\n static INVALID_PACKAGE_TARGET(msg) {\n return new ModuleResolveError(\n msg,\n 'INVALID_PACKAGE_TARGET',\n ModuleResolveError.INVALID_PACKAGE_TARGET\n )\n }\n\n static PACKAGE_PATH_NOT_EXPORTED(msg) {\n return new ModuleResolveError(\n msg,\n 'PACKAGE_PATH_NOT_EXPORTED',\n ModuleResolveError.PACKAGE_PATH_NOT_EXPORTED\n )\n }\n\n static PACKAGE_IMPORT_NOT_DEFINED(msg) {\n return new ModuleResolveError(\n msg,\n 'PACKAGE_IMPORT_NOT_DEFINED',\n ModuleResolveError.PACKAGE_IMPORT_NOT_DEFINED\n )\n }\n\n static UNSUPPORTED_ENGINE(msg) {\n return new ModuleResolveError(\n msg,\n 'UNSUPPORTED_ENGINE',\n ModuleResolveError.UNSUPPORTED_ENGINE\n )\n }\n}\n{\n \"name\": \"bare-module-resolve\",\n \"version\": \"1.11.1\",\n \"description\": \"Low-level module resolution algorithm for Bare\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./errors\": {\n \"types\": \"./lib/errors.d.ts\",\n \"default\": \"./lib/errors.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-module-resolve.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-module-resolve/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-module-resolve#readme\",\n \"dependencies\": {\n \"bare-semver\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"bare-url\": \"^2.1.3\",\n \"brittle\": \"^3.2.1\",\n \"prettier\": \"^3.3.3\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"peerDependencies\": {\n \"bare-url\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-url\": {\n \"optional\": true\n }\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = require.addon()\nconst binding = require('./binding')\nconst errors = require('./lib/errors')\nconst constants = require('./lib/constants')\n\nexports.constants = constants\n\nexports.EOL = binding.platform === 'win32' ? '\\r\\n' : '\\n'\n\nexports.platform = function platform() {\n return binding.platform\n}\n\nexports.arch = function arch() {\n return binding.arch\n}\n\nexports.type = binding.type\nexports.version = binding.version\nexports.release = binding.release\nexports.machine = binding.machine\nexports.execPath = binding.execPath\nexports.pid = binding.pid\nexports.ppid = binding.ppid\nexports.cwd = binding.cwd\nexports.chdir = binding.chdir\nexports.tmpdir = binding.tmpdir\nexports.homedir = binding.homedir\nexports.hostname = binding.hostname\nexports.userInfo = binding.userInfo\n\nexports.kill = function kill(pid, signal = constants.signals.SIGTERM) {\n if (typeof signal === 'string') {\n if (signal in constants.signals === false) {\n throw errors.UNKNOWN_SIGNAL('Unknown signal: ' + signal)\n }\n\n signal = constants.signals[signal]\n }\n\n binding.kill(pid, signal)\n}\n\nexports.endianness = function endianness() {\n return binding.isLittleEndian ? 'LE' : 'BE'\n}\n\nexports.availableParallelism = binding.availableParallelism\n\nexports.cpuUsage = function cpuUsage(previous) {\n const current = binding.cpuUsage()\n\n if (previous) {\n return {\n user: current.user - previous.user,\n system: current.system - previous.system\n }\n }\n\n return current\n}\n\nexports.threadCpuUsage = function threadCpuUsage(previous) {\n const current = binding.threadCpuUsage()\n\n if (previous) {\n return {\n user: current.user - previous.user,\n system: current.system - previous.system\n }\n }\n\n return current\n}\n\nexports.resourceUsage = binding.resourceUsage\nexports.memoryUsage = binding.memoryUsage\nexports.freemem = binding.freemem\nexports.totalmem = binding.totalmem\nexports.uptime = binding.uptime\nexports.loadavg = binding.loadavg\nexports.cpus = binding.cpus\n\nexports.getProcessTitle = binding.getProcessTitle\n\nexports.setProcessTitle = function setProcessTitle(title) {\n if (typeof title !== 'string') title = title.toString()\n\n if (title.length >= 256) {\n throw errors.TITLE_OVERFLOW('Process title is too long')\n }\n\n binding.setProcessTitle(title)\n}\n\nexports.getEnvKeys = binding.getEnvKeys\nexports.getEnv = binding.getEnv\nexports.hasEnv = binding.hasEnv\nexports.setEnv = binding.setEnv\nexports.unsetEnv = binding.unsetEnv\nconst binding = require('../binding')\n\nmodule.exports = {\n signals: binding.signals,\n errnos: binding.errnos\n}\nmodule.exports = class OSError extends Error {\n constructor(msg, code, fn = OSError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'OSError'\n }\n\n static UNKNOWN_SIGNAL(msg) {\n return new OSError(msg, 'UNKNOWN_SIGNAL', OSError.UNKNOWN_SIGNAL)\n }\n\n static TITLE_OVERFLOW(msg) {\n return new OSError(msg, 'TITLE_OVERFLOW', OSError.TITLE_OVERFLOW)\n }\n}\n{\n \"name\": \"bare-os\",\n \"version\": \"3.6.1\",\n \"description\": \"Operating system utilities for Javascript\",\n \"exports\": {\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./package\": \"./package.json\",\n \"./constants\": \"./lib/constants.js\",\n \"./errors\": \"./lib/errors.js\"\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"binding.c\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"lib\",\n \"prebuilds\"\n ],\n \"addon\": true,\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-os.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-os/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-os#readme\",\n \"engines\": {\n \"bare\": \">=1.14.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.1\",\n \"cmake-bare\": \"^1.1.6\",\n \"prettier\": \"^3.4.2\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n/* global Bare */\n\n// This export SHOULD NOT be shortened in any way as having the full\n// `module.exports = require(...)` statement is crucial for synthesizing\n// ESM exports.\n\nif (Bare.platform === 'win32') {\n module.exports = require('./lib/win32')\n} else {\n module.exports = require('./lib/posix')\n}\nmodule.exports = {\n CHAR_UPPERCASE_A: 0x41,\n CHAR_LOWERCASE_A: 0x61,\n CHAR_UPPERCASE_Z: 0x5a,\n CHAR_LOWERCASE_Z: 0x7a,\n CHAR_DOT: 0x2e,\n CHAR_FORWARD_SLASH: 0x2f,\n CHAR_BACKWARD_SLASH: 0x5c,\n CHAR_COLON: 0x3a,\n CHAR_QUESTION_MARK: 0x3f\n}\nconst os = require('bare-os')\n\nconst { normalizeString } = require('./shared')\nconst {\n CHAR_DOT,\n CHAR_FORWARD_SLASH\n} = require('./constants')\n\nfunction isPosixPathSeparator (code) {\n return code === CHAR_FORWARD_SLASH\n}\n\nexports.win32 = require('./win32')\nexports.posix = exports\n\nexports.sep = '/'\nexports.delimiter = ':'\n\nexports.resolve = function resolve (...args) {\n let resolvedPath = ''\n let resolvedAbsolute = false\n\n for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {\n const path = i >= 0 ? args[i] : os.cwd()\n\n if (path.length === 0) {\n continue\n }\n\n resolvedPath = `${path}/${resolvedPath}`\n resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH\n }\n\n resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator)\n\n if (resolvedAbsolute) {\n return `/${resolvedPath}`\n }\n\n return resolvedPath.length > 0 ? resolvedPath : '.'\n}\n\nexports.normalize = function normalize (path) {\n if (path.length === 0) return '.'\n\n const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH\n const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH\n\n path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator)\n\n if (path.length === 0) {\n if (isAbsolute) return '/'\n return trailingSeparator ? './' : '.'\n }\n\n if (trailingSeparator) path += '/'\n\n return isAbsolute ? `/${path}` : path\n}\n\nexports.isAbsolute = function isAbsolute (path) {\n return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH\n}\n\nexports.join = function join (...args) {\n if (args.length === 0) return '.'\n let joined\n for (let i = 0; i < args.length; ++i) {\n const arg = args[i]\n if (arg.length > 0) {\n if (joined === undefined) joined = arg\n else joined += `/${arg}`\n }\n }\n if (joined === undefined) return '.'\n return exports.normalize(joined)\n}\n\nexports.relative = function relative (from, to) {\n if (from === to) return ''\n\n from = exports.resolve(from)\n to = exports.resolve(to)\n\n if (from === to) return ''\n\n const fromStart = 1\n const fromEnd = from.length\n const fromLen = fromEnd - fromStart\n const toStart = 1\n const toLen = to.length - toStart\n\n const length = (fromLen < toLen ? fromLen : toLen)\n let lastCommonSep = -1\n let i = 0\n for (; i < length; i++) {\n const fromCode = from.charCodeAt(fromStart + i)\n if (fromCode !== to.charCodeAt(toStart + i)) {\n break\n } else if (fromCode === CHAR_FORWARD_SLASH) {\n lastCommonSep = i\n }\n }\n if (i === length) {\n if (toLen > length) {\n if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {\n return to.substring(toStart + i + 1)\n }\n if (i === 0) {\n return to.substring(toStart + i)\n }\n } else if (fromLen > length) {\n if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {\n lastCommonSep = i\n } else if (i === 0) {\n lastCommonSep = 0\n }\n }\n }\n\n let out = ''\n for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {\n if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {\n out += out.length === 0 ? '..' : '/..'\n }\n }\n\n return `${out}${to.substring(toStart + lastCommonSep)}`\n}\n\nexports.toNamespacedPath = function toNamespacedPath (path) {\n return path\n}\n\nexports.dirname = function dirname (path) {\n if (path.length === 0) return '.'\n const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH\n let end = -1\n let matchedSlash = true\n for (let i = path.length - 1; i >= 1; --i) {\n if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {\n if (!matchedSlash) {\n end = i\n break\n }\n } else {\n matchedSlash = false\n }\n }\n\n if (end === -1) return hasRoot ? '/' : '.'\n if (hasRoot && end === 1) return '//'\n return path.substring(0, end)\n}\n\nexports.basename = function basename (path, suffix) {\n let start = 0\n let end = -1\n let matchedSlash = true\n\n if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {\n if (suffix === path) { return '' }\n let extIdx = suffix.length - 1\n let firstNonSlashEnd = -1\n for (let i = path.length - 1; i >= 0; --i) {\n const code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else {\n if (firstNonSlashEnd === -1) {\n matchedSlash = false\n firstNonSlashEnd = i + 1\n }\n if (extIdx >= 0) {\n if (code === suffix.charCodeAt(extIdx)) {\n if (--extIdx === -1) {\n end = i\n }\n } else {\n extIdx = -1\n end = firstNonSlashEnd\n }\n }\n }\n }\n\n if (start === end) end = firstNonSlashEnd\n else if (end === -1) end = path.length\n return path.substring(start, end)\n }\n\n for (let i = path.length - 1; i >= 0; --i) {\n if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else if (end === -1) {\n matchedSlash = false\n end = i + 1\n }\n }\n\n if (end === -1) return ''\n return path.substring(start, end)\n}\n\nexports.extname = function extname (path) {\n let startDot = -1\n let startPart = 0\n let end = -1\n let matchedSlash = true\n let preDotState = 0\n for (let i = path.length - 1; i >= 0; --i) {\n const code = path.charCodeAt(i)\n if (code === CHAR_FORWARD_SLASH) {\n if (!matchedSlash) {\n startPart = i + 1\n break\n }\n continue\n }\n if (end === -1) {\n matchedSlash = false\n end = i + 1\n }\n if (code === CHAR_DOT) {\n if (startDot === -1) startDot = i\n else if (preDotState !== 1) preDotState = 1\n } else if (startDot !== -1) {\n preDotState = -1\n }\n }\n\n if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {\n return ''\n }\n return path.substring(startDot, end)\n}\nconst {\n CHAR_DOT,\n CHAR_FORWARD_SLASH\n} = require('./constants')\n\nexports.normalizeString = function normalizeString (path, allowAboveRoot, separator, isPathSeparator) {\n let res = ''\n let lastSegmentLength = 0\n let lastSlash = -1\n let dots = 0\n let code = 0\n for (let i = 0; i <= path.length; ++i) {\n if (i < path.length) {\n code = path.charCodeAt(i)\n } else if (isPathSeparator(code)) {\n break\n } else {\n code = CHAR_FORWARD_SLASH\n }\n\n if (isPathSeparator(code)) {\n if (lastSlash === i - 1 || dots === 1) ;\n else if (dots === 2) {\n if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) {\n if (res.length > 2) {\n const lastSlashIndex = res.lastIndexOf(separator)\n if (lastSlashIndex === -1) {\n res = ''\n lastSegmentLength = 0\n } else {\n res = res.substring(0, lastSlashIndex)\n lastSegmentLength =\n res.length - 1 - res.lastIndexOf(separator)\n }\n lastSlash = i\n dots = 0\n continue\n } else if (res.length !== 0) {\n res = ''\n lastSegmentLength = 0\n lastSlash = i\n dots = 0\n continue\n }\n }\n if (allowAboveRoot) {\n res += res.length > 0 ? `${separator}..` : '..'\n lastSegmentLength = 2\n }\n } else {\n if (res.length > 0) {\n res += `${separator}${path.substring(lastSlash + 1, i)}`\n } else {\n res = path.substring(lastSlash + 1, i)\n }\n lastSegmentLength = i - lastSlash - 1\n }\n lastSlash = i\n dots = 0\n } else if (code === CHAR_DOT && dots !== -1) {\n ++dots\n } else {\n dots = -1\n }\n }\n return res\n}\nconst os = require('bare-os')\n\nconst { normalizeString } = require('./shared')\nconst {\n CHAR_UPPERCASE_A,\n CHAR_LOWERCASE_A,\n CHAR_UPPERCASE_Z,\n CHAR_LOWERCASE_Z,\n CHAR_DOT,\n CHAR_FORWARD_SLASH,\n CHAR_BACKWARD_SLASH,\n CHAR_COLON,\n CHAR_QUESTION_MARK\n} = require('./constants')\n\nfunction isWindowsPathSeparator (code) {\n return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH\n}\n\nfunction isWindowsDeviceRoot (code) {\n return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||\n (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z)\n}\n\nexports.posix = require('./posix')\nexports.win32 = exports\n\nexports.sep = '\\\\'\nexports.delimiter = ';'\n\nexports.resolve = function resolve (...args) {\n let resolvedDevice = ''\n let resolvedTail = ''\n let resolvedAbsolute = false\n\n for (let i = args.length - 1; i >= -1; i--) {\n let path\n if (i >= 0) {\n path = args[i]\n\n if (path.length === 0) continue\n } else if (resolvedDevice.length === 0) {\n path = os.cwd()\n } else {\n path = os.getEnv(`=${resolvedDevice}`) || os.cwd()\n\n if (path === undefined || (path.substring(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && path.charCodeAt(2) === CHAR_BACKWARD_SLASH)) {\n path = `${resolvedDevice}\\\\`\n }\n }\n\n const len = path.length\n let rootEnd = 0\n let device = ''\n let isAbsolute = false\n const code = path.charCodeAt(0)\n\n if (len === 1) {\n if (isWindowsPathSeparator(code)) {\n rootEnd = 1\n isAbsolute = true\n }\n } else if (isWindowsPathSeparator(code)) {\n isAbsolute = true\n\n if (isWindowsPathSeparator(path.charCodeAt(1))) {\n let j = 2\n let last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n const firstPart = path.substring(last, j)\n last = j\n while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j === len || j !== last) {\n device = `\\\\\\\\${firstPart}\\\\${path.substring(last, j)}`\n rootEnd = j\n }\n }\n }\n } else {\n rootEnd = 1\n }\n } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {\n device = path.substring(0, 2)\n rootEnd = 2\n if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) {\n isAbsolute = true\n rootEnd = 3\n }\n }\n\n if (device.length > 0) {\n if (resolvedDevice.length > 0) {\n if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { continue }\n } else {\n resolvedDevice = device\n }\n }\n\n if (resolvedAbsolute) {\n if (resolvedDevice.length > 0) { break }\n } else {\n resolvedTail = `${path.substring(rootEnd)}\\\\${resolvedTail}`\n resolvedAbsolute = isAbsolute\n if (isAbsolute && resolvedDevice.length > 0) {\n break\n }\n }\n }\n\n resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\\\', isWindowsPathSeparator)\n\n return resolvedAbsolute ? `${resolvedDevice}\\\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.'\n}\n\nexports.normalize = function normalize (path) {\n const len = path.length\n if (len === 0) return '.'\n let rootEnd = 0\n let device\n let isAbsolute = false\n const code = path.charCodeAt(0)\n\n if (len === 1) {\n return code === CHAR_FORWARD_SLASH ? '\\\\' : path\n }\n\n if (isWindowsPathSeparator(code)) {\n isAbsolute = true\n\n if (isWindowsPathSeparator(path.charCodeAt(1))) {\n let j = 2\n let last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n const firstPart = path.substring(last, j)\n last = j\n while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j === len) {\n return `\\\\\\\\${firstPart}\\\\${path.substring(last)}\\\\`\n }\n if (j !== last) {\n device = `\\\\\\\\${firstPart}\\\\${path.substring(last, j)}`\n rootEnd = j\n }\n }\n }\n } else {\n rootEnd = 1\n }\n } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {\n device = path.substring(0, 2)\n rootEnd = 2\n if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) {\n isAbsolute = true\n rootEnd = 3\n }\n }\n\n let tail = rootEnd < len ? normalizeString(path.substring(rootEnd), !isAbsolute, '\\\\', isWindowsPathSeparator) : ''\n if (tail.length === 0 && !isAbsolute) {\n tail = '.'\n }\n if (tail.length > 0 && isWindowsPathSeparator(path.charCodeAt(len - 1))) {\n tail += '\\\\'\n }\n if (device === undefined) {\n return isAbsolute ? `\\\\${tail}` : tail\n }\n return isAbsolute ? `${device}\\\\${tail}` : `${device}${tail}`\n}\n\nexports.isAbsolute = function isAbsolute (path) {\n const len = path.length\n if (len === 0) return false\n\n const code = path.charCodeAt(0)\n\n return isWindowsPathSeparator(code) || (len > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isWindowsPathSeparator(path.charCodeAt(2)))\n}\n\nexports.join = function join (...args) {\n if (args.length === 0) return '.'\n\n let joined\n let firstPart\n for (let i = 0; i < args.length; ++i) {\n const arg = args[i]\n if (arg.length > 0) {\n if (joined === undefined) joined = firstPart = arg\n else joined += `\\\\${arg}`\n }\n }\n\n if (joined === undefined) return '.'\n\n let needsReplace = true\n let slashCount = 0\n if (isWindowsPathSeparator(firstPart.charCodeAt(0))) {\n ++slashCount\n const firstLen = firstPart.length\n if (firstLen > 1 && isWindowsPathSeparator(firstPart.charCodeAt(1))) {\n ++slashCount\n if (firstLen > 2) {\n if (isWindowsPathSeparator(firstPart.charCodeAt(2))) {\n ++slashCount\n } else {\n needsReplace = false\n }\n }\n }\n }\n if (needsReplace) {\n while (slashCount < joined.length && isWindowsPathSeparator(joined.charCodeAt(slashCount))) {\n slashCount++\n }\n\n if (slashCount >= 2) {\n joined = `\\\\${joined.substring(slashCount)}`\n }\n }\n\n return exports.normalize(joined)\n}\n\nexports.relative = function relative (from, to) {\n if (from === to) return ''\n\n const fromOrig = exports.resolve(from)\n const toOrig = exports.resolve(to)\n\n if (fromOrig === toOrig) return ''\n\n from = fromOrig.toLowerCase()\n to = toOrig.toLowerCase()\n\n if (from === to) return ''\n\n let fromStart = 0\n while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) {\n fromStart++\n }\n let fromEnd = from.length\n while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) {\n fromEnd--\n }\n const fromLen = fromEnd - fromStart\n\n let toStart = 0\n while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {\n toStart++\n }\n let toEnd = to.length\n while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) {\n toEnd--\n }\n const toLen = toEnd - toStart\n\n const length = fromLen < toLen ? fromLen : toLen\n let lastCommonSep = -1\n let i = 0\n for (; i < length; i++) {\n const fromCode = from.charCodeAt(fromStart + i)\n if (fromCode !== to.charCodeAt(toStart + i)) {\n break\n } else if (fromCode === CHAR_BACKWARD_SLASH) {\n lastCommonSep = i\n }\n }\n\n if (i !== length) {\n if (lastCommonSep === -1) return toOrig\n } else {\n if (toLen > length) {\n if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {\n return toOrig.substring(toStart + i + 1)\n }\n if (i === 2) {\n return toOrig.substring(toStart + i)\n }\n }\n if (fromLen > length) {\n if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {\n lastCommonSep = i\n } else if (i === 2) {\n lastCommonSep = 3\n }\n }\n if (lastCommonSep === -1) lastCommonSep = 0\n }\n\n let out = ''\n for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {\n if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {\n out += out.length === 0 ? '..' : '\\\\..'\n }\n }\n\n toStart += lastCommonSep\n\n if (out.length > 0) {\n return `${out}${toOrig.substring(toStart, toEnd)}`\n }\n if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {\n ++toStart\n }\n return toOrig.substring(toStart, toEnd)\n}\n\nexports.toNamespacedPath = function toNamespacedPath (path) {\n if (path.length === 0) return path\n\n const resolvedPath = exports.resolve(path)\n\n if (resolvedPath.length <= 2) return path\n\n if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {\n if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {\n const code = resolvedPath.charCodeAt(2)\n if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {\n return `\\\\\\\\?\\\\UNC\\\\${resolvedPath.substring(2)}`\n }\n }\n } else if (\n isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) &&\n resolvedPath.charCodeAt(1) === CHAR_COLON &&\n resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH\n ) {\n return `\\\\\\\\?\\\\${resolvedPath}`\n }\n\n return path\n}\n\nexports.dirname = function dirname (path) {\n const len = path.length\n if (len === 0) return '.'\n let rootEnd = -1\n let offset = 0\n const code = path.charCodeAt(0)\n\n if (len === 1) {\n return isWindowsPathSeparator(code) ? path : '.'\n }\n\n if (isWindowsPathSeparator(code)) {\n rootEnd = offset = 1\n\n if (isWindowsPathSeparator(path.charCodeAt(1))) {\n let j = 2\n let last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n last = j\n while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j < len && j !== last) {\n last = j\n while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {\n j++\n }\n if (j === len) {\n return path\n }\n if (j !== last) {\n rootEnd = offset = j + 1\n }\n }\n }\n }\n } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {\n rootEnd = len > 2 && isWindowsPathSeparator(path.charCodeAt(2)) ? 3 : 2\n offset = rootEnd\n }\n\n let end = -1\n let matchedSlash = true\n for (let i = len - 1; i >= offset; --i) {\n if (isWindowsPathSeparator(path.charCodeAt(i))) {\n if (!matchedSlash) {\n end = i\n break\n }\n } else {\n matchedSlash = false\n }\n }\n\n if (end === -1) {\n if (rootEnd === -1) return '.'\n\n end = rootEnd\n }\n return path.substring(0, end)\n}\n\nexports.basename = function basename (path, suffix) {\n let start = 0\n let end = -1\n let matchedSlash = true\n\n if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) {\n start = 2\n }\n\n if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {\n if (suffix === path) return ''\n let extIdx = suffix.length - 1\n let firstNonSlashEnd = -1\n for (let i = path.length - 1; i >= start; --i) {\n const code = path.charCodeAt(i)\n if (isWindowsPathSeparator(code)) {\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else {\n if (firstNonSlashEnd === -1) {\n matchedSlash = false\n firstNonSlashEnd = i + 1\n }\n if (extIdx >= 0) {\n if (code === suffix.charCodeAt(extIdx)) {\n if (--extIdx === -1) {\n end = i\n }\n } else {\n extIdx = -1\n end = firstNonSlashEnd\n }\n }\n }\n }\n\n if (start === end) end = firstNonSlashEnd\n else if (end === -1) end = path.length\n return path.substring(start, end)\n }\n for (let i = path.length - 1; i >= start; --i) {\n if (isWindowsPathSeparator(path.charCodeAt(i))) {\n if (!matchedSlash) {\n start = i + 1\n break\n }\n } else if (end === -1) {\n matchedSlash = false\n end = i + 1\n }\n }\n\n if (end === -1) return ''\n return path.substring(start, end)\n}\n\nexports.extname = function extname (path) {\n let start = 0\n let startDot = -1\n let startPart = 0\n let end = -1\n let matchedSlash = true\n let preDotState = 0\n\n if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) {\n start = startPart = 2\n }\n\n for (let i = path.length - 1; i >= start; --i) {\n const code = path.charCodeAt(i)\n if (isWindowsPathSeparator(code)) {\n if (!matchedSlash) {\n startPart = i + 1\n break\n }\n continue\n }\n if (end === -1) {\n matchedSlash = false\n end = i + 1\n }\n if (code === CHAR_DOT) {\n if (startDot === -1) startDot = i\n else if (preDotState !== 1) preDotState = 1\n } else if (startDot !== -1) {\n preDotState = -1\n }\n }\n\n if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {\n return ''\n }\n return path.substring(startDot, end)\n}\n{\n \"name\": \"bare-path\",\n \"version\": \"3.0.0\",\n \"description\": \"Path manipulation library for JavaScript\",\n \"exports\": {\n \".\": \"./index.js\",\n \"./package\": \"./package.json\",\n \"./posix\": \"./lib/posix.js\",\n \"./win32\": \"./lib/win32.js\"\n },\n \"files\": [\n \"index.js\",\n \"lib\",\n \"NOTICE\"\n ],\n \"scripts\": {\n \"test\": \"standard && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-path.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-path/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-path#readme\",\n \"dependencies\": {\n \"bare-os\": \"^3.0.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"standard\": \"^17.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst safetyCatch = require('safety-catch')\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\nconst m = require('./lib/messages')\nconst { type: t, stream: s } = require('./lib/constants')\nconst IncomingRequest = require('./lib/incoming-request')\nconst IncomingStream = require('./lib/incoming-stream')\nconst OutgoingRequest = require('./lib/outgoing-request')\nconst OutgoingStream = require('./lib/outgoing-stream')\nconst CommandRouter = require('./lib/command-router')\n\nmodule.exports = exports = class RPC {\n constructor(stream, onrequest = noop) {\n this._stream = stream\n\n this._id = 0\n\n this._outgoingRequests = new Map()\n this._outgoingResponses = new Map()\n this._incomingRequests = new Map()\n this._incomingResponses = new Map()\n this._pendingRequests = new Set()\n this._pendingResponses = new Set()\n\n this._buffer = []\n this._buffered = 0\n this._frame = -1\n\n if (typeof onrequest === 'function') {\n onrequest = onrequest.bind(this)\n } else {\n onrequest = onrequest._onrequest.bind(onrequest)\n }\n\n this._onrequest = onrequest\n this._ondata = this._ondata.bind(this)\n\n this._stream.on('data', this._ondata)\n }\n\n request(command) {\n return new OutgoingRequest(this, ++this._id, command)\n }\n\n _sendMessage(message) {\n this._stream.write(c.encode(m.message, message))\n }\n\n _sendRequest(request, data = null) {\n this._outgoingRequests.set(request.id, request)\n\n this._sendMessage({\n type: t.REQUEST,\n id: request.id,\n command: request.command,\n stream: 0,\n data\n })\n }\n\n _createRequestStream(request, isInitiator, opts) {\n if (isInitiator) {\n this._outgoingRequests.set(request.id, request)\n\n request._requestStream = new OutgoingStream(\n this,\n request,\n t.REQUEST,\n opts\n )\n } else {\n this._incomingRequests.set(request.id, request)\n\n request._requestStream = new IncomingStream(\n this,\n request,\n t.REQUEST,\n opts\n )\n\n request._requestStream.on('close', () =>\n this._incomingRequests.delete(request.id)\n )\n }\n }\n\n _sendResponse(request, data) {\n this._sendMessage({\n type: t.RESPONSE,\n id: request.id,\n stream: 0,\n error: null,\n data\n })\n }\n\n _createResponseStream(request, isInitiator, opts) {\n if (isInitiator) {\n this._outgoingResponses.set(request.id, request)\n\n request._responseStream = new OutgoingStream(\n this,\n request,\n t.RESPONSE,\n opts\n )\n } else {\n this._incomingResponses.set(request.id, request)\n\n request._responseStream = new IncomingStream(\n this,\n request,\n t.RESPONSE,\n opts\n )\n\n request._responseStream.on('close', () =>\n this._incomingResponses.delete(request.id)\n )\n }\n }\n\n _sendError(request, err) {\n this._sendMessage({\n type: t.RESPONSE,\n id: request.id,\n stream: 0,\n error: err,\n data: null\n })\n }\n\n _ondata(data) {\n this._buffer.push(data)\n this._buffered += data.byteLength\n\n if (this._frame === -1) {\n this._onbeforeframe()\n } else {\n this._onafterframe()\n }\n }\n\n _onbeforeframe() {\n if (this._buffered < 4) return\n\n const buffer =\n this._buffer.length === 1 ? this._buffer[0] : b4a.concat(this._buffer)\n\n this._buffer = [buffer]\n this._frame = 4 + c.uint32.decode(c.state(0, 4, buffer))\n\n this._onafterframe()\n }\n\n _onafterframe() {\n if (this._buffered < this._frame) return\n\n const buffer =\n this._buffer.length === 1 ? this._buffer[0] : b4a.concat(this._buffer)\n\n const frame = this._frame\n\n this._buffered -= frame\n this._buffer = this._buffered > 0 ? [buffer.subarray(frame)] : []\n this._frame = -1\n\n this._onmessage(buffer.subarray(0, frame))\n this._onbeforeframe()\n }\n\n async _onmessage(buffer) {\n let message\n try {\n message = m.message.decode(c.state(0, buffer.length, buffer))\n } catch (err) {\n safetyCatch(err)\n\n return this._stream.destroy(err)\n }\n\n switch (message.type) {\n case t.REQUEST:\n const request = new IncomingRequest(\n this,\n message.id,\n message.command,\n message.data\n )\n\n try {\n await this._onrequest(request)\n } catch (err) {\n safetyCatch(err)\n\n this._sendError(request, err)\n }\n break\n case t.RESPONSE:\n try {\n this._onresponse(message)\n } catch (err) {\n safetyCatch(err)\n }\n break\n case t.STREAM:\n try {\n this._onstream(message)\n } catch (err) {\n safetyCatch(err)\n }\n }\n }\n\n _onresponse(message) {\n if (message.id === 0) return\n\n const request = this._outgoingRequests.get(message.id)\n if (request === undefined) return\n\n if (message.error) {\n request._reject(message.error)\n } else if (message.stream === 0) {\n request._resolve(message.data)\n }\n }\n\n _onstream(message) {\n if (message.id === 0) return\n\n if (message.stream & s.OPEN) this._onstreamopen(message)\n else if (message.stream & s.CLOSE) this._onstreamclose(message)\n else if (message.stream & s.PAUSE) this._onstreampause(message)\n else if (message.stream & s.RESUME) this._onstreamresume(message)\n else if (message.stream & s.DATA) this._onstreamdata(message)\n else if (message.stream & s.END) this._onstreamend(message)\n else if (message.stream & s.DESTROY) this._onstreamdestroy(message)\n }\n\n _onstreamopen(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._outgoingRequests.get(message.id)\n if (request === undefined) {\n this._pendingRequests.add(message.id)\n return\n }\n\n stream = request._requestStream\n\n if (stream._pendingOpen === null) {\n this._pendingRequests.add(message.id)\n return\n }\n } else if (message.stream & s.RESPONSE) {\n const request = this._outgoingResponses.get(message.id)\n if (request === undefined) {\n this._pendingResponses.add(message.id)\n return\n }\n\n stream = request._responseStream\n\n if (stream._pendingOpen === null) {\n this._pendingResponses.add(message.id)\n return\n }\n } else {\n return\n }\n\n stream._continueOpen()\n }\n\n _onstreamclose(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._incomingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._incomingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n if (message.error) stream.destroy(message.error)\n else stream.push(null)\n }\n\n _onstreampause(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._outgoingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._outgoingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n stream.cork()\n }\n\n _onstreamresume(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._outgoingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._outgoingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n stream.uncork()\n }\n\n _onstreamdata(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._incomingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._incomingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n if (stream.push(message.data) === false) {\n this._sendMessage({\n type: t.STREAM,\n id: stream._request.id,\n stream: stream._mask | s.PAUSE,\n error: null,\n data: null\n })\n }\n }\n\n _onstreamend(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._incomingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._incomingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n stream.push(null)\n }\n\n _onstreamdestroy(message) {\n let stream\n\n if (message.stream & s.REQUEST) {\n const request = this._outgoingRequests.get(message.id)\n if (request === undefined) return\n\n stream = request._requestStream\n } else if (message.stream & s.RESPONSE) {\n const request = this._outgoingResponses.get(message.id)\n if (request === undefined) return\n\n stream = request._responseStream\n } else {\n return\n }\n\n stream.destroy(message.error)\n }\n}\n\nexports.CommandRouter = CommandRouter\n\nfunction noop() {}\nconst c = require('compact-encoding')\n\nmodule.exports = class RPCCommandRouter {\n constructor(opts = {}) {\n const { valueEncoding = c.raw } = opts\n\n this._responders = new Map()\n this._defaultValueEncoding = valueEncoding\n }\n\n respond(command, opts = {}, onrequest) {\n if (typeof opts === 'function') {\n onrequest = opts\n opts = {}\n }\n\n const {\n valueEncoding = this._defaultValueEncoding,\n requestEncoding = valueEncoding,\n responseEncoding = valueEncoding\n } = opts\n\n this._responders.set(command, {\n onrequest,\n requestEncoding,\n responseEncoding\n })\n }\n\n async _onrequest(req) {\n const responder = this._responders.get(req.command)\n\n if (responder === undefined) return\n\n const { onrequest, requestEncoding, responseEncoding } = responder\n\n let data = req.data\n\n if (requestEncoding) data = c.decode(requestEncoding, data)\n\n data = await onrequest(req, data)\n\n if (req.sent) return\n\n if (responseEncoding) data = c.encode(responseEncoding, data)\n\n req.reply(data)\n }\n}\nmodule.exports = {\n type: {\n REQUEST: 1,\n RESPONSE: 2,\n STREAM: 3\n },\n stream: {\n OPEN: 0x1,\n CLOSE: 0x2,\n PAUSE: 0x4,\n RESUME: 0x8,\n DATA: 0x10,\n END: 0x20,\n DESTROY: 0x40,\n ERROR: 0x80,\n REQUEST: 0x100,\n RESPONSE: 0x200\n }\n}\nmodule.exports = class RPCError extends Error {\n constructor(msg, code, fn = RPCError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'RPCError'\n }\n\n static UNKNOWN_MESSAGE(msg) {\n return new RPCError(msg, 'UNKNOWN_MESSAGE', RPCError.UNKNOWN_MESSAGE)\n }\n\n static ALREADY_SENT(msg) {\n return new RPCError(msg, 'ALREADY_SENT', RPCError.ALREADY_SENT)\n }\n\n static ALREADY_RECEIVED(msg) {\n return new RPCError(msg, 'ALREADY_RECEIVED', RPCError.ALREADY_RECEIVED)\n }\n}\nconst b4a = require('b4a')\nconst errors = require('./errors')\n\nmodule.exports = class RPCIncomingRequest {\n constructor(rpc, id, command, data) {\n this.rpc = rpc\n this.id = id\n this.command = command\n this.data = data\n this.sent = false\n this.received = false\n\n this._requestStream = null\n this._responseStream = null\n }\n\n reply(data, encoding) {\n if (this.sent) {\n throw errors.ALREADY_SENT('Response has already been sent')\n }\n\n this.sent = true\n\n this.rpc._sendResponse(\n this,\n typeof data === 'string' ? b4a.from(data, encoding) : data\n )\n }\n\n createResponseStream(opts = {}) {\n if (this.sent) {\n throw errors.ALREADY_SENT('Response has already been sent')\n }\n\n this.sent = true\n\n this.rpc._createResponseStream(this, true, opts)\n\n return this._responseStream\n }\n\n createRequestStream(opts = {}) {\n if (this.received) {\n throw errors.ALREADY_RECEIVED('Request has already been received')\n }\n\n this.received = true\n\n this.rpc._createRequestStream(this, false, opts)\n\n return this._requestStream\n }\n}\nconst { Readable } = require('bare-stream')\nconst { type: t, stream: s } = require('./constants')\n\nmodule.exports = class RPCIncomingStream extends Readable {\n constructor(rpc, request, type, opts) {\n super({ ...opts, eagerOpen: true })\n\n this._rpc = rpc\n this._request = request\n this._type = type\n this._mask = type === t.REQUEST ? s.REQUEST : s.RESPONSE\n }\n\n _open(cb) {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.OPEN,\n error: null,\n data: null\n })\n\n cb(null)\n }\n\n _read() {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.RESUME,\n error: null,\n data: null\n })\n }\n\n _destroy(err, cb) {\n if (err) {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.DESTROY | s.ERROR,\n error: err,\n data: null\n })\n } else {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.DESTROY,\n error: null,\n data: null\n })\n }\n\n cb(null)\n }\n}\nconst c = require('compact-encoding')\nconst { type: t, stream: s } = require('./constants')\nconst errors = require('./errors')\n\nconst request = {\n preencode(state, m) {\n c.uint.preencode(state, m.command)\n c.uint.preencode(state, m.stream)\n if (m.stream === 0) c.buffer.preencode(state, m.data)\n },\n encode(state, m) {\n c.uint.encode(state, m.command)\n c.uint.encode(state, m.stream)\n if (m.stream === 0) c.buffer.encode(state, m.data)\n },\n decode(state, id) {\n const command = c.uint.decode(state)\n const stream = c.uint.decode(state)\n const data = stream === 0 ? c.buffer.decode(state) : null\n\n return { type: t.REQUEST, id, command, stream, data }\n }\n}\n\nconst error = {\n preencode(state, m) {\n c.utf8.preencode(state, m.message)\n c.utf8.preencode(state, m.code || '')\n c.int.preencode(state, m.errno || 0)\n },\n encode(state, m) {\n c.utf8.encode(state, m.message)\n c.utf8.encode(state, m.code || '')\n c.int.encode(state, m.errno || 0)\n },\n decode(state) {\n const err = new Error(`${c.utf8.decode(state)}`)\n err.code = c.utf8.decode(state)\n err.errno = c.int.decode(state)\n return err\n }\n}\n\nconst response = {\n preencode(state, m) {\n c.bool.preencode(state, !!m.error)\n c.uint.preencode(state, m.stream)\n\n if (m.error) error.preencode(state, m.error)\n else if (m.stream === 0) c.buffer.preencode(state, m.data)\n },\n encode(state, m) {\n c.bool.encode(state, !!m.error)\n c.uint.encode(state, m.stream)\n\n if (m.error) error.encode(state, m.error)\n else if (m.stream === 0) c.buffer.encode(state, m.data)\n },\n decode(state) {\n const err = c.bool.decode(state)\n const stream = c.uint.decode(state)\n\n if (err) {\n return { stream, error: error.decode(state), data: null }\n }\n\n if (stream === 0) {\n return { stream, error: null, data: c.buffer.decode(state) }\n }\n\n return { stream, error: null, data: null }\n }\n}\n\nconst stream = {\n preencode(state, m) {\n c.uint.preencode(state, m.stream)\n\n if (m.stream & s.ERROR) error.preencode(state, m.error)\n else if (m.stream & s.DATA) c.buffer.preencode(state, m.data)\n },\n encode(state, m) {\n c.uint.encode(state, m.stream)\n\n if (m.stream & s.ERROR) error.encode(state, m.error)\n else if (m.stream & s.DATA) c.buffer.encode(state, m.data)\n },\n decode(state) {\n const stream = c.uint.decode(state)\n\n if (stream & s.ERROR)\n return { stream, error: error.decode(state), data: null }\n if (stream & s.DATA)\n return { stream, error: null, data: c.buffer.decode(state) }\n\n return { stream, error: null, data: null }\n }\n}\n\nexports.message = {\n preencode(state, m) {\n c.uint32.preencode(state, 0) // Frame\n c.uint.preencode(state, m.type)\n c.uint.preencode(state, m.id)\n\n switch (m.type) {\n case t.REQUEST:\n request.preencode(state, m)\n break\n case t.RESPONSE:\n response.preencode(state, m)\n break\n case t.STREAM:\n stream.preencode(state, m)\n break\n }\n },\n encode(state, m) {\n const frame = state.start\n\n c.uint32.encode(state, 0) // Frame\n\n const start = state.start\n\n c.uint.encode(state, m.type)\n c.uint.encode(state, m.id)\n\n switch (m.type) {\n case t.REQUEST:\n request.encode(state, m)\n break\n case t.RESPONSE:\n response.encode(state, m)\n break\n case t.STREAM:\n stream.encode(state, m)\n break\n }\n\n const end = state.start\n\n state.start = frame\n\n c.uint32.encode(state, end - start)\n\n state.start = end\n },\n decode(state) {\n const frame = c.uint32.decode(state)\n\n if (state.end - state.start < frame) throw new RangeError('Out of bounds')\n\n const type = c.uint.decode(state)\n const id = c.uint.decode(state)\n\n switch (type) {\n case t.REQUEST:\n return { type, id, ...request.decode(state, id) }\n case t.RESPONSE:\n return { type, id, ...response.decode(state, id) }\n case t.STREAM:\n return { type, id, ...stream.decode(state, id) }\n default:\n throw errors.UNKNOWN_MESSAGE(`Unknown message '${type}'`)\n }\n }\n}\nconst b4a = require('b4a')\nconst errors = require('./errors')\n\nmodule.exports = class RPCOutgoingRequest {\n constructor(rpc, id, command) {\n this.rpc = rpc\n this.id = id\n this.command = command\n this.sent = false\n this.received = false\n\n this._promise = new Promise((resolve, reject) => {\n this._resolve = resolve\n this._reject = reject\n })\n\n this._requestStream = null\n this._responseStream = null\n }\n\n send(data, encoding) {\n if (this.sent) {\n throw errors.ALREADY_SENT('Request has already been sent')\n }\n\n this.sent = true\n\n this.rpc._sendRequest(\n this,\n typeof data === 'string' ? b4a.from(data, encoding) : data\n )\n }\n\n reply(encoding) {\n if (this.received) {\n throw errors.ALREADY_RECEIVED('Response is already being received')\n }\n\n this.received = true\n\n return encoding && encoding !== 'buffer'\n ? this._promise.then((data) => b4a.toString(data, encoding))\n : this._promise\n }\n\n createRequestStream(opts = {}) {\n if (this.sent) {\n throw errors.ALREADY_SENT('Request has already been sent')\n }\n\n this.sent = true\n\n this.rpc._createRequestStream(this, true, opts)\n\n return this._requestStream\n }\n\n createResponseStream(opts = {}) {\n if (this.received) {\n throw errors.ALREADY_RECEIVED('Response has already been received')\n }\n\n this.received = true\n\n this.rpc._createResponseStream(this, false, opts)\n\n return this._responseStream\n }\n}\nconst { Writable } = require('bare-stream')\nconst { type: t, stream: s } = require('./constants')\n\nmodule.exports = class RPCOutgoingStream extends Writable {\n constructor(rpc, request, type, opts) {\n super({ ...opts, eagerOpen: true })\n\n this._rpc = rpc\n this._request = request\n this._type = type\n this._mask = type === t.REQUEST ? s.REQUEST : s.RESPONSE\n\n this._pendingOpen = null\n }\n\n _open(cb) {\n let pending\n\n switch (this._type) {\n case t.REQUEST:\n pending = this._rpc._pendingRequests\n\n this._rpc._sendMessage({\n type: t.REQUEST,\n id: this._request.id,\n command: this._request.command,\n stream: s.OPEN,\n data: null\n })\n break\n\n case t.RESPONSE:\n pending = this._rpc._pendingResponses\n\n this._rpc._sendMessage({\n type: t.RESPONSE,\n id: this._request.id,\n error: false,\n stream: s.OPEN,\n data: null\n })\n break\n }\n\n if (pending.has(this._request.id)) {\n pending.delete(this._request.id)\n\n cb(null)\n } else {\n this._pendingOpen = cb\n }\n }\n\n _continueOpen() {\n if (this._pendingOpen === null) return\n const cb = this._pendingOpen\n this._pendingOpen = null\n cb()\n }\n\n _write(data, encoding, cb) {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.DATA,\n error: null,\n data\n })\n\n cb(null)\n }\n\n _final(cb) {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.END,\n error: null,\n data: null\n })\n\n cb(null)\n }\n\n _destroy(err, cb) {\n if (err) {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.CLOSE | s.ERROR,\n error: err,\n data: null\n })\n } else {\n this._rpc._sendMessage({\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.CLOSE,\n error: null,\n data: null\n })\n }\n\n cb(null)\n }\n}\n{\n \"name\": \"bare-rpc\",\n \"version\": \"0.2.6\",\n \"description\": \"librpc ABI compatible RPC for Bare\",\n \"exports\": {\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./package\": \"./package.json\",\n \"./errors\": {\n \"types\": \"./lib/errors.d.ts\",\n \"default\": \"./lib/errors.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-rpc.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-rpc/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-rpc#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.6.6\",\n \"bare-stream\": \"^2.1.3\",\n \"compact-encoding\": \"^2.15.0\",\n \"safety-catch\": \"^1.0.2\"\n },\n \"devDependencies\": {\n \"bare-buffer\": \"^3.0.1\",\n \"bare-ipc\": \"^1.1.0\",\n \"brittle\": \"^3.2.1\",\n \"prettier\": \"^3.4.2\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"peerDependencies\": {\n \"bare-buffer\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-buffer\": {\n \"optional\": true\n }\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nexports.constants = require('./lib/constants')\nexports.errors = require('./lib/errors')\n\nconst Version = exports.Version = require('./lib/version')\nconst Range = exports.Range = require('./lib/range')\nexports.Comparator = require('./lib/comparator')\n\nexports.satisfies = function satisfies (version, range) {\n if (typeof version === 'string') version = Version.parse(version)\n if (typeof range === 'string') range = Range.parse(range)\n\n return range.test(version)\n}\nconst constants = require('./constants')\n\nconst symbols = {\n [constants.EQ]: '=',\n [constants.LT]: '<',\n [constants.LTE]: '<=',\n [constants.GT]: '>',\n [constants.GTE]: '>='\n}\n\nmodule.exports = class Comparator {\n constructor (operator, version) {\n this.operator = operator\n this.version = version\n }\n\n test (version) {\n const result = version.compare(this.version)\n\n switch (this.operator) {\n case constants.LT: return result < 0\n case constants.LTE: return result <= 0\n case constants.GT: return result > 0\n case constants.GTE: return result >= 0\n default: return result === 0\n }\n }\n\n toString () {\n return symbols[this.operator] + this.version\n }\n}\nmodule.exports = {\n EQ: 1,\n LT: 2,\n LTE: 3,\n GT: 4,\n GTE: 5\n}\nmodule.exports = class SemVerError extends Error {\n constructor (msg, code, fn = SemVerError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name () {\n return 'SemVerError'\n }\n\n static INVALID_VERSION (msg, fn = SemVerError.INVALID_VERSION) {\n return new SemVerError(msg, 'INVALID_VERSION', fn)\n }\n\n static INVALID_RANGE (msg, fn = SemVerError.INVALID_RANGE) {\n return new SemVerError(msg, 'INVALID_RANGE', fn)\n }\n}\nconst constants = require('./constants')\nconst errors = require('./errors')\nconst Version = require('./version')\nconst Comparator = require('./comparator')\n\nconst Range = module.exports = exports = class Range {\n constructor (comparators = []) {\n this.comparators = comparators\n }\n\n test (version) {\n for (const set of this.comparators) {\n let matches = true\n\n for (const comparator of set) {\n if (comparator.test(version)) continue\n matches = false\n break\n }\n\n if (matches) return true\n }\n\n return false\n }\n\n toString () {\n let result = ''\n let first = true\n\n for (const set of this.comparators) {\n if (first) first = false\n else result += ' || '\n\n result += set.join(' ')\n }\n\n return result\n }\n}\n\nexports.parse = function parse (input, state = { position: 0, partial: false }) {\n let i = state.position\n let c\n\n const unexpected = (expected) => {\n let msg\n\n if (i >= input.length) {\n msg = `Unexpected end of input in '${input}'`\n } else {\n msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}`\n }\n\n if (expected) msg += `, ${expected}`\n\n throw errors.INVALID_VERSION(msg, unexpected)\n }\n\n const comparators = []\n\n while (i < input.length) {\n const set = []\n\n while (i < input.length) {\n c = input[i]\n\n let operator = constants.EQ\n\n if (c === '<') {\n operator = constants.LT\n c = input[++i]\n\n if (c === '=') {\n operator = constants.LTE\n c = input[++i]\n }\n } else if (c === '>') {\n operator = constants.GT\n c = input[++i]\n\n if (c === '=') {\n operator = constants.GTE\n c = input[++i]\n }\n } else if (c === '=') {\n c = input[++i]\n }\n\n const state = { position: i, partial: true }\n\n set.push(new Comparator(operator, Version.parse(input, state)))\n\n c = input[i = state.position]\n\n while (c === ' ') c = input[++i]\n\n if (c === '|' && input[i + 1] === '|') {\n c = input[i += 2]\n\n while (c === ' ') c = input[++i]\n\n break\n }\n\n if (c && c !== '<' && c !== '>') unexpected('expected \\'||\\', \\'<\\', or \\'>\\'')\n }\n\n if (set.length) comparators.push(set)\n }\n\n if (i < input.length && state.partial === false) unexpected('expected end of input')\n\n state.position = i\n\n return new Range(comparators)\n}\nconst errors = require('./errors')\n\nconst Version = module.exports = exports = class Version {\n constructor (major, minor, patch, opts = {}) {\n const {\n prerelease = [],\n build = []\n } = opts\n\n this.major = major\n this.minor = minor\n this.patch = patch\n this.prerelease = prerelease\n this.build = build\n }\n\n compare (version) {\n return exports.compare(this, version)\n }\n\n toString () {\n let result = `${this.major}.${this.minor}.${this.patch}`\n\n if (this.prerelease.length) {\n result += '-' + this.prerelease.join('.')\n }\n\n if (this.build.length) {\n result += '+' + this.build.join('.')\n }\n\n return result\n }\n}\n\nexports.parse = function parse (input, state = { position: 0, partial: false }) {\n let i = state.position\n let c\n\n const unexpected = (expected) => {\n let msg\n\n if (i >= input.length) {\n msg = `Unexpected end of input in '${input}'`\n } else {\n msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}`\n }\n\n if (expected) msg += `, ${expected}`\n\n throw errors.INVALID_VERSION(msg, unexpected)\n }\n\n const components = []\n\n while (components.length < 3) {\n c = input[i]\n\n if (components.length > 0) {\n if (c === '.') c = input[++i]\n else unexpected('expected \\'.\\'')\n }\n\n if (c === '0') {\n components.push(0)\n\n i++\n } else if (c >= '1' && c <= '9') {\n let j = 0\n do c = input[i + ++j]\n while (c >= '0' && c <= '9')\n\n components.push(parseInt(input.substring(i, i + j)))\n\n i += j\n } else unexpected('expected /[0-9]/')\n }\n\n const prerelease = []\n\n if (input[i] === '-') {\n i++\n\n while (true) {\n c = input[i]\n\n let tag = ''\n let j = 0\n\n while (c >= '0' && c <= '9') c = input[i + ++j]\n\n let isNumeric = false\n\n if (j) {\n tag += input.substring(i, i + j)\n\n c = input[i += j]\n\n isNumeric = tag[0] !== '0' || tag.length === 1\n }\n\n j = 0\n\n while ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-') c = input[i + ++j]\n\n if (j) {\n tag += input.substring(i, i + j)\n\n c = input[i += j]\n } else if (!isNumeric) unexpected('expected /[a-zA-Z-]/')\n\n prerelease.push(tag)\n\n if (c === '.') c = input[++i]\n else break\n }\n }\n\n const build = []\n\n if (input[i] === '+') {\n i++\n\n while (true) {\n c = input[i]\n\n let tag = ''\n let j = 0\n\n while ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '-') c = input[i + ++j]\n\n if (j) {\n tag += input.substring(i, i + j)\n\n c = input[i += j]\n } else unexpected('expected /[0-9a-zA-Z-]/')\n\n build.push(tag)\n\n if (c === '.') c = input[++i]\n else break\n }\n }\n\n if (i < input.length && state.partial === false) unexpected('expected end of input')\n\n state.position = i\n\n return new Version(...components, { prerelease, build })\n}\n\nconst integer = /^[0-9]+$/\n\nexports.compare = function compare (a, b) {\n if (a.major > b.major) return 1\n if (a.major < b.major) return -1\n\n if (a.minor > b.minor) return 1\n if (a.minor < b.minor) return -1\n\n if (a.patch > b.patch) return 1\n if (a.patch < b.patch) return -1\n\n if (a.prerelease.length === 0) return b.prerelease.length === 0 ? 0 : 1\n if (b.prerelease.length === 0) return -1\n\n let i = 0\n do {\n let x = a.prerelease[i]\n let y = b.prerelease[i]\n\n if (x === undefined) return y === undefined ? 0 : -1\n if (y === undefined) return 1\n\n if (x === y) continue\n\n const xInt = integer.test(x)\n const yInt = integer.test(y)\n\n if (xInt && yInt) {\n x = +x\n y = +y\n } else {\n if (xInt) return -1\n if (yInt) return 1\n }\n\n return x > y ? 1 : -1\n } while (++i)\n}\n{\n \"name\": \"bare-semver\",\n \"version\": \"1.0.1\",\n \"description\": \"Minimal semantic versioning library for Bare\",\n \"exports\": {\n \".\": \"./index.js\",\n \"./package\": \"./package.json\",\n \"./constants\": \"./lib/constants.js\",\n \"./errors\": \"./lib/errors.js\",\n \"./version\": \"./lib/version.js\",\n \"./range\": \"./lib/range.js\",\n \"./comparator\": \"./lib/comparator.js\"\n },\n \"files\": [\n \"index.js\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"standard && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-semver.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-semver/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-semver#readme\",\n \"devDependencies\": {\n \"brittle\": \"^3.2.1\",\n \"standard\": \"^17.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst stream = require('streamx')\n\nconst defaultEncoding = 'utf8'\n\nmodule.exports = exports = stream.Stream\n\nexports.pipeline = stream.pipeline\n\nexports.isStream = stream.isStream\nexports.isEnded = stream.isEnded\nexports.isFinished = stream.isFinished\nexports.isDisturbed = stream.isDisturbed\n\nexports.getStreamError = stream.getStreamError\n\nexports.Stream = exports\n\nexports.Readable = class Readable extends stream.Readable {\n constructor(opts = {}) {\n super({\n ...opts,\n byteLength: null,\n byteLengthReadable: null,\n map: null,\n mapReadable: null\n })\n\n if (this._construct) this._open = this._construct\n\n if (this._read !== stream.Readable.prototype._read) {\n this._read = read.bind(this, this._read)\n }\n\n if (this._destroy !== stream.Stream.prototype._destroy) {\n this._destroy = destroy.bind(this, this._destroy)\n }\n }\n\n push(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n return super.push(chunk)\n }\n\n unshift(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n super.unshift(chunk)\n }\n}\n\nexports.Writable = class Writable extends stream.Writable {\n constructor(opts = {}) {\n super({\n ...opts,\n byteLength: null,\n byteLengthWritable,\n map: null,\n mapWritable: null\n })\n\n if (this._construct) this._open = this._construct\n\n if (this._write !== stream.Writable.prototype._write) {\n this._write = write.bind(this, this._write)\n }\n\n if (this._destroy !== stream.Stream.prototype._destroy) {\n this._destroy = destroy.bind(this, this._destroy)\n }\n }\n\n write(chunk, encoding, cb) {\n if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding)\n } else {\n encoding = 'buffer'\n }\n\n const result = super.write({ chunk, encoding })\n\n if (cb) stream.Writable.drained(this).then(() => cb(null), cb)\n\n return result\n }\n\n end(chunk, encoding, cb) {\n if (typeof chunk === 'function') {\n cb = chunk\n chunk = null\n } else if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n } else {\n encoding = 'buffer'\n }\n\n const result =\n chunk !== undefined && chunk !== null\n ? super.end({ chunk, encoding })\n : super.end()\n\n if (cb) this.once('end', () => cb(null))\n\n return result\n }\n}\n\nexports.Duplex = class Duplex extends stream.Duplex {\n constructor(opts = {}) {\n super({\n ...opts,\n byteLength: null,\n byteLengthReadable: null,\n byteLengthWritable,\n map: null,\n mapReadable: null,\n mapWritable: null\n })\n\n if (this._construct) this._open = this._construct\n\n if (this._read !== stream.Readable.prototype._read) {\n this._read = read.bind(this, this._read)\n }\n\n if (this._write !== stream.Duplex.prototype._write) {\n this._write = write.bind(this, this._write)\n }\n\n if (this._destroy !== stream.Stream.prototype._destroy) {\n this._destroy = destroy.bind(this, this._destroy)\n }\n }\n\n push(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n return super.push(chunk)\n }\n\n unshift(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n super.unshift(chunk)\n }\n\n write(chunk, encoding, cb) {\n if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding)\n } else {\n encoding = 'buffer'\n }\n\n const result = super.write({ chunk, encoding })\n\n if (cb) stream.Writable.drained(this).then(() => cb(null), cb)\n\n return result\n }\n\n end(chunk, encoding, cb) {\n if (typeof chunk === 'function') {\n cb = chunk\n chunk = null\n } else if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding)\n } else {\n encoding = 'buffer'\n }\n\n const result =\n chunk !== undefined && chunk !== null\n ? super.end({ chunk, encoding })\n : super.end()\n\n if (cb) this.once('end', () => cb(null))\n\n return result\n }\n}\n\nexports.Transform = class Transform extends stream.Transform {\n constructor(opts = {}) {\n super({\n ...opts,\n byteLength: null,\n byteLengthReadable: null,\n byteLengthWritable,\n map: null,\n mapReadable: null,\n mapWritable: null\n })\n\n if (this._transform !== stream.Transform.prototype._transform) {\n this._transform = transform.bind(this, this._transform)\n } else {\n this._transform = passthrough\n }\n }\n\n push(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n return super.push(chunk)\n }\n\n unshift(chunk, encoding) {\n if (typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding || defaultEncoding)\n }\n\n super.unshift(chunk)\n }\n\n write(chunk, encoding, cb) {\n if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding)\n } else {\n encoding = 'buffer'\n }\n\n const result = super.write({ chunk, encoding })\n\n if (cb) stream.Writable.drained(this).then(() => cb(null), cb)\n\n return result\n }\n\n end(chunk, encoding, cb) {\n if (typeof chunk === 'function') {\n cb = chunk\n chunk = null\n } else if (typeof encoding === 'function') {\n cb = encoding\n encoding = null\n }\n\n if (typeof chunk === 'string') {\n encoding = encoding || defaultEncoding\n chunk = Buffer.from(chunk, encoding)\n } else {\n encoding = 'buffer'\n }\n\n const result =\n chunk !== undefined && chunk !== null\n ? super.end({ chunk, encoding })\n : super.end()\n\n if (cb) this.once('end', () => cb(null))\n\n return result\n }\n}\n\nexports.PassThrough = class PassThrough extends exports.Transform {}\n\nexports.finished = function finished(stream, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (!opts) opts = {}\n\n const { cleanup = false } = opts\n\n const done = () => {\n cb(exports.getStreamError(stream, { all: true }))\n\n if (cleanup) detach()\n }\n\n const detach = () => {\n stream.off('close', done)\n stream.off('error', noop)\n }\n\n if (stream.destroyed) {\n done()\n } else {\n stream.on('close', done)\n stream.on('error', noop)\n }\n\n return detach\n}\n\nfunction read(read, cb) {\n read.call(this, 65536)\n\n cb(null)\n}\n\nfunction write(write, data, cb) {\n write.call(this, data.chunk, data.encoding, cb)\n}\n\nfunction transform(transform, data, cb) {\n transform.call(this, data.chunk, data.encoding, cb)\n}\n\nfunction destroy(destroy, cb) {\n destroy.call(this, exports.getStreamError(this), cb)\n}\n\nfunction passthrough(data, cb) {\n cb(null, data.chunk)\n}\n\nfunction byteLengthWritable(data) {\n return data.chunk.byteLength\n}\n\nfunction noop() {}\n{\n \"name\": \"bare-stream\",\n \"version\": \"2.6.5\",\n \"description\": \"Streaming data for JavaScript\",\n \"exports\": {\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./package\": \"./package.json\",\n \"./promises\": \"./promises.js\",\n \"./web\": \"./web.js\",\n \"./global\": \"./global.js\"\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"promises.js\",\n \"web.js\",\n \"global.js\"\n ],\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-stream.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-stream/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-stream#readme\",\n \"dependencies\": {\n \"streamx\": \"^2.21.0\"\n },\n \"devDependencies\": {\n \"bare-buffer\": \"^3.0.0\",\n \"bare-events\": \"^2.5.4\",\n \"brittle\": \"^3.5.2\",\n \"prettier\": \"^3.3.3\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"peerDependencies\": {\n \"bare-buffer\": \"*\",\n \"bare-events\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-buffer\": {\n \"optional\": true\n },\n \"bare-events\": {\n \"optional\": true\n }\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = require.addon()\nconst path = require('bare-path')\nconst binding = require('./binding')\nconst errors = require('./lib/errors')\n\nconst kind = Symbol.for('bare.url.kind')\n\nconst isWindows = Bare.platform === 'win32'\n\nmodule.exports = exports = class URL {\n static get [kind]() {\n return 0 // Compatibility version\n }\n\n constructor(input, base, opts = {}) {\n if (arguments.length === 0) throw errors.INVALID_URL()\n\n input = `${input}`\n\n if (base !== undefined) base = `${base}`\n\n this._components = new Uint32Array(8)\n\n this._parse(input, base, opts.throw !== false)\n }\n\n get [kind]() {\n return URL[kind]\n }\n\n // https://url.spec.whatwg.org/#dom-url-href\n\n get href() {\n return this._href\n }\n\n set href(value) {\n this._update(value)\n }\n\n // https://url.spec.whatwg.org/#dom-url-protocol\n\n get protocol() {\n return this._slice(0, this._components[0]) + ':'\n }\n\n set protocol(value) {\n this._update(\n this._replace(value.replace(/:+$/, ''), 0, this._components[0])\n )\n }\n\n // https://url.spec.whatwg.org/#dom-url-username\n\n get username() {\n return this._slice(this._components[0] + 3 /* :// */, this._components[1])\n }\n\n set username(value) {\n if (cannotHaveCredentialsOrPort(this)) {\n return\n }\n\n if (this.username === '') value += '@'\n\n this._update(\n this._replace(\n value,\n this._components[0] + 3 /* :// */,\n this._components[1]\n )\n )\n }\n\n // https://url.spec.whatwg.org/#dom-url-password\n\n get password() {\n return this._href.slice(\n this._components[1] + 1 /* : */,\n this._components[2] - 1 /* @ */\n )\n }\n\n set password(value) {\n if (cannotHaveCredentialsOrPort(this)) {\n return\n }\n\n let start = this._components[1] + 1 /* : */\n let end = this._components[2] - 1 /* @ */\n\n if (this.password === '') {\n value = ':' + value\n start--\n }\n\n if (this.username === '') {\n value += '@'\n end++\n }\n\n this._update(this._replace(value, start, end))\n }\n\n // https://url.spec.whatwg.org/#dom-url-host\n\n get host() {\n return this._slice(this._components[2], this._components[5])\n }\n\n set host(value) {\n if (hasOpaquePath(this)) {\n return\n }\n\n this._update(\n this._replace(\n value,\n this._components[2],\n this._components[value.includes(':') ? 5 : 3]\n )\n )\n }\n\n // https://url.spec.whatwg.org/#dom-url-hostname\n\n get hostname() {\n return this._slice(this._components[2], this._components[3])\n }\n\n set hostname(value) {\n if (hasOpaquePath(this)) {\n return\n }\n\n this._update(this._replace(value, this._components[2], this._components[3]))\n }\n\n // https://url.spec.whatwg.org/#dom-url-port\n\n get port() {\n return this._slice(this._components[3] + 1 /* : */, this._components[5])\n }\n\n set port(value) {\n if (cannotHaveCredentialsOrPort(this)) {\n return\n }\n\n let start = this._components[3] + 1 /* : */\n\n if (this.port === '') {\n value = ':' + value\n start--\n }\n\n this._update(this._replace(value, start, this._components[5]))\n }\n\n // https://url.spec.whatwg.org/#dom-url-pathname\n\n get pathname() {\n return this._slice(this._components[5], this._components[6] - 1 /* ? */)\n }\n\n set pathname(value) {\n if (hasOpaquePath(this)) {\n return\n }\n\n if (value[0] !== '/' && value[0] !== '\\\\') {\n value = '/' + value\n }\n\n this._update(\n this._replace(value, this._components[5], this._components[6] - 1 /* ? */)\n )\n }\n\n // https://url.spec.whatwg.org/#dom-url-search\n\n get search() {\n return this._slice(\n this._components[6] - 1 /* ? */,\n this._components[7] - 1 /* # */\n )\n }\n\n set search(value) {\n if (value && value[0] !== '?') value = '?' + value\n\n this._update(\n this._replace(\n value,\n this._components[6] - 1 /* ? */,\n this._components[7] - 1 /* # */\n )\n )\n }\n\n // https://url.spec.whatwg.org/#dom-url-hash\n\n get hash() {\n return this._slice(this._components[7] - 1 /* # */)\n }\n\n set hash(value) {\n if (value && value[0] !== '#') value = '#' + value\n\n this._update(this._replace(value, this._components[7] - 1 /* # */))\n }\n\n toString() {\n return this._href\n }\n\n toJSON() {\n return this._href\n }\n\n [Symbol.for('bare.inspect')]() {\n return {\n __proto__: { constructor: URL },\n\n href: this.href,\n protocol: this.protocol,\n username: this.username,\n password: this.password,\n host: this.host,\n hostname: this.hostname,\n port: this.port,\n pathname: this.pathname,\n search: this.search,\n hash: this.hash\n }\n }\n\n _slice(start, end = this._href.length) {\n return this._href.slice(start, end)\n }\n\n _replace(replacement, start, end = this._href.length) {\n return this._slice(0, start) + replacement + this._slice(end)\n }\n\n _parse(href, base, shouldThrow) {\n try {\n this._href = binding.parse(\n String(href),\n base ? String(base) : null,\n this._components,\n shouldThrow\n )\n } catch (err) {\n if (err instanceof TypeError) throw err\n\n throw errors.INVALID_URL()\n }\n }\n\n _update(href) {\n try {\n this._parse(href, null, true)\n } catch (err) {\n if (err instanceof TypeError) throw err\n }\n }\n}\n\n// https://url.spec.whatwg.org/#url-opaque-path\nfunction hasOpaquePath(url) {\n return url.pathname[0] !== '/'\n}\n\n// https://url.spec.whatwg.org/#cannot-have-a-username-password-port\nfunction cannotHaveCredentialsOrPort(url) {\n return url.hostname === '' || url.protocol === 'file:'\n}\n\nconst URL = exports\n\nexports.URL = URL // For Node.js compatibility\n\nexports.errors = errors\n\nexports.isURL = function isURL(value) {\n if (value instanceof URL) return true\n\n return (\n typeof value === 'object' && value !== null && value[kind] === URL[kind]\n )\n}\n\nexports.parse = function parse(input, base) {\n const url = new URL(input, base, { throw: false })\n return url.href ? url : null\n}\n\nexports.canParse = function canParse(input, base) {\n return binding.canParse(String(input), base ? String(base) : null)\n}\n\nexports.fileURLToPath = function fileURLToPath(url) {\n if (typeof url === 'string') {\n url = new URL(url)\n }\n\n if (url.protocol !== 'file:') {\n throw errors.INVALID_URL_SCHEME('The URL must use the file: protocol')\n }\n\n if (isWindows) {\n if (/%2f|%5c/i.test(url.pathname)) {\n throw errors.INVALID_FILE_URL_PATH(\n 'The file: URL path must not include encoded \\\\ or / characters'\n )\n }\n } else {\n if (url.hostname) {\n throw errors.INVALID_FILE_URL_HOST(\n \"The file: URL host must be 'localhost' or empty\"\n )\n }\n\n if (/%2f/i.test(url.pathname)) {\n throw errors.INVALID_FILE_URL_PATH(\n 'The file: URL path must not include encoded / characters'\n )\n }\n }\n\n const pathname = path.normalize(decodeURIComponent(url.pathname))\n\n if (isWindows) {\n if (url.hostname) return '\\\\\\\\' + url.hostname + pathname\n\n const letter = pathname.charCodeAt(1) | 0x20\n\n if (\n letter < 0x61 /* a */ ||\n letter > 0x7a /* z */ ||\n pathname.charCodeAt(2) !== 0x3a /* : */\n ) {\n throw errors.INVALID_FILE_URL_PATH('The file: URL path must be absolute')\n }\n\n return pathname.slice(1)\n }\n\n return pathname\n}\n\nexports.pathToFileURL = function pathToFileURL(pathname) {\n let resolved = path.resolve(pathname)\n\n if (pathname[pathname.length - 1] === '/') {\n resolved += '/'\n } else if (isWindows && pathname[pathname.length - 1] === '\\\\') {\n resolved += '\\\\'\n }\n\n resolved = resolved\n .replaceAll('%', '%25') // Must be first\n .replaceAll('#', '%23')\n .replaceAll('?', '%3f')\n .replaceAll('\\n', '%0a')\n .replaceAll('\\r', '%0d')\n .replaceAll('\\t', '%09')\n\n if (!isWindows) {\n resolved = resolved.replaceAll('\\\\', '%5c')\n }\n\n return new URL('file:' + resolved)\n}\nmodule.exports = class URLError extends Error {\n constructor(msg, code, fn = URLError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name() {\n return 'URLError'\n }\n\n static INVALID_URL(msg = 'Invalid URL') {\n return new URLError(msg, 'INVALID_URL', URLError.INVALID_URL)\n }\n\n static INVALID_URL_SCHEME(msg = 'Invalid URL') {\n return new URLError(msg, 'INVALID_URL_SCHEME', URLError.INVALID_URL_SCHEME)\n }\n\n static INVALID_FILE_URL_HOST(msg = 'Invalid file: URL host') {\n return new URLError(\n msg,\n 'INVALID_FILE_URL_HOST',\n URLError.INVALID_FILE_URL_HOST\n )\n }\n\n static INVALID_FILE_URL_PATH(msg = 'Invalid file: URL path') {\n return new URLError(\n msg,\n 'INVALID_FILE_URL_PATH',\n URLError.INVALID_FILE_URL_PATH\n )\n }\n}\n{\n \"name\": \"bare-url\",\n \"version\": \"2.1.6\",\n \"description\": \"WHATWG URL implementation for JavaScript\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./global\": {\n \"types\": \"./global.d.ts\",\n \"default\": \"./global.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"global.js\",\n \"global.d.ts\",\n \"binding.c\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"lib\",\n \"prebuilds\"\n ],\n \"addon\": true,\n \"scripts\": {\n \"test\": \"prettier . --check && bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bare-url.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-url/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-url\",\n \"dependencies\": {\n \"bare-path\": \"^3.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"cmake-bare\": \"^1.1.6\",\n \"cmake-fetch\": \"^1.0.0\",\n \"prettier\": \"^3.3.3\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n'use strict'\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i]\n revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n var len = b64.length\n\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4')\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=')\n if (validLen === -1) validLen = len\n\n var placeHoldersLen = validLen === len\n ? 0\n : 4 - (validLen % 4)\n\n return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n var tmp\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n var curByte = 0\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0\n ? validLen - 4\n : validLen\n\n var i\n for (i = 0; i < len; i += 4) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 18) |\n (revLookup[b64.charCodeAt(i + 1)] << 12) |\n (revLookup[b64.charCodeAt(i + 2)] << 6) |\n revLookup[b64.charCodeAt(i + 3)]\n arr[curByte++] = (tmp >> 16) & 0xFF\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 2) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 2) |\n (revLookup[b64.charCodeAt(i + 1)] >> 4)\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 1) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 10) |\n (revLookup[b64.charCodeAt(i + 1)] << 4) |\n (revLookup[b64.charCodeAt(i + 2)] >> 2)\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n return arr\n}\n\nfunction tripletToBase64 (num) {\n return lookup[num >> 18 & 0x3F] +\n lookup[num >> 12 & 0x3F] +\n lookup[num >> 6 & 0x3F] +\n lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n var tmp\n var output = []\n for (var i = start; i < end; i += 3) {\n tmp =\n ((uint8[i] << 16) & 0xFF0000) +\n ((uint8[i + 1] << 8) & 0xFF00) +\n (uint8[i + 2] & 0xFF)\n output.push(tripletToBase64(tmp))\n }\n return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n var tmp\n var len = uint8.length\n var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n var parts = []\n var maxChunkLength = 16383 // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1]\n parts.push(\n lookup[tmp >> 2] +\n lookup[(tmp << 4) & 0x3F] +\n '=='\n )\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n parts.push(\n lookup[tmp >> 10] +\n lookup[(tmp >> 4) & 0x3F] +\n lookup[(tmp << 2) & 0x3F] +\n '='\n )\n }\n\n return parts.join('')\n}\n{\n \"name\": \"base64-js\",\n \"description\": \"Base64 encoding/decoding in pure JS\",\n \"version\": \"1.5.1\",\n \"author\": \"T. Jameson Little \",\n \"typings\": \"index.d.ts\",\n \"bugs\": {\n \"url\": \"https://github.com/beatgammit/base64-js/issues\"\n },\n \"devDependencies\": {\n \"babel-minify\": \"^0.5.1\",\n \"benchmark\": \"^2.1.4\",\n \"browserify\": \"^16.3.0\",\n \"standard\": \"*\",\n \"tape\": \"4.x\"\n },\n \"homepage\": \"https://github.com/beatgammit/base64-js\",\n \"keywords\": [\n \"base64\"\n ],\n \"license\": \"MIT\",\n \"main\": \"index.js\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/beatgammit/base64-js.git\"\n },\n \"scripts\": {\n \"build\": \"browserify -s base64js -r ./ | minify > base64js.min.js\",\n \"lint\": \"standard\",\n \"test\": \"npm run lint && npm run unit\",\n \"unit\": \"tape test/*.js\"\n },\n \"funding\": [\n {\n \"type\": \"github\",\n \"url\": \"https://github.com/sponsors/feross\"\n },\n {\n \"type\": \"patreon\",\n \"url\": \"https://www.patreon.com/feross\"\n },\n {\n \"type\": \"consulting\",\n \"url\": \"https://feross.org/support\"\n }\n ],\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst b4a = require('b4a')\n\nfunction byteLength (size) {\n return Math.ceil(size / 8)\n}\n\nfunction get (buffer, bit) {\n const n = buffer.BYTES_PER_ELEMENT * 8\n\n const offset = bit & (n - 1)\n const i = (bit - offset) / n\n\n return (buffer[i] & (1 << offset)) !== 0\n}\n\nfunction set (buffer, bit, value = true) {\n const n = buffer.BYTES_PER_ELEMENT * 8\n\n const offset = bit & (n - 1)\n const i = (bit - offset) / n\n const mask = 1 << offset\n\n if (value) {\n if ((buffer[i] & mask) !== 0) return false\n } else {\n if ((buffer[i] & mask) === 0) return false\n }\n\n buffer[i] ^= mask\n return true\n}\n\nfunction setRange (buffer, start, end, value = true) {\n const n = buffer.BYTES_PER_ELEMENT * 8\n\n let remaining = end - start\n let offset = start & (n - 1)\n let i = (start - offset) / n\n\n let changed = false\n\n while (remaining > 0) {\n const mask = (2 ** Math.min(remaining, n - offset) - 1) << offset\n\n if (value) {\n if ((buffer[i] & mask) !== mask) {\n buffer[i] |= mask\n changed = true\n }\n } else {\n if ((buffer[i] & mask) !== 0) {\n buffer[i] &= ~mask\n changed = true\n }\n }\n\n remaining -= n - offset\n offset = 0\n i++\n }\n\n return changed\n}\n\nfunction fill (buffer, value, start = 0, end = buffer.byteLength * 8) {\n const n = buffer.BYTES_PER_ELEMENT * 8\n let i, j\n\n {\n const offset = start & (n - 1)\n i = (start - offset) / n\n\n if (offset !== 0) {\n const mask = (2 ** Math.min(n - offset, end - start) - 1) << offset\n\n if (value) buffer[i] |= mask\n else buffer[i] &= ~mask\n\n i++\n }\n }\n\n {\n const offset = end & (n - 1)\n j = (end - offset) / n\n\n if (offset !== 0 && j >= i) {\n const mask = (2 ** offset) - 1\n\n if (value) buffer[j] |= mask\n else buffer[j] &= ~mask\n }\n }\n\n return buffer.fill(value ? (2 ** n) - 1 : 0, i, j)\n}\n\nfunction toggle (buffer, bit) {\n const n = buffer.BYTES_PER_ELEMENT * 8\n\n const offset = bit & (n - 1)\n const i = (bit - offset) / n\n const mask = 1 << offset\n\n buffer[i] ^= mask\n return (buffer[i] & mask) !== 0\n}\n\nfunction remove (buffer, bit) {\n return set(buffer, bit, false)\n}\n\nfunction removeRange (buffer, start, end) {\n return setRange(buffer, start, end, false)\n}\n\nfunction indexOf (buffer, value, position = 0) {\n for (let i = position, n = buffer.byteLength * 8; i < n; i++) {\n if (get(buffer, i) === value) return i\n }\n\n return -1\n}\n\nfunction lastIndexOf (buffer, value, position = buffer.byteLength * 8 - 1) {\n for (let i = position; i >= 0; i--) {\n if (get(buffer, i) === value) return i\n }\n\n return -1\n}\n\nfunction of (...bits) {\n return from(bits)\n}\n\nfunction from (bits) {\n const buffer = b4a.alloc(byteLength(bits.length))\n for (let i = 0; i < bits.length; i++) set(buffer, i, bits[i])\n return buffer\n}\n\nfunction * iterator (buffer) {\n for (let i = 0, n = buffer.byteLength * 8; i < n; i++) yield get(buffer, i)\n}\n\nmodule.exports = {\n byteLength,\n get,\n set,\n setRange,\n fill,\n toggle,\n remove,\n removeRange,\n indexOf,\n lastIndexOf,\n of,\n from,\n iterator\n}\n{\n \"name\": \"bits-to-bytes\",\n \"version\": \"1.3.0\",\n \"description\": \"Functions for doing bit manipulation of typed arrays\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/bits-to-bytes.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð \",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bits-to-bytes/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bits-to-bytes#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.5.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^2.3.1\",\n \"standard\": \"^17.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst EventEmitter = require('events')\nconst Protomux = require('protomux')\nconst { Readable } = require('streamx')\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\nconst bitfield = require('compact-encoding-bitfield')\nconst bits = require('bits-to-bytes')\nconst errors = require('./lib/errors')\n\nexports.Server = class BlindRelayServer extends EventEmitter {\n constructor (opts = {}) {\n super()\n\n const {\n createStream\n } = opts\n\n this._createStream = createStream\n this._pairing = new Map()\n this._sessions = new Set()\n }\n\n get sessions () {\n return this._sessions[Symbol.iterator]()\n }\n\n accept (stream, opts) {\n const session = new BlindRelaySession(this, stream, opts)\n\n this._sessions.add(session)\n\n return session\n }\n\n async close () {\n const ending = []\n\n for (const session of this._sessions) {\n ending.push(session.end())\n }\n\n await Promise.all(ending)\n\n this._pairing.clear()\n }\n}\n\nclass BlindRelaySession extends EventEmitter {\n constructor (server, stream, opts = {}) {\n super()\n\n const {\n id,\n handshake,\n handshakeEncoding\n } = opts\n\n this._server = server\n this._mux = Protomux.from(stream)\n\n this._channel = this._mux.createChannel({\n protocol: 'blind-relay',\n id,\n handshake: handshake ? handshakeEncoding || c.raw : null,\n onopen: this._onopen.bind(this),\n onclose: this._onclose.bind(this),\n ondestroy: this._ondestroy.bind(this)\n })\n\n this._pair = this._channel.addMessage({\n encoding: m.pair,\n onmessage: this._onpair.bind(this)\n })\n\n this._unpair = this._channel.addMessage({\n encoding: m.unpair,\n onmessage: this._onunpair.bind(this)\n })\n\n this._ending = null\n this._destroyed = false\n this._error = null\n this._pairing = new Set()\n this._streams = new Map()\n\n this._onerror = (err) => this.emit('error', err)\n\n this._channel.open(handshake)\n }\n\n get closed () {\n return this._channel.closed\n }\n\n get mux () {\n return this._mux\n }\n\n get stream () {\n return this._mux.stream\n }\n\n _onopen () {\n this.emit('open')\n }\n\n _onclose () {\n this._ending = Promise.resolve()\n\n const err = this._error || errors.CHANNEL_CLOSED()\n\n for (const token of this._pairing) {\n this._server._pairing.delete(token.toString('hex'))\n }\n\n for (const stream of this._streams.values()) {\n stream\n .off('error', this._onerror)\n .on('error', noop)\n .destroy(err)\n }\n\n this._pairing.clear()\n this._streams.clear()\n\n this._server._sessions.delete(this)\n\n this.emit('close')\n }\n\n _ondestroy () {\n this._destroyed = true\n this.emit('destroy')\n }\n\n _onpair ({ isInitiator, token, id: remoteId }) {\n const keyString = token.toString('hex')\n\n let pair = this._server._pairing.get(keyString)\n\n if (pair === undefined) {\n pair = new BlindRelayPair(token)\n this._server._pairing.set(keyString, pair)\n } else if (pair.links[+isInitiator]) return\n\n this._pairing.add(keyString)\n\n pair.links[+isInitiator] = new BlindRelayLink(this, isInitiator, remoteId)\n\n if (!pair.paired) return\n\n this._server._pairing.delete(keyString)\n\n // 1st pass: Create the raw streams needed for each end of the link.\n for (const link of pair.links) {\n link.createStream()\n }\n\n // 2nd pass: Connect the raw streams and set up handlers.\n for (const { isInitiator, session, stream } of pair.links) {\n const remote = pair.remote(isInitiator)\n\n stream\n .on('error', session._onerror)\n .on('close', () => session._streams.delete(keyString))\n .relayTo(remote.stream)\n\n session._pairing.delete(keyString)\n session._streams.set(keyString, stream)\n }\n\n // 3rd pass: Let either end of the link know the streams were set up.\n for (const { isInitiator, session, remoteId, stream } of pair.links) {\n session._pair.send({\n isInitiator,\n token,\n id: stream.id,\n seq: 0\n })\n\n session._endMaybe()\n\n session.emit('pair', isInitiator, token, stream, remoteId)\n }\n }\n\n _onunpair ({ token }) {\n const keyString = token.toString('hex')\n\n const pair = this._server._pairing.get(keyString)\n\n if (pair) {\n for (const link of pair.links) {\n if (link) link.session._pairing.delete(keyString)\n }\n\n return this._server._pairing.delete(keyString)\n }\n\n const stream = this._streams.get(keyString)\n\n if (stream) {\n stream\n .off('error', this._onerror)\n .on('error', noop)\n .destroy(errors.PAIRING_CANCELLED())\n\n this._streams.delete(keyString)\n }\n }\n\n cork () {\n this._channel.cork()\n }\n\n uncork () {\n this._channel.uncork()\n }\n\n async end () {\n if (this._ending) return this._ending\n\n this._ending = EventEmitter.once(this, 'close')\n this._endMaybe()\n\n return this._ending\n }\n\n _endMaybe () {\n if (this._ending && this._pairing.size === 0) {\n this._channel.close()\n }\n }\n\n destroy (err) {\n if (this._destroyed) return\n this._destroyed = true\n\n this._error = err || errors.CHANNEL_DESTROYED()\n this._channel.close()\n }\n}\n\nclass BlindRelayPair {\n constructor (token) {\n this.token = token\n this.links = [null, null]\n }\n\n get paired () {\n return this.links[0] !== null && this.links[1] !== null\n }\n\n remote (isInitiator) {\n return this.links[isInitiator ? 0 : 1]\n }\n}\n\nclass BlindRelayLink {\n constructor (session, isInitiator, remoteId) {\n this.session = session\n this.isInitiator = isInitiator\n this.remoteId = remoteId\n this.stream = null\n }\n\n createStream () {\n if (this.stream) return\n\n this.stream = this.session._server._createStream({\n firewall: this._onfirewall.bind(this)\n })\n }\n\n _onfirewall (socket, port, host) {\n this.stream.connect(socket, this.remoteId, port, host)\n\n return false\n }\n}\n\nexports.Client = class BlindRelayClient extends EventEmitter {\n static _clients = new WeakMap()\n\n static from (stream, opts) {\n let client = this._clients.get(stream)\n if (client) return client\n client = new this(stream, opts)\n this._clients.set(stream, client)\n return client\n }\n\n constructor (stream, opts = {}) {\n super()\n\n const {\n id,\n handshake,\n handshakeEncoding\n } = opts\n\n this._mux = Protomux.from(stream)\n\n this._channel = this._mux.createChannel({\n protocol: 'blind-relay',\n id,\n handshake: handshake ? handshakeEncoding || c.raw : null,\n onopen: this._onopen.bind(this),\n onclose: this._onclose.bind(this),\n ondestroy: this._ondestroy.bind(this)\n })\n\n this._pair = this._channel.addMessage({\n encoding: m.pair,\n onmessage: this._onpair.bind(this)\n })\n\n this._unpair = this._channel.addMessage({\n encoding: m.unpair\n })\n\n this._ending = false\n this._destroyed = false\n this._error = null\n this._requests = new Map()\n\n this._channel.open(handshake)\n }\n\n get closed () {\n return this._channel.closed\n }\n\n get mux () {\n return this._mux\n }\n\n get stream () {\n return this._mux.stream\n }\n\n get requests () {\n return this._requests.values()\n }\n\n _onopen () {\n this.emit('open')\n }\n\n _onclose () {\n this._ending = Promise.resolve()\n\n const err = this._error || errors.CHANNEL_CLOSED()\n\n for (const request of this._requests.values()) {\n request.destroy(err)\n }\n\n this._requests.clear()\n\n this.constructor._clients.delete(this.stream)\n\n this.emit('close')\n }\n\n _ondestroy () {\n this._destroyed = true\n this.emit('destroy')\n }\n\n _onpair ({ isInitiator, token, id: remoteId }) {\n const request = this._requests.get(token.toString('hex'))\n\n if (request === undefined || request.isInitiator !== isInitiator) return\n\n request.push(remoteId)\n request.push(null)\n\n this.emit('pair', request.isInitiator, request.token, request.stream, remoteId)\n }\n\n pair (isInitiator, token, stream) {\n if (this._destroyed) throw errors.CHANNEL_DESTROYED()\n\n const keyString = token.toString('hex')\n\n if (this._requests.has(keyString)) throw errors.ALREADY_PAIRING()\n\n const request = new BlindRelayRequest(this, isInitiator, token, stream)\n\n this._requests.set(keyString, request)\n\n return request\n }\n\n unpair (token) {\n if (this._destroyed) throw errors.CHANNEL_DESTROYED()\n\n const request = this._requests.get(token.toString('hex'))\n\n if (request) request.destroy(errors.PAIRING_CANCELLED())\n\n this._unpair.send({ token })\n }\n\n cork () {\n this._channel.cork()\n }\n\n uncork () {\n this._channel.uncork()\n }\n\n async end () {\n if (this._ending) return this._ending\n\n this._ending = EventEmitter.once(this, 'close')\n this._endMaybe()\n\n return this._ending\n }\n\n _endMaybe () {\n if (this._ending && this._requests.size === 0) {\n this._channel.close()\n }\n }\n\n destroy (err) {\n if (this._destroyed) return\n this._destroyed = true\n\n this._error = err || errors.CHANNEL_DESTROYED()\n this._channel.close()\n }\n}\n\nclass BlindRelayRequest extends Readable {\n constructor (client, isInitiator, token, stream) {\n super()\n\n this.client = client\n this.isInitiator = isInitiator\n this.token = token\n this.stream = stream\n }\n\n _open (cb) {\n if (this.client._destroyed) return cb(errors.CHANNEL_DESTROYED())\n\n this.client._pair.send({\n isInitiator: this.isInitiator,\n token: this.token,\n id: this.stream.id,\n seq: 0\n })\n\n cb(null)\n }\n\n _destroy (cb) {\n this.client._requests.delete(this.token.toString('hex'))\n\n cb(null)\n\n this.client._endMaybe()\n }\n}\n\nexports.token = function token (buf = b4a.allocUnsafe(32)) {\n sodium.randombytes_buf(buf)\n return buf\n}\n\nfunction noop () {}\n\nconst m = exports.messages = {}\n\nconst flags = bitfield(7)\n\nm.pair = {\n preencode (state, m) {\n flags.preencode(state)\n c.fixed32.preencode(state, m.token)\n c.uint.preencode(state, m.id)\n c.uint.preencode(state, m.seq)\n },\n encode (state, m) {\n flags.encode(state, bits.of(m.isInitiator))\n c.fixed32.encode(state, m.token)\n c.uint.encode(state, m.id)\n c.uint.encode(state, m.seq)\n },\n decode (state) {\n const [isInitiator] = bits.iterator(flags.decode(state))\n\n return {\n isInitiator,\n token: c.fixed32.decode(state),\n id: c.uint.decode(state),\n seq: c.uint.decode(state)\n }\n }\n}\n\nm.unpair = {\n preencode (state, m) {\n flags.preencode(state)\n c.fixed32.preencode(state, m.token)\n },\n encode (state, m) {\n flags.encode(state, bits.of())\n c.fixed32.encode(state, m.token)\n },\n decode (state) {\n flags.decode(state)\n\n return {\n token: c.fixed32.decode(state)\n }\n }\n}\nmodule.exports = class BlindRelayError extends Error {\n constructor (msg, code, fn = BlindRelayError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name () {\n return 'BlindRelayError'\n }\n\n static DUPLICATE_CHANNEL (msg = 'Duplicate channel') {\n return new BlindRelayError(msg, 'DUPLICATE_CHANNEL', BlindRelayError.DUPLICATE_CHANNEL)\n }\n\n static CHANNEL_CLOSED (msg = 'Channel closed') {\n return new BlindRelayError(msg, 'CHANNEL_CLOSED', BlindRelayError.CHANNEL_CLOSED)\n }\n\n static CHANNEL_DESTROYED (msg = 'Channel destroyed') {\n return new BlindRelayError(msg, 'CHANNEL_DESTROYED', BlindRelayError.CHANNEL_DESTROYED)\n }\n\n static ALREADY_PAIRING (msg = 'Already pairing') {\n return new BlindRelayError(msg, 'ALREADY_PAIRING', BlindRelayError.ALREADY_PAIRING)\n }\n\n static PAIRING_CANCELLED (msg = 'Pairing cancelled') {\n return new BlindRelayError(msg, 'PAIRING_CANCELLED', BlindRelayError.PAIRING_CANCELLED)\n }\n}\n{\n \"name\": \"blind-relay\",\n \"version\": \"1.4.0\",\n \"description\": \"Blind relay for UDX over Protomux channels\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/blind-relay.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/blind-relay/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/blind-relay#readme\",\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"dependencies\": {\n \"b4a\": \"^1.6.4\",\n \"bare-events\": \"^2.2.0\",\n \"bits-to-bytes\": \"^1.3.0\",\n \"compact-encoding\": \"^2.12.0\",\n \"compact-encoding-bitfield\": \"^1.0.0\",\n \"protomux\": \"^3.5.1\",\n \"sodium-universal\": \"^5.0.0\",\n \"streamx\": \"^2.15.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.2.1\",\n \"hyperdht\": \"^6.6.1\",\n \"standard\": \"^17.0.0\",\n \"udx-native\": \"^1.6.1\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n// https://ipinfo.io/bogon\n\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\nconst net = require('compact-encoding-net')\n\nmodule.exports = exports = function isBogon (ip) {\n return isBogonIP(ensureBuffer(ip))\n}\n\nexports.isBogon = exports\n\nexports.isPrivate = function isPrivate (ip) {\n return isPrivateIP(ensureBuffer(ip))\n}\n\nfunction isBogonIP (ip) {\n return isPrivateIP(ip) || isReservedIP(ip)\n}\n\nfunction isPrivateIP (ip) {\n return ip.byteLength === 4 ? isPrivateIPv4(ip) : false // IPv6 has no private IPs\n}\n\nfunction isPrivateIPv4 (ip) {\n return (\n // 10.0.0.0/8 Private-use networks\n (ip[0] === 10) ||\n // 100.64.0.0/10 Carrier-grade NAT\n (ip[0] === 100 && ip[1] >= 64 && ip[1] <= 127) ||\n // 127.0.0.0/8 Loopback + Name collision occurrence (127.0.53.53)\n (ip[0] === 127) ||\n // 169.254.0.0/16 Link local\n (ip[0] === 169 && ip[1] === 254) ||\n // 172.16.0.0/12 Private-use networks\n (ip[0] === 172 && ip[1] >= 16 && ip[1] <= 31) ||\n // 192.168.0.0/16 Private-use networks\n (ip[0] === 192 && ip[1] === 168)\n )\n}\n\nfunction isReservedIP (ip) {\n return ip.byteLength === 4 ? isReservedIPv4(ip) : isReservedIPv6(ip)\n}\n\nfunction isReservedIPv4 (ip) {\n return (\n // 0.0.0.0/8 \"This\" network\n (ip[0] === 0) ||\n // 192.0.0.0/24 IETF protocol assignments\n (ip[0] === 192 && ip[1] === 0 && ip[2] === 0) ||\n // 192.0.2.0/24 TEST-NET-1\n (ip[0] === 192 && ip[1] === 0 && ip[2] === 2) ||\n // 198.18.0.0/15 Network interconnect device benchmark testing\n (ip[0] === 198 && ip[1] >= 18 && ip[1] <= 19) ||\n // 198.51.100.0/24 TEST-NET-2\n (ip[0] === 198 && ip[1] === 51 && ip[2] === 100) ||\n // 203.0.113.0/24 TEST-NET-3\n (ip[0] === 203 && ip[1] === 0 && ip[2] === 113) ||\n // 224.0.0.0/4 Multicast\n (ip[0] >= 224 && ip[0] <= 239) ||\n // 240.0.0.0/4 Reserved for future use\n (ip[0] >= 240) ||\n // 255.255.255.255/32\n (ip[0] === 255 && ip[1] === 255 && ip[2] === 255 && ip[3] === 255)\n )\n}\n\nfunction isReservedIPv6 (ip) {\n return (\n // ::/128 Node-scope unicast unspecified address\n // ::1/128 Node-scope unicast loopback address\n (\n ip[0] === 0 && ip[1] === 0 && ip[2] === 0 && ip[3] === 0 && ip[4] === 0 &&\n ip[5] === 0 && ip[6] === 0 && ip[7] === 0 && ip[8] === 0 && ip[9] === 0 &&\n ip[10] === 0 && ip[11] === 0 && ip[12] === 0 && ip[13] === 0 && ip[14] === 0 &&\n ip[15] <= 1\n ) ||\n // ::ffff:0:0/96 IPv4-mapped addresses\n // ::/96 IPv4-compatible addresses\n (\n ip[0] === 0 && ip[1] === 0 && ip[2] === 0 && ip[3] === 0 && ip[4] === 0 &&\n ip[5] === 0 && ip[6] === 0 && ip[7] === 0 && ip[8] === 0 && ip[9] === 0 &&\n (ip[10] === 0 || ip[10] === 0xff) &&\n (ip[11] === 0 || ip[11] === 0xff)\n ) ||\n // 100::/64 Remotely triggered black hole addresses\n (ip[0] === 0x01 && ip[1] === 0 && ip[2] === 0 && ip[3] === 0 && ip[4] === 0 && ip[5] === 0 && ip[6] === 0 && ip[7] === 0) ||\n // 2001:10::/28 Overlay routable cryptographic hash identifiers (ORCHID)\n (ip[0] === 0x20 && ip[1] === 0x01 && ip[2] === 0 && ip[3] >= 0x10 && ip[3] <= 0x1f) ||\n // 2001:20::/28 Overlay routable cryptographic hash identifiers version 2 (ORCHIDv2)\n (ip[0] === 0x20 && ip[1] === 0x01 && ip[2] === 0 && ip[3] >= 0x20 && ip[3] <= 0x2f) ||\n // 2001:db8::/32 Documentation prefix\n (ip[0] === 0x20 && ip[1] === 0x01 && ip[2] === 0x0d && ip[3] === 0xb8) ||\n // fc00::/7 Unique local addresses (ULA)\n (ip[0] >= 0xfc && ip[0] <= 0xfd) ||\n // fe80::/10 Link-local unicast\n (ip[0] === 0xfe && ip[1] >= 0x80 && ip[1] <= 0xbf) ||\n // ff00::/8 Multicast\n (ip[0] === 0xff)\n )\n}\n\nconst state = c.state(0, 0, b4a.allocUnsafe(1 /* family */ + 16))\n\nfunction ensureBuffer (ip) {\n if (b4a.isBuffer(ip)) return ip\n\n net.ip.preencode(state, ip)\n net.ip.encode(state, ip)\n\n const buffer = state.buffer.subarray(1 /* family */, state.end)\n\n state.start = 0\n state.end = 0\n\n return buffer\n}\n{\n \"name\": \"bogon\",\n \"version\": \"1.1.0\",\n \"description\": \"Check if an IP is a bogon\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"dependencies\": {\n \"compact-encoding\": \"^2.11.0\",\n \"compact-encoding-net\": \"^1.2.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.4\",\n \"nanobench\": \"^2.1.1\",\n \"standard\": \"^17.0.0\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/bogon.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/bogon/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/bogon\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n'use strict'\n\nconst base64 = require('base64-js')\nconst ieee754 = require('ieee754')\nconst customInspectSymbol =\n (typeof Symbol === 'function' && typeof Symbol['for'] === 'function') // eslint-disable-line dot-notation\n ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation\n : null\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\nconst K_MAX_LENGTH = 0x7fffffff\nexports.kMaxLength = K_MAX_LENGTH\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Print warning and recommend using `buffer` v4.x which has an Object\n * implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * We report that the browser does not support typed arrays if the are not subclassable\n * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`\n * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support\n * for __proto__ and has a buggy typed array implementation.\n */\nBuffer.TYPED_ARRAY_SUPPORT = typedArraySupport()\n\nif (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&\n typeof console.error === 'function') {\n console.error(\n 'This browser lacks typed array (Uint8Array) support which is required by ' +\n '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'\n )\n}\n\nfunction typedArraySupport () {\n // Can typed array instances can be augmented?\n try {\n const arr = new Uint8Array(1)\n const proto = { foo: function () { return 42 } }\n Object.setPrototypeOf(proto, Uint8Array.prototype)\n Object.setPrototypeOf(arr, proto)\n return arr.foo() === 42\n } catch (e) {\n return false\n }\n}\n\nObject.defineProperty(Buffer.prototype, 'parent', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined\n return this.buffer\n }\n})\n\nObject.defineProperty(Buffer.prototype, 'offset', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined\n return this.byteOffset\n }\n})\n\nfunction createBuffer (length) {\n if (length > K_MAX_LENGTH) {\n throw new RangeError('The value \"' + length + '\" is invalid for option \"size\"')\n }\n // Return an augmented `Uint8Array` instance\n const buf = new Uint8Array(length)\n Object.setPrototypeOf(buf, Buffer.prototype)\n return buf\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new TypeError(\n 'The \"string\" argument must be of type string. Received type number'\n )\n }\n return allocUnsafe(arg)\n }\n return from(arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\nfunction from (value, encodingOrOffset, length) {\n if (typeof value === 'string') {\n return fromString(value, encodingOrOffset)\n }\n\n if (ArrayBuffer.isView(value)) {\n return fromArrayView(value)\n }\n\n if (value == null) {\n throw new TypeError(\n 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +\n 'or Array-like Object. Received type ' + (typeof value)\n )\n }\n\n if (isInstance(value, ArrayBuffer) ||\n (value && isInstance(value.buffer, ArrayBuffer))) {\n return fromArrayBuffer(value, encodingOrOffset, length)\n }\n\n if (typeof SharedArrayBuffer !== 'undefined' &&\n (isInstance(value, SharedArrayBuffer) ||\n (value && isInstance(value.buffer, SharedArrayBuffer)))) {\n return fromArrayBuffer(value, encodingOrOffset, length)\n }\n\n if (typeof value === 'number') {\n throw new TypeError(\n 'The \"value\" argument must not be of type number. Received type number'\n )\n }\n\n const valueOf = value.valueOf && value.valueOf()\n if (valueOf != null && valueOf !== value) {\n return Buffer.from(valueOf, encodingOrOffset, length)\n }\n\n const b = fromObject(value)\n if (b) return b\n\n if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&\n typeof value[Symbol.toPrimitive] === 'function') {\n return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length)\n }\n\n throw new TypeError(\n 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +\n 'or Array-like Object. Received type ' + (typeof value)\n )\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(value, encodingOrOffset, length)\n}\n\n// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:\n// https://github.com/feross/buffer/pull/148\nObject.setPrototypeOf(Buffer.prototype, Uint8Array.prototype)\nObject.setPrototypeOf(Buffer, Uint8Array)\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be of type number')\n } else if (size < 0) {\n throw new RangeError('The value \"' + size + '\" is invalid for option \"size\"')\n }\n}\n\nfunction alloc (size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpreted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(size).fill(fill, encoding)\n : createBuffer(size).fill(fill)\n }\n return createBuffer(size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(size, fill, encoding)\n}\n\nfunction allocUnsafe (size) {\n assertSize(size)\n return createBuffer(size < 0 ? 0 : checked(size) | 0)\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(size)\n}\n\nfunction fromString (string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n\n const length = byteLength(string, encoding) | 0\n let buf = createBuffer(length)\n\n const actual = buf.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n buf = buf.slice(0, actual)\n }\n\n return buf\n}\n\nfunction fromArrayLike (array) {\n const length = array.length < 0 ? 0 : checked(array.length) | 0\n const buf = createBuffer(length)\n for (let i = 0; i < length; i += 1) {\n buf[i] = array[i] & 255\n }\n return buf\n}\n\nfunction fromArrayView (arrayView) {\n if (isInstance(arrayView, Uint8Array)) {\n const copy = new Uint8Array(arrayView)\n return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength)\n }\n return fromArrayLike(arrayView)\n}\n\nfunction fromArrayBuffer (array, byteOffset, length) {\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\"offset\" is outside of buffer bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\"length\" is outside of buffer bounds')\n }\n\n let buf\n if (byteOffset === undefined && length === undefined) {\n buf = new Uint8Array(array)\n } else if (length === undefined) {\n buf = new Uint8Array(array, byteOffset)\n } else {\n buf = new Uint8Array(array, byteOffset, length)\n }\n\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(buf, Buffer.prototype)\n\n return buf\n}\n\nfunction fromObject (obj) {\n if (Buffer.isBuffer(obj)) {\n const len = checked(obj.length) | 0\n const buf = createBuffer(len)\n\n if (buf.length === 0) {\n return buf\n }\n\n obj.copy(buf, 0, 0, len)\n return buf\n }\n\n if (obj.length !== undefined) {\n if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {\n return createBuffer(0)\n }\n return fromArrayLike(obj)\n }\n\n if (obj.type === 'Buffer' && Array.isArray(obj.data)) {\n return fromArrayLike(obj.data)\n }\n}\n\nfunction checked (length) {\n // Note: cannot use `length < K_MAX_LENGTH` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= K_MAX_LENGTH) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return b != null && b._isBuffer === true &&\n b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false\n}\n\nBuffer.compare = function compare (a, b) {\n if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)\n if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError(\n 'The \"buf1\", \"buf2\" arguments must be one of type Buffer or Uint8Array'\n )\n }\n\n if (a === b) return 0\n\n let x = a.length\n let y = b.length\n\n for (let i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!Array.isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n let i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n const buffer = Buffer.allocUnsafe(length)\n let pos = 0\n for (i = 0; i < list.length; ++i) {\n let buf = list[i]\n if (isInstance(buf, Uint8Array)) {\n if (pos + buf.length > buffer.length) {\n if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf)\n buf.copy(buffer, pos)\n } else {\n Uint8Array.prototype.set.call(\n buffer,\n buf,\n pos\n )\n }\n } else if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n } else {\n buf.copy(buffer, pos)\n }\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n throw new TypeError(\n 'The \"string\" argument must be one of type string, Buffer, or ArrayBuffer. ' +\n 'Received type ' + typeof string\n )\n }\n\n const len = string.length\n const mustMatch = (arguments.length > 2 && arguments[2] === true)\n if (!mustMatch && len === 0) return 0\n\n // Use a for loop to avoid recursion\n let loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) {\n return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8\n }\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n let loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coercion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)\n// to detect a Buffer instance. It's not possible to use `instanceof Buffer`\n// reliably in a browserify context because there could be multiple different\n// copies of the 'buffer' package in use. This method works even for Buffer\n// instances that were created from another copy of the `buffer` package.\n// See: https://github.com/feross/buffer/issues/154\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n const i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n const len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (let i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n const len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (let i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n const len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (let i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n const length = this.length\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.toLocaleString = Buffer.prototype.toString\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n let str = ''\n const max = exports.INSPECT_MAX_BYTES\n str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()\n if (this.length > max) str += ' ... '\n return ''\n}\nif (customInspectSymbol) {\n Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (isInstance(target, Uint8Array)) {\n target = Buffer.from(target, target.offset, target.byteLength)\n }\n if (!Buffer.isBuffer(target)) {\n throw new TypeError(\n 'The \"target\" argument must be one of type Buffer or Uint8Array. ' +\n 'Received type ' + (typeof target)\n )\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n let x = thisEnd - thisStart\n let y = end - start\n const len = Math.min(x, y)\n\n const thisCopy = this.slice(thisStart, thisEnd)\n const targetCopy = target.slice(start, end)\n\n for (let i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (numberIsNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [val], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n let indexSize = 1\n let arrLength = arr.length\n let valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n let i\n if (dir) {\n let foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n let found = true\n for (let j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n const remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n const strLen = string.length\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n let i\n for (i = 0; i < length; ++i) {\n const parsed = parseInt(string.substr(i * 2, 2), 16)\n if (numberIsNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset >>> 0\n if (isFinite(length)) {\n length = length >>> 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n const remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n let loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n case 'latin1':\n case 'binary':\n return asciiWrite(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n const res = []\n\n let i = start\n while (i < end) {\n const firstByte = buf[i]\n let codePoint = null\n let bytesPerSequence = (firstByte > 0xEF)\n ? 4\n : (firstByte > 0xDF)\n ? 3\n : (firstByte > 0xBF)\n ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n let secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nconst MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n const len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n let res = ''\n let i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n let ret = ''\n end = Math.min(buf.length, end)\n\n for (let i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n let ret = ''\n end = Math.min(buf.length, end)\n\n for (let i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n const len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n let out = ''\n for (let i = start; i < end; ++i) {\n out += hexSliceLookupTable[buf[i]]\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n const bytes = buf.slice(start, end)\n let res = ''\n // If bytes.length is odd, the last 8 bits must be ignored (same as node.js)\n for (let i = 0; i < bytes.length - 1; i += 2) {\n res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n const len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n const newBuf = this.subarray(start, end)\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(newBuf, Buffer.prototype)\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUintLE =\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let val = this[offset]\n let mul = 1\n let i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUintBE =\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n let val = this[offset + --byteLength]\n let mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUint8 =\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUint16LE =\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUint16BE =\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUint32LE =\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUint32BE =\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const lo = first +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 24\n\n const hi = this[++offset] +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n last * 2 ** 24\n\n return BigInt(lo) + (BigInt(hi) << BigInt(32))\n})\n\nBuffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const hi = first * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n this[++offset]\n\n const lo = this[++offset] * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n last\n\n return (BigInt(hi) << BigInt(32)) + BigInt(lo)\n})\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let val = this[offset]\n let mul = 1\n let i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let i = byteLength\n let mul = 1\n let val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n const val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n const val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const val = this[offset + 4] +\n this[offset + 5] * 2 ** 8 +\n this[offset + 6] * 2 ** 16 +\n (last << 24) // Overflow\n\n return (BigInt(val) << BigInt(32)) +\n BigInt(first +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 24)\n})\n\nBuffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const val = (first << 24) + // Overflow\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n this[++offset]\n\n return (BigInt(val) << BigInt(32)) +\n BigInt(this[++offset] * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n last)\n})\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUintLE =\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n const maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n let mul = 1\n let i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUintBE =\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n const maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n let i = byteLength - 1\n let mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUint8 =\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeUint16LE =\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n return offset + 2\n}\n\nBuffer.prototype.writeUint16BE =\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n return offset + 2\n}\n\nBuffer.prototype.writeUint32LE =\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n return offset + 4\n}\n\nBuffer.prototype.writeUint32BE =\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n return offset + 4\n}\n\nfunction wrtBigUInt64LE (buf, value, offset, min, max) {\n checkIntBI(value, min, max, buf, offset, 7)\n\n let lo = Number(value & BigInt(0xffffffff))\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n let hi = Number(value >> BigInt(32) & BigInt(0xffffffff))\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n return offset\n}\n\nfunction wrtBigUInt64BE (buf, value, offset, min, max) {\n checkIntBI(value, min, max, buf, offset, 7)\n\n let lo = Number(value & BigInt(0xffffffff))\n buf[offset + 7] = lo\n lo = lo >> 8\n buf[offset + 6] = lo\n lo = lo >> 8\n buf[offset + 5] = lo\n lo = lo >> 8\n buf[offset + 4] = lo\n let hi = Number(value >> BigInt(32) & BigInt(0xffffffff))\n buf[offset + 3] = hi\n hi = hi >> 8\n buf[offset + 2] = hi\n hi = hi >> 8\n buf[offset + 1] = hi\n hi = hi >> 8\n buf[offset] = hi\n return offset + 8\n}\n\nBuffer.prototype.writeBigUInt64LE = defineBigIntMethod(function writeBigUInt64LE (value, offset = 0) {\n return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff'))\n})\n\nBuffer.prototype.writeBigUInt64BE = defineBigIntMethod(function writeBigUInt64BE (value, offset = 0) {\n return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff'))\n})\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n const limit = Math.pow(2, (8 * byteLength) - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n let i = 0\n let mul = 1\n let sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n const limit = Math.pow(2, (8 * byteLength) - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n let i = byteLength - 1\n let mul = 1\n let sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n return offset + 4\n}\n\nBuffer.prototype.writeBigInt64LE = defineBigIntMethod(function writeBigInt64LE (value, offset = 0) {\n return wrtBigUInt64LE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff'))\n})\n\nBuffer.prototype.writeBigInt64BE = defineBigIntMethod(function writeBigInt64BE (value, offset = 0) {\n return wrtBigUInt64BE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff'))\n})\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('Index out of range')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n const len = end - start\n\n if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {\n // Use built-in when available, missing from IE11\n this.copyWithin(targetStart, start, end)\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, end),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n if (val.length === 1) {\n const code = val.charCodeAt(0)\n if ((encoding === 'utf8' && code < 128) ||\n encoding === 'latin1') {\n // Fast path: If `val` fits into a single byte, use that numeric value.\n val = code\n }\n }\n } else if (typeof val === 'number') {\n val = val & 255\n } else if (typeof val === 'boolean') {\n val = Number(val)\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n let i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n const bytes = Buffer.isBuffer(val)\n ? val\n : Buffer.from(val, encoding)\n const len = bytes.length\n if (len === 0) {\n throw new TypeError('The value \"' + val +\n '\" is invalid for argument \"value\"')\n }\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// CUSTOM ERRORS\n// =============\n\n// Simplified versions from Node, changed for Buffer-only usage\nconst errors = {}\nfunction E (sym, getMessage, Base) {\n errors[sym] = class NodeError extends Base {\n constructor () {\n super()\n\n Object.defineProperty(this, 'message', {\n value: getMessage.apply(this, arguments),\n writable: true,\n configurable: true\n })\n\n // Add the error code to the name to include it in the stack trace.\n this.name = `${this.name} [${sym}]`\n // Access the stack to generate the error message including the error code\n // from the name.\n this.stack // eslint-disable-line no-unused-expressions\n // Reset the name to the actual name.\n delete this.name\n }\n\n get code () {\n return sym\n }\n\n set code (value) {\n Object.defineProperty(this, 'code', {\n configurable: true,\n enumerable: true,\n value,\n writable: true\n })\n }\n\n toString () {\n return `${this.name} [${sym}]: ${this.message}`\n }\n }\n}\n\nE('ERR_BUFFER_OUT_OF_BOUNDS',\n function (name) {\n if (name) {\n return `${name} is outside of buffer bounds`\n }\n\n return 'Attempt to access memory outside buffer bounds'\n }, RangeError)\nE('ERR_INVALID_ARG_TYPE',\n function (name, actual) {\n return `The \"${name}\" argument must be of type number. Received type ${typeof actual}`\n }, TypeError)\nE('ERR_OUT_OF_RANGE',\n function (str, range, input) {\n let msg = `The value of \"${str}\" is out of range.`\n let received = input\n if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) {\n received = addNumericalSeparator(String(input))\n } else if (typeof input === 'bigint') {\n received = String(input)\n if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) {\n received = addNumericalSeparator(received)\n }\n received += 'n'\n }\n msg += ` It must be ${range}. Received ${received}`\n return msg\n }, RangeError)\n\nfunction addNumericalSeparator (val) {\n let res = ''\n let i = val.length\n const start = val[0] === '-' ? 1 : 0\n for (; i >= start + 4; i -= 3) {\n res = `_${val.slice(i - 3, i)}${res}`\n }\n return `${val.slice(0, i)}${res}`\n}\n\n// CHECK FUNCTIONS\n// ===============\n\nfunction checkBounds (buf, offset, byteLength) {\n validateNumber(offset, 'offset')\n if (buf[offset] === undefined || buf[offset + byteLength] === undefined) {\n boundsError(offset, buf.length - (byteLength + 1))\n }\n}\n\nfunction checkIntBI (value, min, max, buf, offset, byteLength) {\n if (value > max || value < min) {\n const n = typeof min === 'bigint' ? 'n' : ''\n let range\n if (byteLength > 3) {\n if (min === 0 || min === BigInt(0)) {\n range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`\n } else {\n range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` +\n `${(byteLength + 1) * 8 - 1}${n}`\n }\n } else {\n range = `>= ${min}${n} and <= ${max}${n}`\n }\n throw new errors.ERR_OUT_OF_RANGE('value', range, value)\n }\n checkBounds(buf, offset, byteLength)\n}\n\nfunction validateNumber (value, name) {\n if (typeof value !== 'number') {\n throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value)\n }\n}\n\nfunction boundsError (value, length, type) {\n if (Math.floor(value) !== value) {\n validateNumber(value, type)\n throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value)\n }\n\n if (length < 0) {\n throw new errors.ERR_BUFFER_OUT_OF_BOUNDS()\n }\n\n throw new errors.ERR_OUT_OF_RANGE(type || 'offset',\n `>= ${type ? 1 : 0} and <= ${length}`,\n value)\n}\n\n// HELPER FUNCTIONS\n// ================\n\nconst INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node takes equal signs as end of the Base64 encoding\n str = str.split('=')[0]\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = str.trim().replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n let codePoint\n const length = string.length\n let leadSurrogate = null\n const bytes = []\n\n for (let i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n const byteArray = []\n for (let i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n let c, hi, lo\n const byteArray = []\n for (let i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n let i\n for (i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\n// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass\n// the `instanceof` check but they should be treated as of that type.\n// See: https://github.com/feross/buffer/issues/166\nfunction isInstance (obj, type) {\n return obj instanceof type ||\n (obj != null && obj.constructor != null && obj.constructor.name != null &&\n obj.constructor.name === type.name)\n}\nfunction numberIsNaN (obj) {\n // For IE11 support\n return obj !== obj // eslint-disable-line no-self-compare\n}\n\n// Create lookup table for `toString('hex')`\n// See: https://github.com/feross/buffer/issues/219\nconst hexSliceLookupTable = (function () {\n const alphabet = '0123456789abcdef'\n const table = new Array(256)\n for (let i = 0; i < 16; ++i) {\n const i16 = i * 16\n for (let j = 0; j < 16; ++j) {\n table[i16 + j] = alphabet[i] + alphabet[j]\n }\n }\n return table\n})()\n\n// Return not function with Error if BigInt not supported\nfunction defineBigIntMethod (fn) {\n return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn\n}\n\nfunction BufferBigIntNotDefined () {\n throw new Error('BigInt not supported')\n}\n{\n \"name\": \"buffer\",\n \"description\": \"Node.js Buffer API, for the browser\",\n \"version\": \"6.0.3\",\n \"author\": {\n \"name\": \"Feross Aboukhadijeh\",\n \"email\": \"feross@feross.org\",\n \"url\": \"https://feross.org\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/feross/buffer/issues\"\n },\n \"contributors\": [\n \"Romain Beauxis \",\n \"James Halliday \"\n ],\n \"dependencies\": {\n \"base64-js\": \"^1.3.1\",\n \"ieee754\": \"^1.2.1\"\n },\n \"devDependencies\": {\n \"airtap\": \"^3.0.0\",\n \"benchmark\": \"^2.1.4\",\n \"browserify\": \"^17.0.0\",\n \"concat-stream\": \"^2.0.0\",\n \"hyperquest\": \"^2.1.3\",\n \"is-buffer\": \"^2.0.5\",\n \"is-nan\": \"^1.3.0\",\n \"split\": \"^1.0.1\",\n \"standard\": \"*\",\n \"tape\": \"^5.0.1\",\n \"through2\": \"^4.0.2\",\n \"uglify-js\": \"^3.11.5\"\n },\n \"homepage\": \"https://github.com/feross/buffer\",\n \"jspm\": {\n \"map\": {\n \"./index.js\": {\n \"node\": \"@node/buffer\"\n }\n }\n },\n \"keywords\": [\n \"arraybuffer\",\n \"browser\",\n \"browserify\",\n \"buffer\",\n \"compatible\",\n \"dataview\",\n \"uint8array\"\n ],\n \"license\": \"MIT\",\n \"main\": \"index.js\",\n \"types\": \"index.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/feross/buffer.git\"\n },\n \"scripts\": {\n \"perf\": \"browserify --debug perf/bracket-notation.js > perf/bundle.js && open perf/index.html\",\n \"perf-node\": \"node perf/bracket-notation.js && node perf/concat.js && node perf/copy-big.js && node perf/copy.js && node perf/new-big.js && node perf/new.js && node perf/readDoubleBE.js && node perf/readFloatBE.js && node perf/readUInt32LE.js && node perf/slice.js && node perf/writeFloatBE.js\",\n \"size\": \"browserify -r ./ | uglifyjs -c -m | gzip | wc -c\",\n \"test\": \"standard && node ./bin/test.js\",\n \"test-browser-old\": \"airtap -- test/*.js\",\n \"test-browser-old-local\": \"airtap --local -- test/*.js\",\n \"test-browser-new\": \"airtap -- test/*.js test/node/*.js\",\n \"test-browser-new-local\": \"airtap --local -- test/*.js test/node/*.js\",\n \"test-node\": \"tape test/*.js test/node/*.js\",\n \"update-authors\": \"./bin/update-authors.sh\"\n },\n \"standard\": {\n \"ignore\": [\n \"test/node/**/*.js\",\n \"test/common.js\",\n \"test/_polyfill.js\",\n \"perf/**/*.js\"\n ]\n },\n \"funding\": [\n {\n \"type\": \"github\",\n \"url\": \"https://github.com/sponsors/feross\"\n },\n {\n \"type\": \"patreon\",\n \"url\": \"https://www.patreon.com/feross\"\n },\n {\n \"type\": \"consulting\",\n \"url\": \"https://feross.org/support\"\n }\n ],\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst c = require('compact-encoding')\n\nmodule.exports = function bitfield (length) {\n if (length > 64) throw new RangeError('Bitfield cannot be larger than 64 bits')\n\n let byteLength\n if (length < 8) byteLength = 1\n else if (length <= 16) byteLength = 2\n else if (length <= 32) byteLength = 4\n else byteLength = 8\n\n return {\n preencode (state) {\n state.end++ // Length byte, used for data when byteLength === 1\n\n if (byteLength === 1) ;\n else if (byteLength === 2) c.uint16.preencode(state)\n else if (byteLength === 4) c.uint32.preencode(state)\n else c.uint64.preencode(state)\n },\n\n encode (state, b) {\n if (byteLength === 1) ;\n else if (byteLength === 2) c.uint8.encode(state, 0xfd)\n else if (byteLength === 4) c.uint8.encode(state, 0xfe)\n else c.uint8.encode(state, 0xff)\n\n if (typeof b === 'number') {\n if (byteLength === 1) c.uint8.encode(state, b)\n else if (byteLength === 2) c.uint16.encode(state, b)\n else if (byteLength === 4) c.uint32.encode(state, b)\n else c.uint64.encode(state, b)\n } else {\n state.buffer.set(b, state.start)\n\n if (b.byteLength < byteLength) {\n // Zero-fill the rest of the byte length.\n state.buffer.fill(\n 0,\n state.start + b.byteLength,\n state.start + byteLength\n )\n }\n\n state.start += byteLength\n }\n },\n\n decode (state) {\n const byte = state.buffer[state.start]\n\n let byteLength\n if (byte <= 0xfc) byteLength = 1\n else if (byte === 0xfd) byteLength = 2\n else if (byte === 0xfe) byteLength = 4\n else byteLength = 8\n\n if (byteLength > 1) state.start++ // Skip the length byte\n\n if (state.end - state.start < byteLength) throw new Error('Out of bounds')\n\n const b = state.buffer.subarray(state.start, (state.start += byteLength))\n\n return length <= 8 ? b.subarray(0, 1) : b\n }\n }\n}\n{\n \"name\": \"compact-encoding-bitfield\",\n \"version\": \"1.0.0\",\n \"description\": \"Compact codec for bitfields\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/compact-encoding/compact-encoding-bitfield.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð \",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/compact-encoding/compact-encoding-bitfield/issues\"\n },\n \"homepage\": \"https://github.com/compact-encoding/compact-encoding-bitfield#readme\",\n \"dependencies\": {\n \"compact-encoding\": \"^2.4.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^1.3.5\",\n \"standard\": \"^16.0.3\"\n },\n \"standard\": {\n \"ignore\": [\n \"__snapshots__/**\"\n ]\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst c = require('compact-encoding')\n\nconst port = c.uint16\n\nconst address = (host, family) => {\n return {\n preencode (state, m) {\n host.preencode(state, m.host)\n port.preencode(state, m.port)\n },\n encode (state, m) {\n host.encode(state, m.host)\n port.encode(state, m.port)\n },\n decode (state) {\n return {\n host: host.decode(state),\n family,\n port: port.decode(state)\n }\n }\n }\n}\n\nconst ipv4 = {\n preencode (state) {\n state.end += 4\n },\n encode (state, string) {\n const start = state.start\n const end = start + 4\n\n let i = 0\n\n while (i < string.length) {\n let n = 0\n let c\n\n while (i < string.length && (c = string.charCodeAt(i++)) !== /* . */ 0x2e) {\n n = n * 10 + (c - /* 0 */ 0x30)\n }\n\n state.buffer[state.start++] = n\n }\n\n state.start = end\n },\n decode (state) {\n if (state.end - state.start < 4) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] + '.' +\n state.buffer[state.start++] + '.' +\n state.buffer[state.start++] + '.' +\n state.buffer[state.start++]\n )\n }\n}\n\nconst ipv4Address = address(ipv4, 4)\n\nconst ipv6 = {\n preencode (state) {\n state.end += 16\n },\n encode (state, string) {\n const start = state.start\n const end = start + 16\n\n let i = 0\n let split = null\n\n while (i < string.length) {\n let n = 0\n let c\n\n while (i < string.length && (c = string.charCodeAt(i++)) !== /* : */ 0x3a) {\n if (c >= 0x30 && c <= 0x39) n = n * 0x10 + (c - /* 0 */ 0x30)\n else if (c >= 0x41 && c <= 0x46) n = n * 0x10 + (c - /* A */ 0x41 + 10)\n else if (c >= 0x61 && c <= 0x66) n = n * 0x10 + (c - /* a */ 0x61 + 10)\n }\n\n state.buffer[state.start++] = n >>> 8\n state.buffer[state.start++] = n\n\n if (i < string.length && string.charCodeAt(i) === /* : */ 0x3a) {\n i++\n split = state.start\n }\n }\n\n if (split !== null) {\n const offset = end - state.start\n state.buffer\n .copyWithin(split + offset, split)\n .fill(0, split, split + offset)\n }\n\n state.start = end\n },\n decode (state) {\n if (state.end - state.start < 16) throw new Error('Out of bounds')\n return (\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +\n (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16)\n )\n }\n}\n\nconst ipv6Address = address(ipv6, 6)\n\nconst ip = {\n preencode (state, string) {\n const family = string.includes(':') ? 6 : 4\n c.uint8.preencode(state, family)\n if (family === 4) ipv4.preencode(state)\n else ipv6.preencode(state)\n },\n encode (state, string) {\n const family = string.includes(':') ? 6 : 4\n c.uint8.encode(state, family)\n if (family === 4) ipv4.encode(state, string)\n else ipv6.encode(state, string)\n },\n decode (state) {\n const family = c.uint8.decode(state)\n if (family === 4) return ipv4.decode(state)\n else return ipv6.decode(state)\n }\n}\n\nconst ipAddress = {\n preencode (state, m) {\n ip.preencode(state, m.host)\n port.preencode(state, m.port)\n },\n encode (state, m) {\n ip.encode(state, m.host)\n port.encode(state, m.port)\n },\n decode (state) {\n const family = c.uint8.decode(state)\n return {\n host: family === 4 ? ipv4.decode(state) : ipv6.decode(state),\n family,\n port: port.decode(state)\n }\n }\n}\n\nmodule.exports = {\n port,\n ipv4,\n ipv4Address,\n ipv6,\n ipv6Address,\n ip,\n ipAddress\n}\n{\n \"name\": \"compact-encoding-net\",\n \"version\": \"1.2.0\",\n \"description\": \"Compact codecs for net types\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/compact-encoding/compact-encoding-net.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð \",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/compact-encoding/compact-encoding-net/issues\"\n },\n \"homepage\": \"https://github.com/compact-encoding/compact-encoding-net#readme\",\n \"dependencies\": {\n \"compact-encoding\": \"^2.4.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^1.3.5\",\n \"nanobench\": \"^2.1.1\",\n \"standard\": \"^16.0.3\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst LE = exports.LE = (new Uint8Array(new Uint16Array([0xff]).buffer))[0] === 0xff\n\nexports.BE = !LE\nconst b4a = require('b4a')\n\nconst { BE } = require('./endian')\n\nexports.state = function (start = 0, end = 0, buffer = null) {\n return { start, end, buffer, cache: null }\n}\n\nconst raw = exports.raw = require('./raw')\n\nconst uint = exports.uint = {\n preencode (state, n) {\n state.end += n <= 0xfc ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9\n },\n encode (state, n) {\n if (n <= 0xfc) uint8.encode(state, n)\n else if (n <= 0xffff) {\n state.buffer[state.start++] = 0xfd\n uint16.encode(state, n)\n } else if (n <= 0xffffffff) {\n state.buffer[state.start++] = 0xfe\n uint32.encode(state, n)\n } else {\n state.buffer[state.start++] = 0xff\n uint64.encode(state, n)\n }\n },\n decode (state) {\n const a = uint8.decode(state)\n if (a <= 0xfc) return a\n if (a === 0xfd) return uint16.decode(state)\n if (a === 0xfe) return uint32.decode(state)\n return uint64.decode(state)\n }\n}\n\nconst uint8 = exports.uint8 = {\n preencode (state, n) {\n state.end += 1\n },\n encode (state, n) {\n validateUint(n)\n state.buffer[state.start++] = n\n },\n decode (state) {\n if (state.start >= state.end) throw new Error('Out of bounds')\n return state.buffer[state.start++]\n }\n}\n\nconst uint16 = exports.uint16 = {\n preencode (state, n) {\n state.end += 2\n },\n encode (state, n) {\n validateUint(n)\n state.buffer[state.start++] = n\n state.buffer[state.start++] = n >>> 8\n },\n decode (state) {\n if (state.end - state.start < 2) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] +\n state.buffer[state.start++] * 0x100\n )\n }\n}\n\nconst uint24 = exports.uint24 = {\n preencode (state, n) {\n state.end += 3\n },\n encode (state, n) {\n validateUint(n)\n state.buffer[state.start++] = n\n state.buffer[state.start++] = n >>> 8\n state.buffer[state.start++] = n >>> 16\n },\n decode (state) {\n if (state.end - state.start < 3) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] +\n state.buffer[state.start++] * 0x100 +\n state.buffer[state.start++] * 0x10000\n )\n }\n}\n\nconst uint32 = exports.uint32 = {\n preencode (state, n) {\n state.end += 4\n },\n encode (state, n) {\n validateUint(n)\n state.buffer[state.start++] = n\n state.buffer[state.start++] = n >>> 8\n state.buffer[state.start++] = n >>> 16\n state.buffer[state.start++] = n >>> 24\n },\n decode (state) {\n if (state.end - state.start < 4) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] +\n state.buffer[state.start++] * 0x100 +\n state.buffer[state.start++] * 0x10000 +\n state.buffer[state.start++] * 0x1000000\n )\n }\n}\n\nconst uint40 = exports.uint40 = {\n preencode (state, n) {\n state.end += 5\n },\n encode (state, n) {\n validateUint(n)\n const r = Math.floor(n / 0x100)\n uint8.encode(state, n)\n uint32.encode(state, r)\n },\n decode (state) {\n if (state.end - state.start < 5) throw new Error('Out of bounds')\n return uint8.decode(state) + 0x100 * uint32.decode(state)\n }\n}\n\nconst uint48 = exports.uint48 = {\n preencode (state, n) {\n state.end += 6\n },\n encode (state, n) {\n validateUint(n)\n const r = Math.floor(n / 0x10000)\n uint16.encode(state, n)\n uint32.encode(state, r)\n },\n decode (state) {\n if (state.end - state.start < 6) throw new Error('Out of bounds')\n return uint16.decode(state) + 0x10000 * uint32.decode(state)\n }\n}\n\nconst uint56 = exports.uint56 = {\n preencode (state, n) {\n state.end += 7\n },\n encode (state, n) {\n validateUint(n)\n const r = Math.floor(n / 0x1000000)\n uint24.encode(state, n)\n uint32.encode(state, r)\n },\n decode (state) {\n if (state.end - state.start < 7) throw new Error('Out of bounds')\n return uint24.decode(state) + 0x1000000 * uint32.decode(state)\n }\n}\n\nconst uint64 = exports.uint64 = {\n preencode (state, n) {\n state.end += 8\n },\n encode (state, n) {\n validateUint(n)\n const r = Math.floor(n / 0x100000000)\n uint32.encode(state, n)\n uint32.encode(state, r)\n },\n decode (state) {\n if (state.end - state.start < 8) throw new Error('Out of bounds')\n return uint32.decode(state) + 0x100000000 * uint32.decode(state)\n }\n}\n\nconst int = exports.int = zigZagInt(uint)\nexports.int8 = zigZagInt(uint8)\nexports.int16 = zigZagInt(uint16)\nexports.int24 = zigZagInt(uint24)\nexports.int32 = zigZagInt(uint32)\nexports.int40 = zigZagInt(uint40)\nexports.int48 = zigZagInt(uint48)\nexports.int56 = zigZagInt(uint56)\nexports.int64 = zigZagInt(uint64)\n\nconst biguint64 = exports.biguint64 = {\n preencode (state, n) {\n state.end += 8\n },\n encode (state, n) {\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8)\n view.setBigUint64(0, n, true) // little endian\n state.start += 8\n },\n decode (state) {\n if (state.end - state.start < 8) throw new Error('Out of bounds')\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8)\n const n = view.getBigUint64(0, true) // little endian\n state.start += 8\n return n\n }\n}\n\nexports.bigint64 = zigZagBigInt(biguint64)\n\nconst biguint = exports.biguint = {\n preencode (state, n) {\n let len = 0\n for (let m = n; m; m = m >> 64n) len++\n uint.preencode(state, len)\n state.end += 8 * len\n },\n encode (state, n) {\n let len = 0\n for (let m = n; m; m = m >> 64n) len++\n uint.encode(state, len)\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8 * len)\n for (let m = n, i = 0; m; m = m >> 64n, i += 8) {\n view.setBigUint64(i, BigInt.asUintN(64, m), true) // little endian\n }\n state.start += 8 * len\n },\n decode (state) {\n const len = uint.decode(state)\n if (state.end - state.start < 8 * len) throw new Error('Out of bounds')\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8 * len)\n let n = 0n\n for (let i = len - 1; i >= 0; i--) n = (n << 64n) + view.getBigUint64(i * 8, true) // little endian\n state.start += 8 * len\n return n\n }\n}\n\nexports.bigint = zigZagBigInt(biguint)\n\nexports.lexint = require('./lexint')\n\nexports.float32 = {\n preencode (state, n) {\n state.end += 4\n },\n encode (state, n) {\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 4)\n view.setFloat32(0, n, true) // little endian\n state.start += 4\n },\n decode (state) {\n if (state.end - state.start < 4) throw new Error('Out of bounds')\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 4)\n const float = view.getFloat32(0, true) // little endian\n state.start += 4\n return float\n }\n}\n\nexports.float64 = {\n preencode (state, n) {\n state.end += 8\n },\n encode (state, n) {\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8)\n view.setFloat64(0, n, true) // little endian\n state.start += 8\n },\n decode (state) {\n if (state.end - state.start < 8) throw new Error('Out of bounds')\n const view = new DataView(state.buffer.buffer, state.start + state.buffer.byteOffset, 8)\n const float = view.getFloat64(0, true) // little endian\n state.start += 8\n return float\n }\n}\n\nconst buffer = exports.buffer = {\n preencode (state, b) {\n if (b) uint8array.preencode(state, b)\n else state.end++\n },\n encode (state, b) {\n if (b) uint8array.encode(state, b)\n else state.buffer[state.start++] = 0\n },\n decode (state) {\n const len = uint.decode(state)\n if (len === 0) return null\n if (state.end - state.start < len) throw new Error('Out of bounds')\n return state.buffer.subarray(state.start, (state.start += len))\n }\n}\n\nexports.binary = {\n ...buffer,\n preencode (state, b) {\n if (typeof b === 'string') utf8.preencode(state, b)\n else buffer.preencode(state, b)\n },\n encode (state, b) {\n if (typeof b === 'string') utf8.encode(state, b)\n else buffer.encode(state, b)\n }\n}\n\nexports.arraybuffer = {\n preencode (state, b) {\n uint.preencode(state, b.byteLength)\n state.end += b.byteLength\n },\n encode (state, b) {\n uint.encode(state, b.byteLength)\n\n const view = new Uint8Array(b)\n\n state.buffer.set(view, state.start)\n state.start += b.byteLength\n },\n decode (state) {\n const len = uint.decode(state)\n\n const b = new ArrayBuffer(len)\n const view = new Uint8Array(b)\n\n view.set(state.buffer.subarray(state.start, state.start += len))\n\n return b\n }\n}\n\nfunction typedarray (TypedArray, swap) {\n const n = TypedArray.BYTES_PER_ELEMENT\n\n return {\n preencode (state, b) {\n uint.preencode(state, b.length)\n state.end += b.byteLength\n },\n encode (state, b) {\n uint.encode(state, b.length)\n\n const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)\n\n if (BE && swap) swap(view)\n\n state.buffer.set(view, state.start)\n state.start += b.byteLength\n },\n decode (state) {\n const len = uint.decode(state)\n\n let b = state.buffer.subarray(state.start, state.start += len * n)\n if (b.byteLength !== len * n) throw new Error('Out of bounds')\n if ((b.byteOffset % n) !== 0) b = new Uint8Array(b)\n\n if (BE && swap) swap(b)\n\n return new TypedArray(b.buffer, b.byteOffset, b.byteLength / n)\n }\n }\n}\n\nconst uint8array = exports.uint8array = typedarray(Uint8Array)\nexports.uint16array = typedarray(Uint16Array, b4a.swap16)\nexports.uint32array = typedarray(Uint32Array, b4a.swap32)\n\nexports.int8array = typedarray(Int8Array)\nexports.int16array = typedarray(Int16Array, b4a.swap16)\nexports.int32array = typedarray(Int32Array, b4a.swap32)\n\nexports.biguint64array = typedarray(BigUint64Array, b4a.swap64)\nexports.bigint64array = typedarray(BigInt64Array, b4a.swap64)\n\nexports.float32array = typedarray(Float32Array, b4a.swap32)\nexports.float64array = typedarray(Float64Array, b4a.swap64)\n\nfunction string (encoding) {\n return {\n preencode (state, s) {\n const len = b4a.byteLength(s, encoding)\n uint.preencode(state, len)\n state.end += len\n },\n encode (state, s) {\n const len = b4a.byteLength(s, encoding)\n uint.encode(state, len)\n b4a.write(state.buffer, s, state.start, encoding)\n state.start += len\n },\n decode (state) {\n const len = uint.decode(state)\n if (state.end - state.start < len) throw new Error('Out of bounds')\n return b4a.toString(state.buffer, encoding, state.start, (state.start += len))\n },\n fixed (n) {\n return {\n preencode (state) {\n state.end += n\n },\n encode (state, s) {\n b4a.write(state.buffer, s, state.start, n, encoding)\n state.start += n\n },\n decode (state) {\n if (state.end - state.start < n) throw new Error('Out of bounds')\n return b4a.toString(state.buffer, encoding, state.start, (state.start += n))\n }\n }\n }\n }\n}\n\nconst utf8 = exports.string = exports.utf8 = string('utf-8')\nexports.ascii = string('ascii')\nexports.hex = string('hex')\nexports.base64 = string('base64')\nexports.ucs2 = exports.utf16le = string('utf16le')\n\nexports.bool = {\n preencode (state, b) {\n state.end++\n },\n encode (state, b) {\n state.buffer[state.start++] = b ? 1 : 0\n },\n decode (state) {\n if (state.start >= state.end) throw Error('Out of bounds')\n return state.buffer[state.start++] === 1\n }\n}\n\nconst fixed = exports.fixed = function fixed (n) {\n return {\n preencode (state, s) {\n if (s.byteLength !== n) throw new Error('Incorrect buffer size')\n state.end += n\n },\n encode (state, s) {\n state.buffer.set(s, state.start)\n state.start += n\n },\n decode (state) {\n if (state.end - state.start < n) throw new Error('Out of bounds')\n return state.buffer.subarray(state.start, (state.start += n))\n }\n }\n}\n\nexports.fixed32 = fixed(32)\nexports.fixed64 = fixed(64)\n\nexports.array = function array (enc) {\n return {\n preencode (state, list) {\n uint.preencode(state, list.length)\n for (let i = 0; i < list.length; i++) enc.preencode(state, list[i])\n },\n encode (state, list) {\n uint.encode(state, list.length)\n for (let i = 0; i < list.length; i++) enc.encode(state, list[i])\n },\n decode (state) {\n const len = uint.decode(state)\n if (len > 0x100000) throw new Error('Array is too big')\n const arr = new Array(len)\n for (let i = 0; i < len; i++) arr[i] = enc.decode(state)\n return arr\n }\n }\n}\n\nexports.frame = function frame (enc) {\n const dummy = exports.state()\n\n return {\n preencode (state, m) {\n const end = state.end\n enc.preencode(state, m)\n uint.preencode(state, state.end - end)\n },\n encode (state, m) {\n dummy.end = 0\n enc.preencode(dummy, m)\n uint.encode(state, dummy.end)\n enc.encode(state, m)\n },\n decode (state) {\n const end = state.end\n const len = uint.decode(state)\n state.end = state.start + len\n const m = enc.decode(state)\n state.start = state.end\n state.end = end\n return m\n }\n }\n}\n\nexports.date = {\n preencode (state, d) {\n int.preencode(state, d.getTime())\n },\n encode (state, d) {\n int.encode(state, d.getTime())\n },\n decode (state, d) {\n return new Date(int.decode(state))\n }\n}\n\nexports.json = {\n preencode (state, v) {\n utf8.preencode(state, JSON.stringify(v))\n },\n encode (state, v) {\n utf8.encode(state, JSON.stringify(v))\n },\n decode (state) {\n return JSON.parse(utf8.decode(state))\n }\n}\n\nexports.ndjson = {\n preencode (state, v) {\n utf8.preencode(state, JSON.stringify(v) + '\\n')\n },\n encode (state, v) {\n utf8.encode(state, JSON.stringify(v) + '\\n')\n },\n decode (state) {\n return JSON.parse(utf8.decode(state))\n }\n}\n\n// simple helper for when you want to just express nothing\nexports.none = {\n preencode (state, n) {\n // do nothing\n },\n encode (state, n) {\n // do nothing\n },\n decode (state) {\n return null\n }\n}\n\n// \"any\" encoders here for helping just structure any object without schematising it\n\nconst anyArray = {\n preencode (state, arr) {\n uint.preencode(state, arr.length)\n for (let i = 0; i < arr.length; i++) {\n any.preencode(state, arr[i])\n }\n },\n encode (state, arr) {\n uint.encode(state, arr.length)\n for (let i = 0; i < arr.length; i++) {\n any.encode(state, arr[i])\n }\n },\n decode (state) {\n const arr = []\n let len = uint.decode(state)\n while (len-- > 0) {\n arr.push(any.decode(state))\n }\n return arr\n }\n}\n\nconst anyObject = {\n preencode (state, o) {\n const keys = Object.keys(o)\n uint.preencode(state, keys.length)\n for (const key of keys) {\n utf8.preencode(state, key)\n any.preencode(state, o[key])\n }\n },\n encode (state, o) {\n const keys = Object.keys(o)\n uint.encode(state, keys.length)\n for (const key of keys) {\n utf8.encode(state, key)\n any.encode(state, o[key])\n }\n },\n decode (state) {\n let len = uint.decode(state)\n const o = {}\n while (len-- > 0) {\n const key = utf8.decode(state)\n o[key] = any.decode(state)\n }\n return o\n }\n}\n\nconst anyTypes = [\n exports.none,\n exports.bool,\n exports.string,\n exports.buffer,\n exports.uint,\n exports.int,\n exports.float64,\n anyArray,\n anyObject,\n exports.date\n]\n\nconst any = exports.any = {\n preencode (state, o) {\n const t = getType(o)\n uint.preencode(state, t)\n anyTypes[t].preencode(state, o)\n },\n encode (state, o) {\n const t = getType(o)\n uint.encode(state, t)\n anyTypes[t].encode(state, o)\n },\n decode (state) {\n const t = uint.decode(state)\n if (t >= anyTypes.length) throw new Error('Unknown type: ' + t)\n return anyTypes[t].decode(state)\n }\n}\n\nfunction getType (o) {\n if (o === null || o === undefined) return 0\n if (typeof o === 'boolean') return 1\n if (typeof o === 'string') return 2\n if (b4a.isBuffer(o)) return 3\n if (typeof o === 'number') {\n if (Number.isInteger(o)) return o >= 0 ? 4 : 5\n return 6\n }\n if (Array.isArray(o)) return 7\n if (o instanceof Date) return 9\n if (typeof o === 'object') return 8\n\n throw new Error('Unsupported type for ' + o)\n}\n\nexports.from = function from (enc) {\n if (typeof enc === 'string') return fromNamed(enc)\n if (enc.preencode) return enc\n if (enc.encodingLength) return fromAbstractEncoder(enc)\n return fromCodec(enc)\n}\n\nfunction fromNamed (enc) {\n switch (enc) {\n case 'ascii': return raw.ascii\n case 'utf-8':\n case 'utf8': return raw.utf8\n case 'hex': return raw.hex\n case 'base64': return raw.base64\n case 'utf16-le':\n case 'utf16le':\n case 'ucs-2':\n case 'ucs2': return raw.ucs2\n case 'ndjson': return raw.ndjson\n case 'json': return raw.json\n case 'binary':\n default: return raw.binary\n }\n}\n\nfunction fromCodec (enc) {\n let tmpM = null\n let tmpBuf = null\n\n return {\n preencode (state, m) {\n tmpM = m\n tmpBuf = enc.encode(m)\n state.end += tmpBuf.byteLength\n },\n encode (state, m) {\n raw.encode(state, m === tmpM ? tmpBuf : enc.encode(m))\n tmpM = tmpBuf = null\n },\n decode (state) {\n return enc.decode(raw.decode(state))\n }\n }\n}\n\nfunction fromAbstractEncoder (enc) {\n return {\n preencode (state, m) {\n state.end += enc.encodingLength(m)\n },\n encode (state, m) {\n enc.encode(m, state.buffer, state.start)\n state.start += enc.encode.bytes\n },\n decode (state) {\n const m = enc.decode(state.buffer, state.start, state.end)\n state.start += enc.decode.bytes\n return m\n }\n }\n}\n\nexports.encode = function encode (enc, m) {\n const state = exports.state()\n enc.preencode(state, m)\n state.buffer = b4a.allocUnsafe(state.end)\n enc.encode(state, m)\n return state.buffer\n}\n\nexports.decode = function decode (enc, buffer) {\n return enc.decode(exports.state(0, buffer.byteLength, buffer))\n}\n\nfunction zigZagInt (enc) {\n return {\n preencode (state, n) {\n enc.preencode(state, zigZagEncodeInt(n))\n },\n encode (state, n) {\n enc.encode(state, zigZagEncodeInt(n))\n },\n decode (state) {\n return zigZagDecodeInt(enc.decode(state))\n }\n }\n}\n\nfunction zigZagDecodeInt (n) {\n return n === 0 ? n : (n & 1) === 0 ? n / 2 : -(n + 1) / 2\n}\n\nfunction zigZagEncodeInt (n) {\n // 0, -1, 1, -2, 2, ...\n return n < 0 ? (2 * -n) - 1 : n === 0 ? 0 : 2 * n\n}\n\nfunction zigZagBigInt (enc) {\n return {\n preencode (state, n) {\n enc.preencode(state, zigZagEncodeBigInt(n))\n },\n encode (state, n) {\n enc.encode(state, zigZagEncodeBigInt(n))\n },\n decode (state) {\n return zigZagDecodeBigInt(enc.decode(state))\n }\n }\n}\n\nfunction zigZagDecodeBigInt (n) {\n return n === 0n ? n : (n & 1n) === 0n ? n / 2n : -(n + 1n) / 2n\n}\n\nfunction zigZagEncodeBigInt (n) {\n // 0, -1, 1, -2, 2, ...\n return n < 0n ? (2n * -n) - 1n : n === 0n ? 0n : 2n * n\n}\n\nfunction validateUint (n) {\n if ((n >= 0) === false /* Handles NaN as well */) throw new Error('uint must be positive')\n}\nmodule.exports = {\n preencode,\n encode,\n decode\n}\n\nfunction preencode (state, num) {\n if (num < 251) {\n state.end++\n } else if (num < 256) {\n state.end += 2\n } else if (num < 0x10000) {\n state.end += 3\n } else if (num < 0x1000000) {\n state.end += 4\n } else if (num < 0x100000000) {\n state.end += 5\n } else {\n state.end++\n const exp = Math.floor(Math.log(num) / Math.log(2)) - 32\n preencode(state, exp)\n state.end += 6\n }\n}\n\nfunction encode (state, num) {\n const max = 251\n const x = num - max\n\n if (num < max) {\n state.buffer[state.start++] = num\n } else if (num < 256) {\n state.buffer[state.start++] = max\n state.buffer[state.start++] = x\n } else if (num < 0x10000) {\n state.buffer[state.start++] = max + 1\n state.buffer[state.start++] = x >> 8 & 0xff\n state.buffer[state.start++] = x & 0xff\n } else if (num < 0x1000000) {\n state.buffer[state.start++] = max + 2\n state.buffer[state.start++] = x >> 16\n state.buffer[state.start++] = x >> 8 & 0xff\n state.buffer[state.start++] = x & 0xff\n } else if (num < 0x100000000) {\n state.buffer[state.start++] = max + 3\n state.buffer[state.start++] = x >> 24\n state.buffer[state.start++] = x >> 16 & 0xff\n state.buffer[state.start++] = x >> 8 & 0xff\n state.buffer[state.start++] = x & 0xff\n } else {\n // need to use Math here as bitwise ops are 32 bit\n const exp = Math.floor(Math.log(x) / Math.log(2)) - 32\n state.buffer[state.start++] = 0xff\n\n encode(state, exp)\n const rem = x / Math.pow(2, exp - 11)\n\n for (let i = 5; i >= 0; i--) {\n state.buffer[state.start++] = rem / Math.pow(2, 8 * i) & 0xff\n }\n }\n}\n\nfunction decode (state) {\n const max = 251\n\n if (state.end - state.start < 1) throw new Error('Out of bounds')\n\n const flag = state.buffer[state.start++]\n\n if (flag < max) return flag\n\n if (state.end - state.start < flag - max + 1) {\n throw new Error('Out of bounds.')\n }\n\n if (flag < 252) {\n return state.buffer[state.start++] +\n max\n }\n\n if (flag < 253) {\n return (state.buffer[state.start++] << 8) +\n state.buffer[state.start++] +\n max\n }\n\n if (flag < 254) {\n return (state.buffer[state.start++] << 16) +\n (state.buffer[state.start++] << 8) +\n state.buffer[state.start++] +\n max\n }\n\n // << 24 result may be interpreted as negative\n if (flag < 255) {\n return (state.buffer[state.start++] * 0x1000000) +\n (state.buffer[state.start++] << 16) +\n (state.buffer[state.start++] << 8) +\n state.buffer[state.start++] +\n max\n }\n\n const exp = decode(state)\n\n if (state.end - state.start < 6) throw new Error('Out of bounds')\n\n let rem = 0\n for (let i = 5; i >= 0; i--) {\n rem += state.buffer[state.start++] * Math.pow(2, 8 * i)\n }\n\n return (rem * Math.pow(2, exp - 11)) + max\n}\n{\n \"name\": \"compact-encoding\",\n \"version\": \"2.16.1\",\n \"description\": \"A series of compact encoding schemes for building small and fast parsers and serializers\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.3.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"standard\": \"^16.0.3\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/compact-encoding/compact-encoding.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/compact-encoding/compact-encoding/issues\"\n },\n \"homepage\": \"https://github.com/compact-encoding/compact-encoding\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst b4a = require('b4a')\n\nconst { BE } = require('./endian')\n\nexports = module.exports = {\n preencode (state, b) {\n state.end += b.byteLength\n },\n encode (state, b) {\n state.buffer.set(b, state.start)\n state.start += b.byteLength\n },\n decode (state) {\n const b = state.buffer.subarray(state.start, state.end)\n state.start = state.end\n return b\n }\n}\n\nconst buffer = exports.buffer = {\n preencode (state, b) {\n if (b) uint8array.preencode(state, b)\n else state.end++\n },\n encode (state, b) {\n if (b) uint8array.encode(state, b)\n else state.buffer[state.start++] = 0\n },\n decode (state) {\n const b = state.buffer.subarray(state.start)\n if (b.byteLength === 0) return null\n state.start = state.end\n return b\n }\n}\n\nexports.binary = {\n ...buffer,\n preencode (state, b) {\n if (typeof b === 'string') utf8.preencode(state, b)\n else buffer.preencode(state, b)\n },\n encode (state, b) {\n if (typeof b === 'string') utf8.encode(state, b)\n else buffer.encode(state, b)\n }\n}\n\nexports.arraybuffer = {\n preencode (state, b) {\n state.end += b.byteLength\n },\n encode (state, b) {\n const view = new Uint8Array(b)\n\n state.buffer.set(view, state.start)\n state.start += b.byteLength\n },\n decode (state) {\n const b = new ArrayBuffer(state.end - state.start)\n const view = new Uint8Array(b)\n\n view.set(state.buffer.subarray(state.start))\n\n state.start = state.end\n\n return b\n }\n}\n\nfunction typedarray (TypedArray, swap) {\n const n = TypedArray.BYTES_PER_ELEMENT\n\n return {\n preencode (state, b) {\n state.end += b.byteLength\n },\n encode (state, b) {\n const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)\n\n if (BE && swap) swap(view)\n\n state.buffer.set(view, state.start)\n state.start += b.byteLength\n },\n decode (state) {\n let b = state.buffer.subarray(state.start)\n if ((b.byteOffset % n) !== 0) b = new Uint8Array(b)\n\n if (BE && swap) swap(b)\n\n state.start = state.end\n\n return new TypedArray(b.buffer, b.byteOffset, b.byteLength / n)\n }\n }\n}\n\nconst uint8array = exports.uint8array = typedarray(Uint8Array)\nexports.uint16array = typedarray(Uint16Array, b4a.swap16)\nexports.uint32array = typedarray(Uint32Array, b4a.swap32)\n\nexports.int8array = typedarray(Int8Array)\nexports.int16array = typedarray(Int16Array, b4a.swap16)\nexports.int32array = typedarray(Int32Array, b4a.swap32)\n\nexports.biguint64array = typedarray(BigUint64Array, b4a.swap64)\nexports.bigint64array = typedarray(BigInt64Array, b4a.swap64)\n\nexports.float32array = typedarray(Float32Array, b4a.swap32)\nexports.float64array = typedarray(Float64Array, b4a.swap64)\n\nfunction string (encoding) {\n return {\n preencode (state, s) {\n state.end += b4a.byteLength(s, encoding)\n },\n encode (state, s) {\n state.start += b4a.write(state.buffer, s, state.start, encoding)\n },\n decode (state) {\n const s = b4a.toString(state.buffer, encoding, state.start)\n state.start = state.end\n return s\n }\n }\n}\n\nconst utf8 = exports.string = exports.utf8 = string('utf-8')\nexports.ascii = string('ascii')\nexports.hex = string('hex')\nexports.base64 = string('base64')\nexports.ucs2 = exports.utf16le = string('utf16le')\n\nexports.array = function array (enc) {\n return {\n preencode (state, list) {\n for (const value of list) enc.preencode(state, value)\n },\n encode (state, list) {\n for (const value of list) enc.encode(state, value)\n },\n decode (state) {\n const arr = []\n while (state.start < state.end) arr.push(enc.decode(state))\n return arr\n }\n }\n}\n\nexports.json = {\n preencode (state, v) {\n utf8.preencode(state, JSON.stringify(v))\n },\n encode (state, v) {\n utf8.encode(state, JSON.stringify(v))\n },\n decode (state) {\n return JSON.parse(utf8.decode(state))\n }\n}\n\nexports.ndjson = {\n preencode (state, v) {\n utf8.preencode(state, JSON.stringify(v) + '\\n')\n },\n encode (state, v) {\n utf8.encode(state, JSON.stringify(v) + '\\n')\n },\n decode (state) {\n return JSON.parse(utf8.decode(state))\n }\n}\nconst { EventEmitter } = require('events')\nconst Table = require('kademlia-routing-table')\nconst TOS = require('time-ordered-set')\nconst UDX = require('udx-native')\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst NatSampler = require('nat-sampler')\nconst b4a = require('b4a')\nconst IO = require('./lib/io')\nconst Query = require('./lib/query')\nconst Session = require('./lib/session')\nconst peer = require('./lib/peer')\nconst { UNKNOWN_COMMAND, INVALID_TOKEN } = require('./lib/errors')\nconst { PING, PING_NAT, FIND_NODE, DOWN_HINT } = require('./lib/commands')\n\nconst TMP = b4a.allocUnsafe(32)\nconst TICK_INTERVAL = 5000\nconst SLEEPING_INTERVAL = 3 * TICK_INTERVAL\nconst STABLE_TICKS = 240 // if nothing major bad happens in ~20mins we can consider this node stable (if nat is friendly)\nconst MORE_STABLE_TICKS = 3 * STABLE_TICKS\nconst REFRESH_TICKS = 60 // refresh every ~5min when idle\nconst RECENT_NODE = 12 // we've heard from a node less than 1min ago\nconst OLD_NODE = 360 // if an node has been around more than 30 min we consider it old\n\nclass DHT extends EventEmitter {\n constructor (opts = {}) {\n super()\n\n this.bootstrapNodes = opts.bootstrap === false ? [] : (opts.bootstrap || []).map(parseNode)\n this.table = new Table(randomBytes(32))\n this.nodes = new TOS()\n this.udx = opts.udx || new UDX()\n this.io = new IO(this.table, this.udx, {\n ...opts,\n onrequest: this._onrequest.bind(this),\n onresponse: this._onresponse.bind(this),\n ontimeout: this._ontimeout.bind(this)\n })\n\n this.concurrency = opts.concurrency || 10\n this.bootstrapped = false\n this.ephemeral = true\n this.firewalled = this.io.firewalled\n this.adaptive = typeof opts.ephemeral !== 'boolean' && opts.adaptive !== false\n this.destroyed = false\n this.suspended = false\n this.online = true\n this.stats = {\n queries: { active: 0, total: 0 },\n commands: {\n ping: this.io.stats.commands[PING],\n pingNat: this.io.stats.commands[PING_NAT],\n findNode: this.io.stats.commands[FIND_NODE],\n downHint: this.io.stats.commands[DOWN_HINT]\n }\n }\n\n this._nat = new NatSampler()\n this._quickFirewall = opts.quickFirewall !== false\n this._forcePersistent = opts.ephemeral === false\n this._repinging = 0\n this._checks = 0\n this._tick = randomOffset(100) // make sure to random offset all the network ticks\n this._refreshTicks = randomOffset(REFRESH_TICKS)\n this._stableTicks = this.adaptive ? STABLE_TICKS : 0\n this._tickInterval = setInterval(this._ontick.bind(this), TICK_INTERVAL)\n this._lastTick = Date.now()\n this._lastHost = null\n this._filterNode = opts.filterNode || opts.addNode || null // opts.addNode is deprecating, use opts.filterNode instead\n this._onrow = (row) => row.on('full', (node) => this._onfullrow(node, row))\n this._nonePersistentSamples = []\n this._bootstrapping = this._bootstrap()\n this._bootstrapping.catch(noop)\n\n this.table.on('row', this._onrow)\n\n this.io.networkInterfaces.on('change', (interfaces) => this._onnetworkchange(interfaces))\n\n if (opts.nodes) {\n for (let i = opts.nodes.length - 1; i >= 0; i--) {\n this.addNode(opts.nodes[i])\n }\n }\n }\n\n static bootstrapper (port, host, opts) {\n if (!port) throw new Error('Port is required')\n if (!host) throw new Error('Host is required')\n if (host === '0.0.0.0' || host === '::') throw new Error('Invalid host')\n if (!UDX.isIPv4(host)) throw new Error('Host must be a IPv4 address')\n\n const dht = new this({ port, ephemeral: false, firewalled: false, anyPort: false, bootstrap: [], ...opts })\n dht._nat.add(host, port)\n return dht\n }\n\n get id () {\n return this.ephemeral ? null : this.table.id\n }\n\n get host () {\n return this._nat.host\n }\n\n get port () {\n return this._nat.port\n }\n\n get randomized () {\n return this._nat.host !== null && (this._nat.port === 0)\n }\n\n get socket () {\n return this.firewalled ? this.io.clientSocket : this.io.serverSocket\n }\n\n onmessage (socket, buf, rinfo) {\n if (buf.byteLength > 1) this.io.onmessage(socket, buf, rinfo)\n }\n\n bind () {\n return this.io.bind()\n }\n\n async suspend ({ log = noop } = {}) {\n log('Suspending waiting for io bind...')\n await this.io.bind()\n log('Done, continuing')\n if (this.suspended || this.destroyed) return\n this.suspended = true\n clearInterval(this._tickInterval)\n log('Done, suspending io')\n await this.io.suspend({ log })\n log('Done, dht suspended')\n this.emit('suspend')\n }\n\n async resume ({ log = noop } = {}) {\n if (!this.suspended || this.destroyed) return\n this.suspended = false\n this._tickInterval = setInterval(this._ontick.bind(this), TICK_INTERVAL)\n this._onwakeup()\n log('Resuming io')\n await this.io.resume()\n log('Done, dht resumed')\n this.io.networkInterfaces.on('change', (interfaces) => this._onnetworkchange(interfaces))\n this.refresh()\n this.emit('resume')\n }\n\n address () {\n const socket = this.socket\n return socket ? socket.address() : null\n }\n\n localAddress () {\n if (!this.io.serverSocket) return null\n\n return {\n host: localIP(this.udx),\n port: this.io.serverSocket.address().port\n }\n }\n\n remoteAddress () {\n if (!this.host) return null\n if (!this.port) return null\n if (this.firewalled) return null\n if (!this.io.serverSocket) return null\n\n const port = this.io.serverSocket.address().port\n if (port !== this.port) return null\n\n return {\n host: this.host,\n port\n }\n }\n\n addNode ({ host, port }) {\n this._addNode({\n id: peer.id(host, port),\n port,\n host,\n token: null,\n to: null,\n sampled: 0,\n added: this._tick,\n pinged: 0,\n seen: 0,\n downHints: 0,\n prev: null,\n next: null\n })\n }\n\n toArray (opts) {\n const limit = (opts && opts.limit)\n if (limit === 0) return []\n return this.nodes.toArray({ limit, reverse: true }).map(({ host, port }) => ({ host, port }))\n }\n\n async fullyBootstrapped () {\n return this._bootstrapping\n }\n\n ready () {\n // Deprecating, use fullyBootstrapped instead (removed on next major)\n return this.fullyBootstrapped()\n }\n\n findNode (target, opts) {\n if (this.destroyed) throw new Error('Node destroyed')\n this._refreshTicks = REFRESH_TICKS\n return new Query(this, target, true, FIND_NODE, null, opts)\n }\n\n query ({ target, command, value }, opts) {\n if (this.destroyed) throw new Error('Node destroyed')\n this._refreshTicks = REFRESH_TICKS\n return new Query(this, target, false, command, value || null, opts)\n }\n\n ping ({ host, port }, opts) {\n let value = null\n\n if (opts && opts.size && opts.size > 0) value = b4a.alloc(opts.size)\n\n const req = this.io.createRequest({ id: null, host, port }, null, true, PING, null, value, (opts && opts.session) || null, (opts && opts.ttl))\n return this._requestToPromise(req, opts)\n }\n\n request ({ token = null, command, target = null, value = null }, { host, port }, opts) {\n const req = this.io.createRequest({ id: null, host, port }, token, false, command, target, value, (opts && opts.session) || null, (opts && opts.ttl))\n return this._requestToPromise(req, opts)\n }\n\n session () {\n return new Session(this)\n }\n\n _requestToPromise (req, opts) {\n if (req === null) return Promise.reject(new Error('Node destroyed'))\n\n if (opts && opts.socket) req.socket = opts.socket\n if (opts && opts.retry === false) req.retries = 0\n\n return new Promise((resolve, reject) => {\n req.onresponse = resolve\n req.onerror = reject\n req.send()\n })\n }\n\n async _bootstrap () {\n const self = this\n\n await Promise.resolve() // wait a tick, so apis can be used from the outside\n await this.io.bind()\n\n this.emit('listening')\n\n // TODO: some papers describe more advanced ways of bootstrapping - we should prob look into that\n\n let first = this.firewalled && this._quickFirewall && !this._forcePersistent\n let testNat = false\n\n const onlyFirewall = !this._forcePersistent\n\n for (let i = 0; i < 2; i++) {\n await this._backgroundQuery(this.table.id).on('data', ondata).finished()\n\n if (this.bootstrapped || (!testNat && !this._forcePersistent)) break\n if (!(await this._updateNetworkState(onlyFirewall))) break\n }\n\n if (this.bootstrapped) return\n this.bootstrapped = true\n\n this.emit('ready')\n\n function ondata (data) {\n // Simple QUICK nat heuristic.\n // If we get ONE positive nat ping before the bootstrap query finishes\n // then we always to a nat test, no matter if we are adaptive...\n // This should be expanded in the future to try more than one node etc, not always hit the first etc\n // If this fails, then nbd, as the onstable hook will pick it up later.\n\n if (!first) return\n first = false\n\n const value = b4a.allocUnsafe(2)\n c.uint16.encode({ start: 0, end: 2, buffer: value }, self.io.serverSocket.address().port)\n\n self._request(data.from, false, true, PING_NAT, null, value, null, () => { testNat = true }, noop)\n }\n }\n\n refresh () {\n const node = this.table.random()\n this._backgroundQuery(node ? node.id : this.table.id).on('error', noop)\n }\n\n async destroy () {\n const emitClose = !this.destroyed\n this.destroyed = true\n clearInterval(this._tickInterval)\n await this.io.destroy()\n if (emitClose) this.emit('close')\n }\n\n _request (to, force, internal, command, target, value, session, onresponse, onerror) {\n const req = this.io.createRequest(to, null, internal, command, target, value, session)\n if (req === null) return null\n\n req.onresponse = onresponse\n req.onerror = onerror\n req.send(force)\n\n return req\n }\n\n _natAdd (host, port) {\n const prevHost = this._nat.host\n const prevPort = this._nat.port\n\n this._nat.add(host, port)\n\n if (prevHost === this._nat.host && prevPort === this._nat.port) return\n\n this.emit('nat-update', this._nat.host, this._nat.port)\n }\n\n // we don't check that this is a bootstrap node but we limit the sample size to very few nodes, so fine\n _sampleBootstrapMaybe (from, to) {\n if (this._nonePersistentSamples.length >= Math.max(1, this.bootstrapNodes.length)) return\n const id = from.host + ':' + from.port\n if (this._nonePersistentSamples.indexOf(id) > -1) return\n this._nonePersistentSamples.push(id)\n this._natAdd(to.host, to.port)\n }\n\n _addNodeFromNetwork (sample, from, to) {\n if (this._filterNode !== null && !this._filterNode(from)) {\n return\n }\n\n if (from.id === null) {\n this._sampleBootstrapMaybe(from, to)\n return\n }\n\n const oldNode = this.table.get(from.id)\n\n // refresh it, if we've seen this before\n if (oldNode) {\n if (sample && (oldNode.sampled === 0 || (this._tick - oldNode.sampled) >= OLD_NODE)) {\n oldNode.to = to\n oldNode.sampled = this._tick\n this._natAdd(to.host, to.port)\n }\n\n oldNode.pinged = oldNode.seen = this._tick\n this.nodes.add(oldNode)\n return\n }\n\n this._addNode({\n id: from.id,\n port: from.port,\n host: from.host,\n to,\n sampled: 0,\n added: this._tick,\n pinged: this._tick, // last time we interacted with them\n seen: this._tick, // last time we heard from them\n downHints: 0,\n prev: null,\n next: null\n })\n }\n\n _addNode (node) {\n if (this.nodes.has(node) || b4a.equals(node.id, this.table.id)) return\n\n node.added = node.pinged = node.seen = this._tick\n\n if (!this.table.add(node)) return\n this.nodes.add(node)\n\n if (node.to && node.sampled === 0) {\n node.sampled = this._tick\n this._natAdd(node.to.host, node.to.port)\n }\n\n this.emit('add-node', node)\n }\n\n _removeStaleNode (node, lastSeen) {\n if (node.seen <= lastSeen) this._removeNode(node)\n }\n\n _removeNode (node) {\n if (!this.nodes.has(node)) return\n\n this.table.remove(node.id)\n this.nodes.remove(node)\n\n this.emit('remove-node', node)\n }\n\n _onwakeup () {\n this._tick += 2 * OLD_NODE // bump the tick enough that everything appears old.\n this._tick += 8 - (this._tick & 7) - 2 // triggers a series of pings in two ticks\n this._stableTicks = MORE_STABLE_TICKS\n this._refreshTicks = 1 // triggers a refresh next tick (allow network time to wake up also)\n this._lastHost = null // clear network cache check\n\n if (this.adaptive) {\n // TODO: re-enable this as soon as we find out why this is over triggering in some edge cases\n // this.firewalled = true\n // this.io.firewalled = true\n\n if (!this.ephemeral) {\n this.ephemeral = true\n this.io.ephemeral = true\n this.emit('ephemeral')\n }\n }\n\n this.emit('wakeup')\n }\n\n _onfullrow (newNode, row) {\n if (!this.bootstrapped || this._repinging >= 3) return\n\n let oldest = null\n for (const node of row.nodes) {\n if (node.pinged === this._tick) continue\n if (oldest === null || oldest.pinged > node.pinged || (oldest.pinged === node.pinged && oldest.added > node.added)) oldest = node\n }\n\n if (oldest === null) return\n if ((this._tick - oldest.pinged) < RECENT_NODE && (this._tick - oldest.added) > OLD_NODE) return\n\n this._repingAndSwap(newNode, oldest)\n }\n\n _onnetworkchange (interfaces) {\n this.emit('network-change', interfaces)\n this.emit('network-update')\n }\n\n _repingAndSwap (newNode, oldNode) {\n const self = this\n const lastSeen = oldNode.seen\n\n oldNode.pinged = this._tick\n\n this._repinging++\n this._request({ id: null, host: oldNode.host, port: oldNode.port }, false, true, PING, null, null, null, onsuccess, onswap)\n\n function onsuccess (m) {\n if (oldNode.seen <= lastSeen) return onswap()\n self._repinging--\n }\n\n function onswap (e) {\n self._repinging--\n self._removeNode(oldNode)\n self._addNode(newNode)\n }\n }\n\n _onrequest (req, external) {\n if (req.from.id !== null) {\n this._addNodeFromNetwork(!external, req.from, req.to)\n }\n\n if (req.internal) {\n switch (req.command) {\n // standard keep alive call\n case PING: {\n req.sendReply(0, null, false, false)\n return\n }\n // check if the other side can receive a message to their other socket\n case PING_NAT: {\n if (req.value === null || req.value.byteLength < 2) return\n const port = c.uint16.decode({ start: 0, end: 2, buffer: req.value })\n if (port === 0) return\n req.from.port = port\n req.sendReply(0, null, false, false)\n return\n }\n // empty dht reply back\n case FIND_NODE: {\n if (!req.target) return\n req.sendReply(0, null, false, true)\n return\n }\n // \"this is node you sent me is down\" - let's try to ping it\n case DOWN_HINT: {\n if (req.value === null || req.value.byteLength < 6) return\n if (this._checks < 10) {\n sodium.crypto_generichash(TMP, req.value.subarray(0, 6))\n const node = this.table.get(TMP)\n if (node && (node.pinged < this._tick || node.downHints === 0)) {\n node.downHints++\n this._check(node)\n }\n }\n req.sendReply(0, null, false, false)\n return\n }\n }\n\n req.sendReply(UNKNOWN_COMMAND, null, false, req.target !== null)\n return\n }\n\n // ask the user to handle it or reply back with a bad command\n if (this.onrequest(req) === false) {\n req.sendReply(UNKNOWN_COMMAND, null, false, req.target !== null)\n }\n }\n\n onrequest (req) {\n return this.emit('request', req)\n }\n\n _onresponse (res, external) {\n this._addNodeFromNetwork(!external, res.from, res.to)\n }\n\n _ontimeout (req) {\n if (!req.to.id) return\n const node = this.table.get(req.to.id)\n if (node) this._removeNode(node)\n }\n\n _pingSome () {\n let cnt = this.io.inflight.length > 2 ? 3 : 5\n let oldest = this.nodes.oldest\n\n // tiny dht, pinged the bootstrap again\n if (!oldest) {\n this.refresh()\n return\n }\n\n // we've recently pinged the oldest one, so only trigger a couple of repings\n if ((this._tick - oldest.pinged) < RECENT_NODE) {\n cnt = 2\n }\n\n while (cnt--) {\n if (!oldest || this._tick === oldest.pinged) continue\n this._check(oldest)\n oldest = oldest.next\n }\n }\n\n _check (node) {\n node.pinged = this._tick\n\n const lastSeen = node.seen\n const onresponse = () => {\n this._checks--\n this._removeStaleNode(node, lastSeen)\n }\n const onerror = () => {\n this._checks--\n this._removeNode(node)\n }\n\n this._checks++\n this._request({ id: null, host: node.host, port: node.port }, false, true, PING, null, null, null, onresponse, onerror)\n }\n\n _ontick () {\n const time = Date.now()\n\n if (time - this._lastTick > SLEEPING_INTERVAL && this.suspended === false) {\n this._onwakeup()\n } else {\n this._tick++\n }\n\n this._lastTick = time\n\n if (!this.bootstrapped || this.suspended) return\n\n if (this.adaptive && this.ephemeral && --this._stableTicks <= 0) {\n if (this._lastHost === this._nat.host) { // do not recheck the same network...\n this._stableTicks = MORE_STABLE_TICKS\n } else {\n this._updateNetworkState() // the promise returned here never fails so just ignore it\n }\n }\n\n if ((this._tick & 7) === 0) {\n this._pingSome()\n }\n\n if (((this._tick & 63) === 0 && this.nodes.length < this.table.k) || --this._refreshTicks <= 0) {\n this.refresh()\n }\n }\n\n async _updateNetworkState (onlyFirewall = false) {\n if (!this.ephemeral) return false\n if (onlyFirewall && !this.firewalled) return false\n\n const { host, port } = this._nat\n\n if (!onlyFirewall) {\n // remember what host we checked and reset the counter\n this._stableTicks = MORE_STABLE_TICKS\n this._lastHost = host\n }\n\n // check if we have a consistent host and port\n if (host === null || port === 0) {\n return false\n }\n\n const natSampler = this.firewalled ? new NatSampler() : this._nat\n\n // ask remote nodes to ping us on our server socket to see if we have the port open\n const firewalled = this.firewalled && await this._checkIfFirewalled(natSampler)\n if (firewalled) return false\n\n this.firewalled = this.io.firewalled = false\n\n // incase it's called in parallel for some reason, or if our nat status somehow changed\n if (!this.ephemeral || host !== this._nat.host || port !== this._nat.port) return false\n // if the firewall probe returned a different host / non consistent port, bail as well\n if (natSampler.host !== host || natSampler.port === 0) return false\n\n const id = peer.id(natSampler.host, natSampler.port)\n\n if (!onlyFirewall) {\n this.ephemeral = this.io.ephemeral = false\n }\n\n if (natSampler !== this._nat) {\n const prevHost = this._nat.host\n const prevPort = this._nat.port\n\n this._nonePersistentSamples = []\n this._nat = natSampler\n\n if (prevHost !== this._nat.host || prevPort !== this._nat.port) {\n this.emit('nat-update', this._nat.host, this._nat.port)\n }\n }\n\n // TODO: we should make this a bit more defensive in terms of using more\n // resources to make sure that the new routing table contains as many alive nodes\n // as possible, vs blindly copying them over...\n\n // all good! copy over the old routing table to the new one\n if (!b4a.equals(this.table.id, id)) {\n const nodes = this.table.toArray()\n\n this.table = this.io.table = new Table(id)\n\n for (const node of nodes) {\n if (b4a.equals(node.id, id)) continue\n if (!this.table.add(node)) this.nodes.remove(node)\n }\n\n this.table.on('row', this._onrow)\n\n // we need to rebootstrap/refresh since we updated our id\n if (this.bootstrapped) this.refresh()\n }\n\n if (!this.ephemeral) {\n this.emit('persistent')\n }\n\n return true\n }\n\n async * _resolveBootstrapNodes () {\n for (let { host, port } of this.bootstrapNodes) {\n let doLookup = false\n\n if (host.indexOf('@') === -1) {\n doLookup = true\n } else {\n const [suggestedIP, fallbackHost] = host.split('@')\n try {\n await this.ping({ host: suggestedIP, port })\n host = suggestedIP\n } catch {\n host = fallbackHost\n doLookup = true\n }\n }\n\n if (doLookup) {\n try {\n host = UDX.isIPv4(host) ? host : (await this.udx.lookup(host, { family: 4 })).host\n } catch {\n continue\n }\n }\n\n yield {\n id: peer.id(host, port),\n host,\n port\n }\n }\n }\n\n async _addBootstrapNodes (nodes) {\n for await (const node of this._resolveBootstrapNodes()) {\n nodes.push(node)\n }\n }\n\n async _checkIfFirewalled (natSampler = new NatSampler()) {\n const nodes = []\n for (let node = this.nodes.latest; node && nodes.length < 5; node = node.prev) {\n nodes.push(node)\n }\n\n if (nodes.length < 5) await this._addBootstrapNodes(nodes)\n // if no nodes are available, including bootstrappers - bail\n if (nodes.length === 0) return true\n\n const hosts = new Set()\n const value = b4a.allocUnsafe(2)\n\n c.uint16.encode({ start: 0, end: 2, buffer: value }, this.io.serverSocket.address().port)\n\n // double check they actually came on the server socket...\n this.io.serverSocket.on('message', onmessage)\n\n const pongs = await requestAll(this, true, PING_NAT, value, nodes)\n\n let count = 0\n for (const res of pongs) {\n if (hosts.has(res.from.host)) {\n count++\n natSampler.add(res.to.host, res.to.port)\n }\n }\n\n this.io.serverSocket.removeListener('message', onmessage)\n\n // if we got no or very few replies, consider it a fluke\n if (count < (nodes.length >= 5 ? 3 : 1)) return true\n\n // check that the server socket has the same ip as the client socket\n if (natSampler.host === null || this._nat.host !== natSampler.host) return true\n\n // check that the local port of the server socket is the same as the remote port\n // TODO: we might want a flag to opt out of this heuristic for specific remapped port servers\n if (natSampler.port === 0 || natSampler.port !== this.io.serverSocket.address().port) return true\n\n return false\n\n function onmessage (_, { host }) {\n hosts.add(host)\n }\n }\n\n _backgroundQuery (target) {\n this._refreshTicks = REFRESH_TICKS\n\n const backgroundCon = Math.min(this.concurrency, Math.max(2, (this.concurrency / 8) | 0))\n const q = new Query(this, target, true, FIND_NODE, null, { concurrency: backgroundCon, maxSlow: 0 })\n\n q.on('data', () => {\n // yield to other traffic\n q.concurrency = this.io.inflight.length < 3\n ? this.concurrency\n : backgroundCon\n })\n\n return q\n }\n\n // called by the query\n _online () {\n if (this.online) return\n this.online = true\n this.emit('network-update')\n }\n\n // called by the query\n _offline () {\n if (!this.online) return\n this.online = false\n this.emit('network-update')\n }\n}\n\nDHT.OK = 0\nDHT.ERROR_UNKNOWN_COMMAND = UNKNOWN_COMMAND\nDHT.ERROR_INVALID_TOKEN = INVALID_TOKEN\n\nmodule.exports = DHT\n\nfunction localIP (udx, family = 4) {\n let host = null\n\n for (const n of udx.networkInterfaces()) {\n if (n.family !== family || n.internal) continue\n\n // mac really likes en0, mb a better way but this shouldnt be bad anywhere so return now\n if (n.name === 'en0') return n.host\n\n // otherwise pick the first non internal host (let the loop continue in case we see en0)\n if (host === null) host = n.host\n }\n\n return host || (family === 4 ? '127.0.0.1' : '::1')\n}\n\nfunction parseNode (s) {\n if (typeof s === 'object') return s\n if (typeof s === 'number') return { host: '127.0.0.1', port: s }\n const [host, port] = s.split(':')\n if (!port) throw new Error('Bootstrap node format is host:port')\n\n return {\n host,\n port: Number(port)\n }\n}\n\nfunction randomBytes (n) {\n const b = b4a.alloc(n)\n sodium.randombytes_buf(b)\n return b\n}\n\nfunction randomOffset (n) {\n return n - ((Math.random() * 0.5 * n) | 0)\n}\n\nfunction requestAll (dht, internal, command, value, nodes) {\n let missing = nodes.length\n const replies = []\n\n return new Promise((resolve) => {\n for (const node of nodes) {\n const req = dht._request(node, false, internal, command, null, value, null, onsuccess, onerror)\n if (!req) return resolve(replies)\n }\n\n function onsuccess (res) {\n replies.push(res)\n if (--missing === 0) resolve(replies)\n }\n\n function onerror () {\n if (--missing === 0) resolve(replies)\n }\n })\n}\n\nfunction noop () {}\nexports.PING = 0\nexports.PING_NAT = 1\nexports.FIND_NODE = 2\nexports.DOWN_HINT = 3\nmodule.exports = class DHTError extends Error {\n constructor (msg, code, fn = DHTError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name () {\n return 'DHTError'\n }\n\n static UNKNOWN_COMMAND = 1\n static INVALID_TOKEN = 2\n\n static REQUEST_TIMEOUT (msg = 'Request timed out') {\n return new DHTError(msg, 'REQUEST_TIMEOUT', DHTError.REQUEST_TIMEOUT)\n }\n\n static REQUEST_DESTROYED (msg = 'Request destroyed') {\n return new DHTError(msg, 'REQUEST_DESTROYED', DHTError.REQUEST_DESTROYED)\n }\n\n static IO_SUSPENDED (msg = 'I/O suspended') {\n return new DHTError(msg, 'IO_SUSPENDED', DHTError.IO_SUSPENDED)\n }\n}\nconst FIFO = require('fast-fifo')\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst peer = require('./peer')\nconst {\n INVALID_TOKEN,\n REQUEST_TIMEOUT,\n REQUEST_DESTROYED,\n IO_SUSPENDED\n} = require('./errors')\n\nconst VERSION = 0b11\nconst RESPONSE_ID = (0b0001 << 4) | VERSION\nconst REQUEST_ID = (0b0000 << 4) | VERSION\nconst EMPTY_ARRAY = []\n\nmodule.exports = class IO {\n constructor (table, udx, { maxWindow = 80, port = 0, host = '0.0.0.0', anyPort = true, firewalled = true, onrequest, onresponse = noop, ontimeout = noop } = {}) {\n this.table = table\n this.udx = udx\n this.inflight = []\n this.clientSocket = null\n this.serverSocket = null\n this.firewalled = firewalled !== false\n this.ephemeral = true\n this.congestion = new CongestionWindow(maxWindow)\n this.networkInterfaces = udx.watchNetworkInterfaces()\n this.suspended = false\n\n this.stats = {\n commands: [\n { tx: 0, rx: 0 }, // tx = transmitted, rx = received\n { tx: 0, rx: 0 },\n { tx: 0, rx: 0 },\n { tx: 0, rx: 0 }\n ]\n }\n\n this.onrequest = onrequest\n this.onresponse = onresponse\n this.ontimeout = ontimeout\n\n this._pending = new FIFO()\n this._rotateSecrets = 10\n this._tid = (Math.random() * 65536) | 0\n this._secrets = null\n this._drainInterval = null\n this._destroying = null\n this._binding = null\n\n // port can be a number or a range [start, to]\n this.portRange = port.length\n ? port\n : port === 0 ? [0, 0] : [port, port + 5]\n\n this._host = host\n this._anyPort = anyPort !== false\n this._boundServerPort = 0\n this._boundClientPort = 0\n }\n\n onmessage (socket, buffer, { host, port }) {\n if (buffer.byteLength < 2 || !(port > 0 && port < 65536) || this.suspended === true) return\n\n const from = { id: null, host, port }\n const state = { start: 1, end: buffer.byteLength, buffer }\n const expectedSocket = this.firewalled ? this.clientSocket : this.serverSocket\n const external = socket !== expectedSocket\n\n if (buffer[0] === REQUEST_ID) {\n const req = Request.decode(this, socket, from, state)\n if (req === null) return\n if (req.token !== null && !b4a.equals(req.token, this.token(req.from, 1)) && !b4a.equals(req.token, this.token(req.from, 0))) {\n req.error(INVALID_TOKEN, { token: true })\n return\n }\n this.onrequest(req, external)\n return\n }\n\n if (buffer[0] === RESPONSE_ID) {\n const res = decodeReply(from, state)\n if (res === null) return\n\n for (let i = 0; i < this.inflight.length; i++) {\n const req = this.inflight[i]\n if (req.tid !== res.tid) continue\n\n res.rtt = Date.now() - req._timestamp\n\n if (i === this.inflight.length - 1) this.inflight.pop()\n else this.inflight[i] = this.inflight.pop()\n\n if (req.session) req.session._detach(req)\n\n // TODO: Auto retry here if errors.INVALID_TOKEN is returned?\n\n if (req._timeout) {\n clearTimeout(req._timeout)\n req._timeout = null\n }\n\n this.congestion.recv()\n\n if (req.internal && req.command < this.stats.commands.length) {\n this.stats.commands[req.command].rx++\n }\n\n this.onresponse(res, external)\n req.onresponse(res, req)\n break\n }\n }\n }\n\n token (addr, i) {\n if (this._secrets === null) {\n const buf = b4a.alloc(64)\n this._secrets = [buf.subarray(0, 32), buf.subarray(32, 64)]\n sodium.randombytes_buf(this._secrets[0])\n sodium.randombytes_buf(this._secrets[1])\n }\n\n const token = b4a.allocUnsafe(32)\n sodium.crypto_generichash(token, b4a.from(addr.host), this._secrets[i])\n return token\n }\n\n async destroy () {\n if (this._destroying) return this._destroying\n this._destroying = this._destroy()\n return this._destroying\n }\n\n async _destroy () {\n // simplifies timing to await the bind here also, although it might be unneeded\n await this.bind()\n await this._clear(false)\n }\n\n async _clear (suspended) {\n if (this._drainInterval) {\n clearInterval(this._drainInterval)\n this._drainInterval = null\n }\n\n while (this.inflight.length) {\n const req = this.inflight.pop()\n if (req._timeout) clearTimeout(req._timeout)\n req._timeout = null\n req.destroyed = true\n\n if (req.session) req.session._detach(req)\n\n this.congestion.recv()\n\n req.onerror(suspended ? IO_SUSPENDED() : REQUEST_DESTROYED(), req)\n }\n\n await Promise.allSettled([\n this.serverSocket.close(),\n this.clientSocket.close()\n ])\n\n this.networkInterfaces.destroy()\n }\n\n async suspend () {\n this.suspended = true\n await this._clear(true)\n\n this.congestion.clear()\n\n if (this._drainInterval) {\n clearInterval(this._drainInterval)\n this._drainInterval = null\n }\n }\n\n async _rebind (binding) {\n if (binding) await binding\n if (this._destroying) return this._destroying\n await this._bindSockets()\n this.networkInterfaces = this.udx.watchNetworkInterfaces()\n }\n\n resume () {\n this.suspended = false\n const binding = this._binding\n this._binding = this._rebind(binding)\n return this._binding\n }\n\n bind () {\n if (this._binding) return this._binding\n this._binding = this._bindSockets()\n return this._binding\n }\n\n async _bindSockets () {\n const serverSocket = this.udx.createSocket()\n\n const candidatePorts = []\n\n // Retrying previous port always has precedence\n if (this._boundServerPort) candidatePorts.push(this._boundServerPort)\n\n for (let i = this.portRange[0]; i < this.portRange[1]; i++) candidatePorts.push(i)\n\n for (const port of candidatePorts) {\n if (serverSocket.bound) break\n\n try {\n serverSocket.bind(port, this._host)\n } catch (err) {\n if (!this._anyPort) {\n await serverSocket.close()\n throw err\n }\n }\n }\n\n if (!serverSocket.bound) {\n try {\n serverSocket.bind(0, this._host)\n } catch (err) {\n await serverSocket.close()\n throw err\n }\n }\n\n const clientSocket = this.udx.createSocket()\n\n try {\n clientSocket.bind(this._boundClientPort || 0, this._host)\n } catch {\n try {\n clientSocket.bind(0, this._host)\n } catch (err) {\n await serverSocket.close()\n await clientSocket.close()\n throw err\n }\n }\n\n this._boundServerPort = serverSocket.address().port\n this._boundClientPort = clientSocket.address().port\n\n this.clientSocket = clientSocket\n this.serverSocket = serverSocket\n\n this.serverSocket.on('message', this.onmessage.bind(this, this.serverSocket))\n this.clientSocket.on('message', this.onmessage.bind(this, this.clientSocket))\n\n if (this._drainInterval === null) {\n this._drainInterval = setInterval(this._drain.bind(this), 750)\n if (this._drainInterval.unref) this._drainInterval.unref()\n }\n\n for (const req of this.inflight) {\n if (!req.socket) req.socket = this.firewalled ? this.clientSocket : this.serverSocket\n req.sent = 0\n req.send(false)\n }\n }\n\n _drain () {\n if (this._secrets !== null && --this._rotateSecrets === 0) {\n this._rotateSecrets = 10\n const tmp = this._secrets[0]\n this._secrets[0] = this._secrets[1]\n this._secrets[1] = tmp\n sodium.crypto_generichash(tmp, tmp)\n }\n\n this.congestion.drain()\n\n while (!this.congestion.isFull()) {\n const p = this._pending.shift()\n if (p === undefined) return\n p._sendNow()\n }\n }\n\n createRequest (to, token, internal, command, target, value, session, ttl) {\n if (this._destroying !== null) return null\n\n if (this._tid === 65536) this._tid = 0\n\n const tid = this._tid++\n const socket = this.firewalled ? this.clientSocket : this.serverSocket\n\n const req = new Request(this, socket, tid, null, to, token, internal, command, target, value, session, ttl || 0)\n this.inflight.push(req)\n if (session) session._attach(req)\n\n if (internal && command < this.stats.commands.length) {\n this.stats.commands[command].tx++\n }\n\n return req\n }\n}\n\nclass Request {\n constructor (io, socket, tid, from, to, token, internal, command, target, value, session, ttl) {\n this.socket = socket\n this.tid = tid\n this.from = from\n this.to = to\n this.token = token\n this.command = command\n this.target = target\n this.value = value\n this.internal = internal\n this.session = session\n this.ttl = ttl\n this.index = -1\n this.sent = 0\n this.retries = 3\n this.destroyed = false\n\n this.oncycle = noop\n this.onerror = noop\n this.onresponse = noop\n\n this._buffer = null\n this._io = io\n this._timeout = null\n this._timestamp = Date.now()\n }\n\n static decode (io, socket, from, state) {\n try {\n const flags = c.uint.decode(state)\n const tid = c.uint16.decode(state)\n const to = peer.ipv4.decode(state)\n const id = flags & 1 ? c.fixed32.decode(state) : null\n const token = flags & 2 ? c.fixed32.decode(state) : null\n const internal = (flags & 4) !== 0\n const command = c.uint.decode(state)\n const target = flags & 8 ? c.fixed32.decode(state) : null\n const value = flags & 16 ? c.buffer.decode(state) : null\n\n if (id !== null) from.id = validateId(id, from)\n\n return new Request(io, socket, tid, from, to, token, internal, command, target, value, null, 0)\n } catch {\n return null\n }\n }\n\n reply (value, opts = {}) {\n const socket = opts.socket || this.socket\n const to = opts.to || this.from\n this._sendReply(0, value || null, opts.token !== false, opts.closerNodes !== false, to, socket)\n }\n\n error (code, opts = {}) {\n const socket = opts.socket || this.socket\n const to = opts.to || this.from\n this._sendReply(code, null, opts.token === true, opts.closerNodes !== false, to, socket)\n }\n\n relay (value, to, opts) {\n const socket = (opts && opts.socket) || this.socket\n const buffer = this._encodeRequest(null, value, to, socket)\n socket.trySend(buffer, to.port, to.host, this.ttl)\n }\n\n send (force = false) {\n if (this.destroyed) return\n\n if (this.socket === null) return\n if (this._buffer === null) this._buffer = this._encodeRequest(this.token, this.value, this.to, this.socket)\n\n if (!force && this._io.congestion.isFull()) {\n this._io._pending.push(this)\n return\n }\n\n this._sendNow()\n }\n\n sendReply (error, value, token, hasCloserNodes) {\n this._sendReply(error, value, token, hasCloserNodes, this.from, this.socket, null)\n }\n\n _sendNow () {\n if (this.destroyed) return\n this.sent++\n this._io.congestion.send()\n this.socket.trySend(this._buffer, this.to.port, this.to.host, this.ttl)\n if (this._timeout) clearTimeout(this._timeout)\n this._timeout = setTimeout(oncycle, 1000, this)\n }\n\n destroy (err) {\n if (this.destroyed) return\n this.destroyed = true\n\n if (this._timeout) {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n const i = this._io.inflight.indexOf(this)\n if (i === -1) return\n\n if (i === this._io.inflight.length - 1) this._io.inflight.pop()\n else this._io.inflight[i] = this._io.inflight.pop()\n\n if (this.session) this.session._detach(this)\n\n this._io.congestion.recv()\n\n this.onerror(err || REQUEST_DESTROYED(), this)\n }\n\n _sendReply (error, value, token, hasCloserNodes, from, socket) {\n if (socket === null || this.destroyed) return\n\n const id = this._io.ephemeral === false && socket === this._io.serverSocket\n const closerNodes = (this.target !== null && hasCloserNodes) ? this._io.table.closest(this.target) : EMPTY_ARRAY\n const state = { start: 0, end: 1 + 1 + 6 + 2, buffer: null } // (type | version) + flags + to + tid\n\n if (id) state.end += 32\n if (token) state.end += 32\n if (closerNodes.length > 0) peer.ipv4Array.preencode(state, closerNodes)\n if (error > 0) c.uint.preencode(state, error)\n if (value) c.buffer.preencode(state, value)\n\n state.buffer = b4a.allocUnsafe(state.end)\n state.buffer[state.start++] = RESPONSE_ID\n state.buffer[state.start++] = (id ? 1 : 0) | (token ? 2 : 0) | (closerNodes.length > 0 ? 4 : 0) | (error > 0 ? 8 : 0) | (value ? 16 : 0)\n\n c.uint16.encode(state, this.tid)\n peer.ipv4.encode(state, from)\n\n if (id) c.fixed32.encode(state, this._io.table.id)\n if (token) c.fixed32.encode(state, this._io.token(from, 1))\n if (closerNodes.length > 0) peer.ipv4Array.encode(state, closerNodes)\n if (error > 0) c.uint.encode(state, error)\n if (value) c.buffer.encode(state, value)\n\n socket.trySend(state.buffer, from.port, from.host, this.ttl)\n }\n\n _encodeRequest (token, value, to, socket) {\n const id = this._io.ephemeral === false && socket === this._io.serverSocket\n const state = { start: 0, end: 1 + 1 + 6 + 2, buffer: null } // (type | version) + flags + to + tid\n\n if (id) state.end += 32\n if (token) state.end += 32\n\n c.uint.preencode(state, this.command)\n\n if (this.target) state.end += 32\n if (value) c.buffer.preencode(state, value)\n\n state.buffer = b4a.allocUnsafe(state.end)\n state.buffer[state.start++] = REQUEST_ID\n state.buffer[state.start++] = (id ? 1 : 0) | (token ? 2 : 0) | (this.internal ? 4 : 0) | (this.target ? 8 : 0) | (value ? 16 : 0)\n\n c.uint16.encode(state, this.tid)\n peer.ipv4.encode(state, to)\n\n if (id) c.fixed32.encode(state, this._io.table.id)\n if (token) c.fixed32.encode(state, token)\n\n c.uint.encode(state, this.command)\n\n if (this.target) c.fixed32.encode(state, this.target)\n if (value) c.buffer.encode(state, value)\n\n return state.buffer\n }\n}\n\nclass CongestionWindow {\n constructor (maxWindow) {\n this._i = 0\n this._total = 0\n this._window = [0, 0, 0, 0]\n this._maxWindow = maxWindow\n }\n\n clear () {\n this._i = 0\n this._total = 0\n this._window = [0, 0, 0, 0]\n }\n\n isFull () {\n return this._total >= 2 * this._maxWindow || this._window[this._i] >= this._maxWindow\n }\n\n recv () {\n if (this._window[this._i] > 0) {\n this._window[this._i]--\n this._total--\n }\n }\n\n send () {\n this._total++\n this._window[this._i]++\n }\n\n drain () {\n this._i = (this._i + 1) & 3\n this._total -= this._window[this._i]\n this._window[this._i] = 0 // clear oldest\n }\n}\n\nfunction noop () {}\n\nfunction oncycle (req) {\n req._timeout = null\n req.oncycle(req)\n if (req.sent >= req.retries) {\n req.destroy(REQUEST_TIMEOUT())\n req._io.ontimeout(req)\n } else {\n req.send()\n }\n}\n\nfunction decodeReply (from, state) {\n try {\n const flags = c.uint.decode(state)\n const tid = c.uint16.decode(state)\n const to = peer.ipv4.decode(state)\n const id = flags & 1 ? c.fixed32.decode(state) : null\n const token = flags & 2 ? c.fixed32.decode(state) : null\n const closerNodes = flags & 4 ? peer.ipv4Array.decode(state) : null\n const error = flags & 8 ? c.uint.decode(state) : 0\n const value = flags & 16 ? c.buffer.decode(state) : null\n\n if (id !== null) from.id = validateId(id, from)\n\n return { tid, rtt: 0, from, to, token, closerNodes, error, value }\n } catch {\n return null\n }\n}\n\nfunction validateId (id, from) {\n const expected = peer.id(from.host, from.port)\n return b4a.equals(expected, id) ? expected : null\n}\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst net = require('compact-encoding-net')\nconst b4a = require('b4a')\n\nconst ipv4 = {\n ...net.ipv4Address,\n decode (state) {\n const ip = net.ipv4Address.decode(state)\n return {\n id: null, // populated by the callee\n host: ip.host,\n port: ip.port\n }\n }\n}\n\nmodule.exports = { id, ipv4, ipv4Array: c.array(ipv4) }\n\nfunction id (host, port, out = b4a.allocUnsafeSlow(32)) {\n const addr = out.subarray(0, 6)\n ipv4.encode(\n { start: 0, end: 6, buffer: addr },\n { host, port }\n )\n sodium.crypto_generichash(out, addr)\n return out\n}\nconst { Readable } = require('streamx')\nconst b4a = require('b4a')\nconst peer = require('./peer')\nconst { DOWN_HINT } = require('./commands')\n\nconst DONE = []\nconst DOWN = []\n\nmodule.exports = class Query extends Readable {\n constructor (dht, target, internal, command, value, opts = {}) {\n super()\n\n dht.stats.queries.total++\n dht.stats.queries.active++\n\n this.force = !!opts.force\n this.dht = dht\n this.k = this.dht.table.k\n this.target = target\n this.internal = internal\n this.command = command\n this.value = value\n this.errors = 0\n this.successes = 0\n this.concurrency = opts.concurrency || this.dht.concurrency\n this.inflight = 0\n this.map = opts.map || defaultMap\n this.maxSlow = opts.maxSlow === 0 ? 0 : (opts.maxSlow || 5)\n this.closestReplies = []\n\n this._slow = 0\n this._online = false\n this._slowdown = false\n this._seen = new Map()\n this._pending = []\n this._fromTable = false\n this._commit = opts.commit === true ? autoCommit : (opts.commit || null)\n this._commiting = false\n this._session = opts.session || dht.session()\n this._autoDestroySession = !opts.session\n this._onlyClosestNodes = false\n\n this._onvisitbound = this._onvisit.bind(this)\n this._onerrorbound = this._onerror.bind(this)\n this._oncyclebound = this._oncycle.bind(this)\n\n const nodes = opts.nodes || opts.closestNodes\n const replies = opts.replies || opts.closestReplies\n\n // add them reverse as we pop below\n if (nodes) {\n for (let i = nodes.length - 1; i >= 0; i--) {\n const node = nodes[i]\n this._addPending({ id: node.id || peer.id(node.host, node.port), host: node.host, port: node.port }, null)\n }\n } else if (replies) {\n for (let i = replies.length - 1; i >= 0; i--) {\n this._addPending(replies[i].from, null)\n }\n }\n\n if (opts.onlyClosestNodes) this._onlyClosestNodes = true\n }\n\n get closestNodes () {\n const nodes = new Array(this.closestReplies.length)\n\n for (let i = 0; i < nodes.length; i++) {\n nodes[i] = this.closestReplies[i].from\n }\n\n return nodes\n }\n\n finished () {\n return new Promise((resolve, reject) => {\n const self = this\n let error = null\n\n this.resume()\n this.on('error', onerror)\n this.on('close', onclose)\n\n function onclose () {\n self.removeListener('error', onerror)\n self.removeListener('close', onclose)\n if (error) reject(error)\n else resolve()\n }\n\n function onerror (err) {\n error = err\n }\n })\n }\n\n _addFromTable () {\n if (this._pending.length >= this.k) return\n this._fromTable = true\n\n const closest = this.dht.table.closest(this.target, this.k - this._pending.length)\n\n for (const node of closest) {\n this._addPending({ id: node.id, host: node.host, port: node.port }, null)\n }\n }\n\n async _open (cb) {\n this._addFromTable()\n if (this._pending.length >= this.k) return cb(null)\n\n for await (const node of this.dht._resolveBootstrapNodes()) {\n this._addPending(node, null)\n }\n\n cb(null)\n }\n\n _isCloser (id) {\n return this.closestReplies.length < this.k || this._compare(id, this.closestReplies[this.closestReplies.length - 1].from.id) < 0\n }\n\n _addPending (node, ref) {\n if (this._onlyClosestNodes) return false\n\n const addr = node.host + ':' + node.port\n const refs = this._seen.get(addr)\n const isCloser = this._isCloser(node.id)\n\n if (refs === DONE) {\n return isCloser\n }\n\n if (refs === DOWN) {\n if (ref) this._downHint(ref, node)\n return isCloser\n }\n\n if (refs) {\n if (ref !== null) refs.push(ref)\n return isCloser\n }\n\n if (!isCloser) {\n return false\n }\n\n this._seen.set(addr, ref === null ? [] : [ref])\n this._pending.push(node)\n\n return true\n }\n\n _read (cb) {\n this._readMore()\n cb(null)\n }\n\n _readMore () {\n if (this.destroying || this._commiting) return\n\n const concurrency = (this._slowdown ? 3 : this.concurrency) + this._slow\n\n while (this.inflight < concurrency && this._pending.length > 0) {\n const next = this._pending.pop()\n if (next && next.id && !this._isCloser(next.id)) continue\n this._visit(next)\n }\n\n // if reusing closest nodes, slow down after the first readMore tick to allow\n // the closest node a chance to reply before going broad to question more\n if (!this._fromTable && this.successes === 0 && this.errors === 0) {\n this._slowdown = true\n }\n\n if (this._pending.length > 0) return\n\n // if no inflight OR all the queries we are waiting on are marked as slow (within our limits) and we have a full result.\n if (this.inflight === 0 || (this._slow <= this.maxSlow && this._slow === this.inflight && this.closestReplies.length >= this.k)) {\n // if more than 3/4 failed and we only used cached nodes, try again from the routing table\n if (!this._fromTable && this.successes < this.k / 4) {\n this._addFromTable()\n this._readMore()\n return\n }\n\n this._flush()\n }\n }\n\n _flush () {\n if (this._commiting) return\n this._commiting = true\n\n if (this._commit === null) {\n this.push(null)\n return\n }\n\n const p = []\n for (const m of this.closestReplies) p.push(this._commit(m, this.dht, this))\n this._endAfterCommit(p)\n }\n\n _endAfterCommit (ps) {\n if (!ps.length) {\n this.destroy(new Error('Too few nodes responded'))\n return\n }\n\n const self = this\n\n let pending = ps.length\n let success = 0\n\n for (const p of ps) p.then(ondone, onerror)\n\n function ondone () {\n success++\n if (--pending === 0) self.push(null)\n }\n\n function onerror (err) {\n if (--pending > 0) return\n if (success) self.push(null)\n else self.destroy(err)\n }\n }\n\n _dec (req) {\n if (req.oncycle === noop) {\n this._slow--\n } else {\n req.oncycle = noop\n }\n this.inflight--\n }\n\n _onvisit (m, req) {\n this._dec(req)\n\n this._online = true\n if (!this.dht.online) this.dht._online()\n\n const addr = req.to.host + ':' + req.to.port\n this._seen.set(addr, DONE)\n\n if (this._commiting) return\n\n if (m.error === 0) this.successes++\n else this.errors++\n\n if (m.error === 0 && m.from.id !== null && this._isCloser(m.from.id)) this._pushClosest(m)\n\n if (m.closerNodes !== null) {\n for (const node of m.closerNodes) {\n node.id = peer.id(node.host, node.port)\n if (this.dht._filterNode !== null && !this.dht._filterNode(node)) continue\n if (b4a.equals(node.id, this.dht.table.id)) continue\n // TODO: we could continue here instead of breaking to ensure that one of the nodes in the closer list\n // is later marked as DOWN that we gossip that back\n if (!this._addPending(node, m.from)) break\n }\n }\n\n if (!this._fromTable && this.successes + this.errors >= this.concurrency) {\n this._slowdown = false\n }\n\n if (m.error !== 0) {\n this._readMore()\n return\n }\n\n const data = this.map(m)\n if (!data || this.push(data) !== false) {\n this._readMore()\n }\n }\n\n _onerror (err, req) {\n const addr = req.to.host + ':' + req.to.port\n const refs = this._seen.get(addr)\n\n if (err.code === 'REQUEST_TIMEOUT') {\n this._seen.set(addr, DOWN)\n for (const node of refs) this._downHint(node, req.to)\n }\n\n this._dec(req)\n this.errors++\n this._readMore()\n }\n\n _oncycle (req) {\n req.oncycle = noop\n this._slow++\n this._readMore()\n }\n\n _downHint (node, down) {\n const state = { start: 0, end: 6, buffer: b4a.allocUnsafe(6) }\n peer.ipv4.encode(state, down)\n this.dht._request(node, false, true, DOWN_HINT, null, state.buffer, this._session, noop, noop)\n }\n\n _pushClosest (m) {\n this.closestReplies.push(m)\n for (let i = this.closestReplies.length - 2; i >= 0; i--) {\n const prev = this.closestReplies[i]\n const cmp = this._compare(prev.from.id, m.from.id)\n // if sorted, done!\n if (cmp < 0) break\n // if dup, splice it out (rare)\n if (cmp === 0) {\n this.closestReplies.splice(i + 1, 1)\n break\n }\n // swap and continue down\n this.closestReplies[i + 1] = prev\n this.closestReplies[i] = m\n }\n if (this.closestReplies.length > this.k) this.closestReplies.pop()\n }\n\n _compare (a, b) {\n for (let i = 0; i < a.length; i++) {\n if (a[i] === b[i]) continue\n const t = this.target[i]\n return (t ^ a[i]) - (t ^ b[i])\n }\n return 0\n }\n\n _visit (to) {\n this.inflight++\n\n const req = this.dht._request(to, this.force, this.internal, this.command, this.target, this.value, this._session, this._onvisitbound, this._onerrorbound)\n if (req === null) {\n this.destroy(new Error('Node was destroyed'))\n return\n }\n req.oncycle = this._oncyclebound\n if (this.force) req.retries = 0\n }\n\n _destroy (cb) {\n this.dht.stats.queries.active--\n if (!this._online && this.dht.online) this.dht._offline()\n if (this._autoDestroySession) this._session.destroy()\n cb(null)\n }\n}\n\nfunction autoCommit (reply, dht, query) {\n if (!reply.token) return Promise.reject(new Error('No token received for closest node'))\n return dht.request({ token: reply.token, target: query.target, command: query.command, value: query.value }, reply.from)\n}\n\nfunction defaultMap (m) {\n return m\n}\n\nfunction noop () {}\nmodule.exports = class Session {\n constructor (dht) {\n this.dht = dht\n this.inflight = []\n }\n\n _attach (req) {\n req.index = this.inflight.push(req) - 1\n }\n\n _detach (req) {\n const i = req.index\n if (i === -1) return\n req.index = -1\n\n if (i === this.inflight.length - 1) this.inflight.pop()\n else {\n const req = this.inflight[i] = this.inflight.pop()\n req.index = i\n }\n }\n\n query ({ target, command, value }, opts = {}) {\n return this.dht.query({ target, command, value }, { ...opts, session: this })\n }\n\n request ({ token, command, target, value }, { host, port }, opts = {}) {\n return this.dht.request({ token, command, target, value }, { host, port }, { ...opts, session: this })\n }\n\n ping ({ host, port }, opts = {}) {\n return this.dht.ping({ host, port }, { ...opts, session: this })\n }\n\n destroy (err) {\n while (this.inflight.length) {\n const req = this.inflight[0]\n // prevent destroyed requests from contributing to congestion counts\n this.dht.io.congestion.recv()\n req.destroy(err)\n }\n }\n}\n{\n \"name\": \"dht-rpc\",\n \"version\": \"6.18.1\",\n \"description\": \"Make RPC calls over a Kademlia based DHT\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/*.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"dependencies\": {\n \"b4a\": \"^1.6.1\",\n \"bare-events\": \"^2.2.0\",\n \"compact-encoding\": \"^2.11.0\",\n \"compact-encoding-net\": \"^1.2.0\",\n \"fast-fifo\": \"^1.1.0\",\n \"kademlia-routing-table\": \"^1.0.1\",\n \"nat-sampler\": \"^1.0.1\",\n \"sodium-universal\": \"^5.0.0\",\n \"streamx\": \"^2.13.2\",\n \"time-ordered-set\": \"^2.0.0\",\n \"udx-native\": \"^1.5.3\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"standard\": \"^17.0.0\",\n \"test-suspend\": \"^1.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\",\n \"test:bare\": \"bare test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/dht-rpc.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/dht-rpc/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/dht-rpc\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = class FixedFIFO {\n constructor (hwm) {\n if (!(hwm > 0) || ((hwm - 1) & hwm) !== 0) throw new Error('Max size for a FixedFIFO should be a power of two')\n this.buffer = new Array(hwm)\n this.mask = hwm - 1\n this.top = 0\n this.btm = 0\n this.next = null\n }\n\n clear () {\n this.top = this.btm = 0\n this.next = null\n this.buffer.fill(undefined)\n }\n\n push (data) {\n if (this.buffer[this.top] !== undefined) return false\n this.buffer[this.top] = data\n this.top = (this.top + 1) & this.mask\n return true\n }\n\n shift () {\n const last = this.buffer[this.btm]\n if (last === undefined) return undefined\n this.buffer[this.btm] = undefined\n this.btm = (this.btm + 1) & this.mask\n return last\n }\n\n peek () {\n return this.buffer[this.btm]\n }\n\n isEmpty () {\n return this.buffer[this.btm] === undefined\n }\n}\nconst FixedFIFO = require('./fixed-size')\n\nmodule.exports = class FastFIFO {\n constructor (hwm) {\n this.hwm = hwm || 16\n this.head = new FixedFIFO(this.hwm)\n this.tail = this.head\n this.length = 0\n }\n\n clear () {\n this.head = this.tail\n this.head.clear()\n this.length = 0\n }\n\n push (val) {\n this.length++\n if (!this.head.push(val)) {\n const prev = this.head\n this.head = prev.next = new FixedFIFO(2 * this.head.buffer.length)\n this.head.push(val)\n }\n }\n\n shift () {\n if (this.length !== 0) this.length--\n const val = this.tail.shift()\n if (val === undefined && this.tail.next) {\n const next = this.tail.next\n this.tail.next = null\n this.tail = next\n return this.tail.shift()\n }\n\n return val\n }\n\n peek () {\n const val = this.tail.peek()\n if (val === undefined && this.tail.next) return this.tail.next.peek()\n return val\n }\n\n isEmpty () {\n return this.length === 0\n }\n}\n{\n \"name\": \"fast-fifo\",\n \"version\": \"1.3.2\",\n \"description\": \"A fast fifo implementation similar to the one powering nextTick in Node.js core\",\n \"main\": \"index.js\",\n \"files\": [\n \"./index.js\",\n \"./fixed-size.js\"\n ],\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^17.1.0\",\n \"brittle\": \"^3.3.2\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/fast-fifo.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/fast-fifo/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/fast-fifo\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\n\n// https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack\nconst LEAF_TYPE = b4a.from([0])\nconst PARENT_TYPE = b4a.from([1])\nconst ROOT_TYPE = b4a.from([2])\n\nconst HYPERCORE = b4a.from('hypercore')\n\nexports.keyPair = function (seed) {\n // key pairs might stay around for a while, so better not to use a default slab to avoid retaining it completely\n const slab = b4a.allocUnsafeSlow(sodium.crypto_sign_PUBLICKEYBYTES + sodium.crypto_sign_SECRETKEYBYTES)\n const publicKey = slab.subarray(0, sodium.crypto_sign_PUBLICKEYBYTES)\n const secretKey = slab.subarray(sodium.crypto_sign_PUBLICKEYBYTES)\n\n if (seed) sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed)\n else sodium.crypto_sign_keypair(publicKey, secretKey)\n\n return {\n publicKey,\n secretKey\n }\n}\n\nexports.validateKeyPair = function (keyPair) {\n const pk = b4a.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES)\n sodium.crypto_sign_ed25519_sk_to_pk(pk, keyPair.secretKey)\n return b4a.equals(pk, keyPair.publicKey)\n}\n\nexports.sign = function (message, secretKey) {\n // Dedicated slab for the signature, to avoid retaining unneeded mem and for security\n const signature = b4a.allocUnsafeSlow(sodium.crypto_sign_BYTES)\n sodium.crypto_sign_detached(signature, message, secretKey)\n return signature\n}\n\nexports.verify = function (message, signature, publicKey) {\n if (signature.byteLength !== sodium.crypto_sign_BYTES) return false\n if (publicKey.byteLength !== sodium.crypto_sign_PUBLICKEYBYTES) return false\n return sodium.crypto_sign_verify_detached(signature, message, publicKey)\n}\n\nexports.encrypt = function (message, publicKey) {\n const ciphertext = b4a.alloc(message.byteLength + sodium.crypto_box_SEALBYTES)\n sodium.crypto_box_seal(ciphertext, message, publicKey)\n return ciphertext\n}\n\nexports.decrypt = function (ciphertext, keyPair) {\n if (ciphertext.byteLength < sodium.crypto_box_SEALBYTES) return null\n\n const plaintext = b4a.alloc(ciphertext.byteLength - sodium.crypto_box_SEALBYTES)\n\n if (!sodium.crypto_box_seal_open(plaintext, ciphertext, keyPair.publicKey, keyPair.secretKey)) {\n return null\n }\n\n return plaintext\n}\n\nexports.encryptionKeyPair = function (seed) {\n const publicKey = b4a.alloc(sodium.crypto_box_PUBLICKEYBYTES)\n const secretKey = b4a.alloc(sodium.crypto_box_SECRETKEYBYTES)\n\n if (seed) {\n sodium.crypto_box_seed_keypair(publicKey, secretKey, seed)\n } else {\n sodium.crypto_box_keypair(publicKey, secretKey)\n }\n\n return {\n publicKey,\n secretKey\n }\n}\n\nexports.data = function (data) {\n const out = b4a.allocUnsafe(32)\n\n sodium.crypto_generichash_batch(out, [\n LEAF_TYPE,\n c.encode(c.uint64, data.byteLength),\n data\n ])\n\n return out\n}\n\nexports.parent = function (a, b) {\n if (a.index > b.index) {\n const tmp = a\n a = b\n b = tmp\n }\n\n const out = b4a.allocUnsafe(32)\n\n sodium.crypto_generichash_batch(out, [\n PARENT_TYPE,\n c.encode(c.uint64, a.size + b.size),\n a.hash,\n b.hash\n ])\n\n return out\n}\n\nexports.tree = function (roots, out) {\n const buffers = new Array(3 * roots.length + 1)\n let j = 0\n\n buffers[j++] = ROOT_TYPE\n\n for (let i = 0; i < roots.length; i++) {\n const r = roots[i]\n buffers[j++] = r.hash\n buffers[j++] = c.encode(c.uint64, r.index)\n buffers[j++] = c.encode(c.uint64, r.size)\n }\n\n if (!out) out = b4a.allocUnsafe(32)\n sodium.crypto_generichash_batch(out, buffers)\n return out\n}\n\nexports.hash = function (data, out) {\n if (!out) out = b4a.allocUnsafe(32)\n if (!Array.isArray(data)) data = [data]\n\n sodium.crypto_generichash_batch(out, data)\n\n return out\n}\n\nexports.randomBytes = function (n) {\n const buf = b4a.allocUnsafe(n)\n sodium.randombytes_buf(buf)\n return buf\n}\n\nexports.discoveryKey = function (key) {\n if (!key || key.byteLength !== 32) throw new Error('Must pass a 32 byte buffer')\n // Discovery keys might stay around for a while, so better not to use slab memory (for better gc)\n const digest = b4a.allocUnsafeSlow(32)\n sodium.crypto_generichash(digest, HYPERCORE, key)\n return digest\n}\n\nif (sodium.sodium_free) {\n exports.free = function (secureBuf) {\n if (secureBuf.secure) sodium.sodium_free(secureBuf)\n }\n} else {\n exports.free = function () {}\n}\n\nexports.namespace = function (name, count) {\n const ids = typeof count === 'number' ? range(count) : count\n\n // Namespaces are long-lived, so better to use a dedicated slab\n const buf = b4a.allocUnsafeSlow(32 * ids.length)\n\n const list = new Array(ids.length)\n\n // ns is emhemeral, so default slab\n const ns = b4a.allocUnsafe(33)\n sodium.crypto_generichash(ns.subarray(0, 32), typeof name === 'string' ? b4a.from(name) : name)\n\n for (let i = 0; i < list.length; i++) {\n list[i] = buf.subarray(32 * i, 32 * i + 32)\n ns[32] = ids[i]\n sodium.crypto_generichash(list[i], ns)\n }\n\n return list\n}\n\nfunction range (count) {\n const arr = new Array(count)\n for (let i = 0; i < count; i++) arr[i] = i\n return arr\n}\n{\n \"name\": \"hypercore-crypto\",\n \"version\": \"3.6.1\",\n \"description\": \"The crypto primitives used in hypercore, extracted into a separate module\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.6.6\",\n \"compact-encoding\": \"^2.15.0\",\n \"sodium-universal\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.5.0\",\n \"standard\": \"^17.1.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/hypercore-crypto.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/hypercore-crypto/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/hypercore-crypto\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst z32 = require('z32')\nconst b4a = require('b4a')\n\nmodule.exports = {\n encode,\n decode,\n normalize,\n isValid\n}\n\nfunction encode (key) {\n if (!b4a.isBuffer(key)) throw new Error('Key must be a Buffer')\n if (key.byteLength !== 32) throw new Error('Key must be 32-bytes long')\n return z32.encode(key)\n}\n\nfunction decode (id) {\n if (b4a.isBuffer(id)) {\n if (id.byteLength !== 32) throw new Error('ID must be 32-bytes long')\n return id\n }\n if (typeof id === 'string') {\n if (id.startsWith('pear://')) id = id.slice(7).split('/')[0]\n if (id.length === 52) return z32.decode(id)\n if (id.length === 64) {\n const buf = b4a.from(id, 'hex')\n if (buf.byteLength === 32) return buf\n }\n }\n throw new Error('Invalid Hypercore key')\n}\n\nfunction normalize (any) {\n return encode(decode(any))\n}\n\nfunction isValid (any) {\n try {\n decode(any)\n return true\n } catch {\n return false\n }\n}\n{\n \"name\": \"hypercore-id-encoding\",\n \"version\": \"1.3.0\",\n \"description\": \"Convert Hypercore keys to/from z-base32 or hex\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.5.3\",\n \"z32\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.1\",\n \"hypercore\": \"^10.0.0\",\n \"random-access-memory\": \"^6.0.0\",\n \"standard\": \"^17.1.0\"\n },\n \"license\": \"Apache-2.0\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hypercore-id-encoding.git\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hypercore-id-encoding/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hypercore-id-encoding#readme\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst DHT = require('dht-rpc')\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst safetyCatch = require('safety-catch')\nconst m = require('./lib/messages')\nconst SocketPool = require('./lib/socket-pool')\nconst Persistent = require('./lib/persistent')\nconst Router = require('./lib/router')\nconst Server = require('./lib/server')\nconst connect = require('./lib/connect')\nconst { FIREWALL, BOOTSTRAP_NODES, KNOWN_NODES, COMMANDS } = require('./lib/constants')\nconst { hash, createKeyPair } = require('./lib/crypto')\nconst { decode } = require('hypercore-id-encoding')\nconst RawStreamSet = require('./lib/raw-stream-set')\nconst ConnectionPool = require('./lib/connection-pool')\nconst { STREAM_NOT_CONNECTED } = require('./lib/errors')\n\nclass HyperDHT extends DHT {\n constructor (opts = {}) {\n const port = opts.port || 49737\n const bootstrap = opts.bootstrap || BOOTSTRAP_NODES\n const nodes = opts.nodes || KNOWN_NODES\n\n super({ ...opts, port, bootstrap, nodes, filterNode })\n\n const { router, persistent } = defaultCacheOpts(opts)\n\n this.defaultKeyPair = opts.keyPair || createKeyPair(opts.seed)\n this.listening = new Set()\n this.connectionKeepAlive = opts.connectionKeepAlive === false\n ? 0\n : opts.connectionKeepAlive || 5000\n\n // stats is inherited from dht-rpc so fwd the ones from there\n this.stats = { punches: { consistent: 0, random: 0, open: 0 }, ...this.stats }\n\n this._router = new Router(this, router)\n this._socketPool = new SocketPool(this, opts.host || '0.0.0.0')\n this._rawStreams = new RawStreamSet(this)\n this._persistent = null\n this._validatedLocalAddresses = new Map()\n\n this._lastRandomPunch = 0\n this._connectable = true\n this._randomPunchInterval = opts.randomPunchInterval || 20000 // min 20s between random punches...\n this._randomPunches = 0\n this._randomPunchLimit = 1 // set to one for extra safety for now\n\n this.once('persistent', () => {\n this._persistent = new Persistent(this, persistent)\n })\n\n this.on('network-change', () => {\n for (const server of this.listening) server.refresh()\n })\n\n this.on('network-update', () => {\n if (!this.online) return\n for (const server of this.listening) server.notifyOnline()\n })\n }\n\n connect (remotePublicKey, opts) {\n return connect(this, decode(remotePublicKey), opts)\n }\n\n createServer (opts, onconnection) {\n if (typeof opts === 'function') return this.createServer({}, opts)\n if (opts && opts.onconnection) onconnection = opts.onconnection\n const s = new Server(this, opts)\n if (onconnection) s.on('connection', onconnection)\n return s\n }\n\n pool () {\n return new ConnectionPool(this)\n }\n\n async resume ({ log = noop } = {}) {\n await super.resume({ log })\n const resuming = []\n for (const server of this.listening) resuming.push(server.resume())\n log('Resuming hyperdht servers')\n await Promise.allSettled(resuming)\n log('Done, hyperdht fully resumed')\n }\n\n async suspend ({ log = noop } = {}) {\n this._connectable = false // just so nothing gets connected during suspension\n const suspending = []\n for (const server of this.listening) suspending.push(server.suspend())\n log('Suspending all hyperdht servers')\n await Promise.allSettled(suspending)\n log('Done, clearing all raw streams')\n await this._rawStreams.clear()\n log('Done, suspending dht-rpc')\n await super.suspend({ log })\n log('Done, clearing raw streams again')\n await this._rawStreams.clear()\n log('Done, hyperdht fully suspended')\n this._connectable = true\n }\n\n async destroy ({ force = false } = {}) {\n if (!force) {\n const closing = []\n for (const server of this.listening) closing.push(server.close())\n await Promise.allSettled(closing)\n }\n this._router.destroy()\n if (this._persistent) this._persistent.destroy()\n await this._rawStreams.clear()\n await this._socketPool.destroy()\n await super.destroy()\n }\n\n async validateLocalAddresses (addresses) {\n const list = []\n const socks = []\n const waiting = []\n\n for (const addr of addresses) {\n const { host } = addr\n\n if (this._validatedLocalAddresses.has(host)) {\n if (await this._validatedLocalAddresses.get(host)) {\n list.push(addr)\n }\n continue\n }\n\n const sock = this.udx.createSocket()\n try {\n sock.bind(0, host)\n } catch {\n this._validatedLocalAddresses.set(host, Promise.resolve(false))\n continue\n }\n\n socks.push(sock)\n\n // semi terrible heuristic until we proper fix local connections by racing them to the remote...\n const promise = new Promise(resolve => {\n sock.on('message', () => resolve(true))\n setTimeout(() => resolve(false), 500)\n sock.trySend(b4a.alloc(1), sock.address().port, addr.host)\n })\n\n this._validatedLocalAddresses.set(host, promise)\n waiting.push(addr)\n }\n\n for (const addr of waiting) {\n const { host } = addr\n if (this._validatedLocalAddresses.has(host)) {\n if (await this._validatedLocalAddresses.get(host)) {\n list.push(addr)\n }\n continue\n }\n }\n\n for (const sock of socks) await sock.close()\n\n return list\n }\n\n findPeer (publicKey, opts = {}) {\n const target = opts.hash === false ? publicKey : hash(publicKey)\n opts = { ...opts, map: mapFindPeer }\n return this.query({ target, command: COMMANDS.FIND_PEER, value: null }, opts)\n }\n\n lookup (target, opts = {}) {\n opts = { ...opts, map: mapLookup }\n return this.query({ target, command: COMMANDS.LOOKUP, value: null }, opts)\n }\n\n lookupAndUnannounce (target, keyPair, opts = {}) {\n const unannounces = []\n const dht = this\n const userCommit = opts.commit || noop\n const signUnannounce = opts.signUnannounce || Persistent.signUnannounce\n\n if (this._persistent !== null) { // unlink self\n this._persistent.unannounce(target, keyPair.publicKey)\n }\n\n opts = { ...opts, map, commit }\n return this.query({ target, command: COMMANDS.LOOKUP, value: null }, opts)\n\n async function commit (reply, dht, query) {\n await Promise.all(unannounces) // can never fail, caught below\n return userCommit(reply, dht, query)\n }\n\n function map (reply) {\n const data = mapLookup(reply)\n\n if (!data || !data.token) return data\n\n let found = data.peers.length >= 20\n for (let i = 0; !found && i < data.peers.length; i++) {\n found = b4a.equals(data.peers[i].publicKey, keyPair.publicKey)\n }\n\n if (!found) return data\n\n if (!data.from.id) return data\n\n unannounces.push(\n dht._requestUnannounce(\n keyPair,\n dht,\n target,\n data.token,\n data.from,\n signUnannounce\n ).catch(safetyCatch)\n )\n\n return data\n }\n }\n\n unannounce (target, keyPair, opts = {}) {\n return this.lookupAndUnannounce(target, keyPair, opts).finished()\n }\n\n announce (target, keyPair, relayAddresses, opts = {}) {\n const signAnnounce = opts.signAnnounce || Persistent.signAnnounce\n\n opts = { ...opts, commit }\n\n return opts.clear\n ? this.lookupAndUnannounce(target, keyPair, opts)\n : this.lookup(target, opts)\n\n function commit (reply, dht) {\n return dht._requestAnnounce(\n keyPair,\n dht,\n target,\n reply.token,\n reply.from,\n relayAddresses,\n signAnnounce\n )\n }\n }\n\n async immutableGet (target, opts = {}) {\n opts = { ...opts, map: mapImmutable }\n\n const query = this.query({ target, command: COMMANDS.IMMUTABLE_GET, value: null }, opts)\n const check = b4a.allocUnsafe(32)\n\n for await (const node of query) {\n const { value } = node\n sodium.crypto_generichash(check, value)\n if (b4a.equals(check, target)) return node\n }\n\n return null\n }\n\n async immutablePut (value, opts = {}) {\n const target = b4a.allocUnsafe(32)\n sodium.crypto_generichash(target, value)\n\n opts = {\n ...opts,\n map: mapImmutable,\n commit (reply, dht) {\n return dht.request({ token: reply.token, target, command: COMMANDS.IMMUTABLE_PUT, value }, reply.from)\n }\n }\n\n const query = this.query({ target, command: COMMANDS.IMMUTABLE_GET, value: null }, opts)\n await query.finished()\n\n return { hash: target, closestNodes: query.closestNodes }\n }\n\n async mutableGet (publicKey, opts = {}) {\n let refresh = opts.refresh || null\n let signed = null\n let result = null\n\n opts = { ...opts, map: mapMutable, commit: refresh ? commit : null }\n\n const target = b4a.allocUnsafe(32)\n sodium.crypto_generichash(target, publicKey)\n\n const userSeq = opts.seq || 0\n const query = this.query({ target, command: COMMANDS.MUTABLE_GET, value: c.encode(c.uint, userSeq) }, opts)\n const latest = opts.latest !== false\n\n for await (const node of query) {\n if (result && node.seq <= result.seq) continue\n if (node.seq < userSeq || !Persistent.verifyMutable(node.signature, node.seq, node.value, publicKey)) continue\n if (!latest) return node\n if (!result || node.seq > result.seq) result = node\n }\n\n return result\n\n function commit (reply, dht) {\n if (!signed && result && refresh) {\n if (refresh(result)) {\n signed = c.encode(m.mutablePutRequest, {\n publicKey,\n seq: result.seq,\n value: result.value,\n signature: result.signature\n })\n } else {\n refresh = null\n }\n }\n\n return signed ? dht.request({ token: reply.token, target, command: COMMANDS.MUTABLE_PUT, value: signed }, reply.from) : Promise.resolve(null)\n }\n }\n\n async mutablePut (keyPair, value, opts = {}) {\n const signMutable = opts.signMutable || Persistent.signMutable\n\n const target = b4a.allocUnsafe(32)\n sodium.crypto_generichash(target, keyPair.publicKey)\n\n const seq = opts.seq || 0\n const signature = await signMutable(seq, value, keyPair)\n\n const signed = c.encode(m.mutablePutRequest, {\n publicKey: keyPair.publicKey,\n seq,\n value,\n signature\n })\n\n opts = {\n ...opts,\n map: mapMutable,\n commit (reply, dht) {\n return dht.request({ token: reply.token, target, command: COMMANDS.MUTABLE_PUT, value: signed }, reply.from)\n }\n }\n\n // use seq = 0, for the query part here, as we don't care about the actual values\n const query = this.query({ target, command: COMMANDS.MUTABLE_GET, value: c.encode(c.uint, 0) }, opts)\n await query.finished()\n\n return { publicKey: keyPair.publicKey, closestNodes: query.closestNodes, seq, signature }\n }\n\n onrequest (req) {\n switch (req.command) {\n case COMMANDS.PEER_HANDSHAKE: {\n this._router.onpeerhandshake(req)\n return true\n }\n case COMMANDS.PEER_HOLEPUNCH: {\n this._router.onpeerholepunch(req)\n return true\n }\n }\n\n if (this._persistent === null) return false\n\n switch (req.command) {\n case COMMANDS.FIND_PEER: {\n this._persistent.onfindpeer(req)\n return true\n }\n case COMMANDS.LOOKUP: {\n this._persistent.onlookup(req)\n return true\n }\n case COMMANDS.ANNOUNCE: {\n this._persistent.onannounce(req)\n return true\n }\n case COMMANDS.UNANNOUNCE: {\n this._persistent.onunannounce(req)\n return true\n }\n case COMMANDS.MUTABLE_PUT: {\n this._persistent.onmutableput(req)\n return true\n }\n case COMMANDS.MUTABLE_GET: {\n this._persistent.onmutableget(req)\n return true\n }\n case COMMANDS.IMMUTABLE_PUT: {\n this._persistent.onimmutableput(req)\n return true\n }\n case COMMANDS.IMMUTABLE_GET: {\n this._persistent.onimmutableget(req)\n return true\n }\n }\n\n return false\n }\n\n static keyPair (seed) {\n return createKeyPair(seed)\n }\n\n static hash (data) {\n return hash(data)\n }\n\n static connectRawStream (encryptedStream, rawStream, remoteId) {\n const stream = encryptedStream.rawStream\n\n if (!stream.connected) throw STREAM_NOT_CONNECTED()\n\n rawStream.connect(\n stream.socket,\n remoteId,\n stream.remotePort,\n stream.remoteHost\n )\n }\n\n createRawStream (opts) {\n return this._rawStreams.add(opts)\n }\n\n async _requestAnnounce (keyPair, dht, target, token, from, relayAddresses, sign) {\n const ann = {\n peer: {\n publicKey: keyPair.publicKey,\n relayAddresses: relayAddresses || []\n },\n refresh: null,\n signature: null\n }\n\n ann.signature = await sign(target, token, from.id, ann, keyPair)\n\n const value = c.encode(m.announce, ann)\n\n return dht.request({\n token,\n target,\n command: COMMANDS.ANNOUNCE,\n value\n }, from)\n }\n\n async _requestUnannounce (keyPair, dht, target, token, from, sign) {\n const unann = {\n peer: {\n publicKey: keyPair.publicKey,\n relayAddresses: []\n },\n signature: null\n }\n\n unann.signature = await sign(target, token, from.id, unann, keyPair)\n\n const value = c.encode(m.announce, unann)\n\n return dht.request({\n token,\n target,\n command: COMMANDS.UNANNOUNCE,\n value\n }, from)\n }\n}\n\nHyperDHT.BOOTSTRAP = BOOTSTRAP_NODES\nHyperDHT.FIREWALL = FIREWALL\n\nmodule.exports = HyperDHT\n\nfunction mapLookup (node) {\n if (!node.value) return null\n\n try {\n return {\n token: node.token,\n from: node.from,\n to: node.to,\n peers: c.decode(m.peers, node.value)\n }\n } catch {\n return null\n }\n}\n\nfunction mapFindPeer (node) {\n if (!node.value) return null\n\n try {\n return {\n token: node.token,\n from: node.from,\n to: node.to,\n peer: c.decode(m.peer, node.value)\n }\n } catch {\n return null\n }\n}\n\nfunction mapImmutable (node) {\n if (!node.value) return null\n\n return {\n token: node.token,\n from: node.from,\n to: node.to,\n value: node.value\n }\n}\n\nfunction mapMutable (node) {\n if (!node.value) return null\n\n try {\n const { seq, value, signature } = c.decode(m.mutableGetResponse, node.value)\n\n return {\n token: node.token,\n from: node.from,\n to: node.to,\n seq,\n value,\n signature\n }\n } catch {\n return null\n }\n}\n\nfunction noop () {}\n\nfunction filterNode (node) {\n // always skip these testnet nodes that got mixed in by accident, until they get updated\n return !(node.port === 49738 && (node.host === '134.209.28.98' || node.host === '167.99.142.185')) &&\n !(node.port === 9400 && node.host === '35.233.47.252') && !(node.host === '150.136.142.116')\n}\n\nconst defaultMaxSize = 65536\nconst defaultMaxAge = 20 * 60 * 1000 // 20 minutes\n\nfunction defaultCacheOpts (opts) {\n const maxSize = opts.maxSize || defaultMaxSize\n const maxAge = opts.maxAge || defaultMaxAge\n\n return {\n router: {\n forwards: { maxSize, maxAge }\n },\n persistent: {\n records: { maxSize, maxAge },\n refreshes: { maxSize, maxAge },\n mutables: {\n maxSize: maxSize / 2 | 0,\n maxAge: opts.maxAge || 48 * 60 * 60 * 1000 // 48 hours\n },\n immutables: {\n maxSize: maxSize / 2 | 0,\n maxAge: opts.maxAge || 48 * 60 * 60 * 1000 // 48 hours\n }\n }\n }\n}\nconst safetyCatch = require('safety-catch')\nconst c = require('compact-encoding')\nconst Signal = require('signal-promise')\nconst { encodeUnslab } = require('./encode')\nconst Sleeper = require('./sleeper')\nconst m = require('./messages')\nconst Persistent = require('./persistent')\nconst { COMMANDS } = require('./constants')\n\nconst MIN_ACTIVE = 3\n\nmodule.exports = class Announcer {\n constructor (dht, keyPair, target, opts = {}) {\n this.dht = dht\n this.keyPair = keyPair\n this.target = target\n this.relays = []\n this.relayAddresses = []\n this.stopped = false\n this.suspended = false\n this.record = encodeUnslab(m.peer, { publicKey: keyPair.publicKey, relayAddresses: [] })\n this.online = new Signal()\n\n this._refreshing = false\n this._closestNodes = null\n this._active = null\n this._sleeper = new Sleeper()\n this._resumed = new Signal()\n this._signAnnounce = opts.signAnnounce || Persistent.signAnnounce\n this._signUnannounce = opts.signUnannounce || Persistent.signUnannounce\n this._updating = null\n this._activeQuery = null\n this._unannouncing = null\n\n this._serverRelays = [\n new Map(),\n new Map(),\n new Map()\n ]\n }\n\n isRelay (addr) {\n const id = addr.host + ':' + addr.port\n const [a, b, c] = this._serverRelays\n return a.has(id) || b.has(id) || c.has(id)\n }\n\n async suspend ({ log = noop } = {}) {\n if (this.suspended) return\n this.suspended = true\n\n log('Suspending announcer')\n\n // Suspend has its own sleep logic\n // so we don't want to hang on this one\n this.online.notify()\n\n if (this._activeQuery) this._activeQuery.destroy()\n\n this._sleeper.resume()\n if (this._updating) await this._updating\n log('Suspending announcer (post update)')\n\n if (this.suspended === false || this.stopped) return\n\n log('Suspending announcer (pre unannounce)')\n await this._unannounceCurrent()\n log('Suspending announcer (post unannounce)')\n }\n\n resume () {\n if (!this.suspended) return\n this.suspended = false\n\n this.refresh()\n this._sleeper.resume()\n this._resumed.notify()\n }\n\n refresh () {\n if (this.stopped) return\n this._refreshing = true\n }\n\n async start () {\n if (this.stopped) return\n this._active = this._runUpdate()\n await this._active\n if (this.stopped) return\n this._active = this._background()\n }\n\n async stop () {\n this.stopped = true\n this.online.notify() // Break out of the _background loop if we're offline\n this._sleeper.resume()\n this._resumed.notify()\n await this._active\n await this._unannounceCurrent()\n }\n\n async _unannounceCurrent () {\n while (this._unannouncing !== null) await this._unannouncing\n const un = this._unannouncing = this._unannounceAll(this._serverRelays[2].values())\n await this._unannouncing\n if (un === this._unannouncing) this._unannouncing = null\n }\n\n async _background () {\n while (!this.dht.destroyed && !this.stopped) {\n try {\n this._refreshing = false\n\n // ~5min +-\n for (let i = 0; i < 100 && !this.stopped && !this._refreshing && !this.suspended; i++) {\n const pings = []\n\n for (const node of this._serverRelays[2].values()) {\n pings.push(this.dht.ping(node))\n }\n\n const active = await resolved(pings)\n if (active < Math.min(pings.length, MIN_ACTIVE)) {\n this.refresh() // we lost too many relay nodes, retry all\n }\n\n if (this.stopped) return\n\n if (!this.suspended && !this._refreshing) await this._sleeper.pause(3000)\n }\n\n while (!this.stopped && this.suspended) await this._resumed.wait()\n\n if (!this.stopped) await this._runUpdate()\n\n while (!this.dht.online && !this.stopped && !this.suspended) {\n // Being offline can make _background repeat very quickly\n // So wait until we're back online\n await this.online.wait()\n }\n } catch (err) {\n safetyCatch(err)\n }\n }\n }\n\n async _runUpdate () {\n this._updating = this._update()\n await this._updating\n this._updating = null\n }\n\n async _update () {\n while (this._unannouncing) await this._unannouncing\n\n this._cycle()\n\n const q = this._activeQuery = this.dht.findPeer(this.target, { hash: false, nodes: this._closestNodes })\n\n try {\n await q.finished()\n } catch {\n // ignore failures...\n }\n\n this._activeQuery = null\n\n if (this.stopped || this.suspended) return\n\n const ann = []\n const replies = pickBest(q.closestReplies)\n\n const relays = []\n const relayAddresses = []\n\n if (!this.dht.firewalled) {\n const addr = this.dht.remoteAddress()\n if (addr) relayAddresses.push(addr)\n }\n\n for (const msg of replies) {\n ann.push(this._commit(msg, relays, relayAddresses))\n }\n\n await Promise.allSettled(ann)\n if (this.stopped || this.suspended) return\n\n this._closestNodes = q.closestNodes\n this.relays = relays\n this.relayAddresses = relayAddresses\n\n const removed = []\n for (const [key, value] of this._serverRelays[1]) {\n if (!this._serverRelays[2].has(key)) removed.push(value)\n }\n\n await this._unannounceAll(removed)\n }\n\n _unannounceAll (relays) {\n const unann = []\n for (const r of relays) unann.push(this._unannounce(r))\n return Promise.allSettled(unann)\n }\n\n async _unannounce (to) {\n const unann = {\n peer: {\n publicKey: this.keyPair.publicKey,\n relayAddresses: []\n },\n refresh: null,\n signature: null\n }\n\n const { from, token, value } = await this.dht.request({\n token: null,\n command: COMMANDS.FIND_PEER,\n target: this.target,\n value: null\n }, to)\n\n if (!token || !from.id || !value) return\n\n unann.signature = await this._signUnannounce(this.target, token, from.id, unann, this.keyPair)\n\n await this.dht.request({\n token,\n command: COMMANDS.UNANNOUNCE,\n target: this.target,\n value: c.encode(m.announce, unann)\n }, to)\n }\n\n async _commit (msg, relays, relayAddresses) {\n const ann = {\n peer: {\n publicKey: this.keyPair.publicKey,\n relayAddresses: []\n },\n refresh: null,\n signature: null\n }\n\n ann.signature = await this._signAnnounce(this.target, msg.token, msg.from.id, ann, this.keyPair)\n\n const res = await this.dht.request({\n token: msg.token,\n command: COMMANDS.ANNOUNCE,\n target: this.target,\n value: c.encode(m.announce, ann)\n }, msg.from)\n\n if (res.error !== 0) return\n\n if (relayAddresses.length < 3) relayAddresses.push({ host: msg.from.host, port: msg.from.port })\n relays.push({ relayAddress: msg.from, peerAddress: msg.to })\n\n this._serverRelays[2].set(msg.from.host + ':' + msg.from.port, msg.from)\n }\n\n _cycle () {\n const tmp = this._serverRelays[0]\n this._serverRelays[0] = this._serverRelays[1]\n this._serverRelays[1] = this._serverRelays[2]\n this._serverRelays[2] = tmp\n tmp.clear()\n }\n}\n\nfunction resolved (ps) {\n let replied = 0\n let ticks = ps.length + 1\n\n return new Promise((resolve) => {\n for (const p of ps) p.then(push, tick)\n tick()\n\n function push (v) {\n replied++\n tick()\n }\n\n function tick () {\n if (--ticks === 0) resolve(replied)\n }\n })\n}\n\nfunction pickBest (replies) { // TODO: pick the ones closest to us RTT wise\n return replies.slice(0, 3)\n}\n\nfunction noop () {}\nconst NoiseSecretStream = require('@hyperswarm/secret-stream')\nconst b4a = require('b4a')\nconst relay = require('blind-relay')\nconst { isPrivate, isBogon } = require('bogon')\nconst safetyCatch = require('safety-catch')\nconst unslab = require('unslab')\nconst Semaphore = require('./semaphore')\nconst NoiseWrap = require('./noise-wrap')\nconst SecurePayload = require('./secure-payload')\nconst Holepuncher = require('./holepuncher')\nconst Sleeper = require('./sleeper')\nconst { FIREWALL, ERROR } = require('./constants')\nconst { unslabbedHash } = require('./crypto')\nconst {\n CANNOT_HOLEPUNCH,\n HANDSHAKE_INVALID,\n HOLEPUNCH_ABORTED,\n HOLEPUNCH_INVALID,\n HOLEPUNCH_PROBE_TIMEOUT,\n HOLEPUNCH_DOUBLE_RANDOMIZED_NATS,\n PEER_CONNECTION_FAILED,\n PEER_NOT_FOUND,\n REMOTE_ABORTED,\n REMOTE_NOT_HOLEPUNCHABLE,\n REMOTE_NOT_HOLEPUNCHING,\n SERVER_ERROR,\n SERVER_INCOMPATIBLE,\n RELAY_ABORTED,\n SUSPENDED\n} = require('./errors')\n\nmodule.exports = function connect (dht, publicKey, opts = {}) {\n const pool = opts.pool || null\n\n if (pool && pool.has(publicKey)) return pool.get(publicKey)\n\n publicKey = unslab(publicKey)\n\n const keyPair = opts.keyPair || dht.defaultKeyPair\n const relayThrough = selectRelay(opts.relayThrough || null)\n const encryptedSocket = (opts.createSecretStream || defaultCreateSecretStream)(true, null, {\n publicKey: keyPair.publicKey,\n remotePublicKey: publicKey,\n autoStart: false,\n keepAlive: dht.connectionKeepAlive\n })\n\n // in case a socket is made during suspended state, destroy it immediately\n if (dht.suspended || !dht._connectable) {\n encryptedSocket.destroy(SUSPENDED())\n return encryptedSocket\n }\n\n if (pool) pool._attachStream(encryptedSocket, false)\n\n const c = {\n dht,\n session: dht.session(),\n relayAddresses: opts.relayAddresses || [],\n pool,\n round: 0,\n target: unslabbedHash(publicKey),\n remotePublicKey: publicKey,\n reusableSocket: !!opts.reusableSocket,\n handshake: (opts.createHandshake || defaultCreateHandshake)(keyPair, publicKey),\n request: null,\n requesting: false,\n lan: opts.localConnection !== false,\n firewall: FIREWALL.UNKNOWN,\n rawStream: dht.createRawStream({ framed: true, firewall }),\n connect: null,\n query: null,\n puncher: null,\n payload: null,\n passiveConnectTimeout: null,\n serverSocket: null,\n serverAddress: null,\n onsocket: null,\n sleeper: new Sleeper(),\n encryptedSocket,\n\n // Relay state\n relayTimeout: null,\n relayThrough,\n relayToken: relayThrough ? relay.token() : null,\n relaySocket: null,\n relayClient: null,\n relayPaired: false,\n relayKeepAlive: opts.relayKeepAlive || 5000\n }\n\n // If the raw stream receives an error signal pre connect (ie from the firewall hook), make sure\n // to forward that to the encrypted socket for proper teardown\n c.rawStream.on('error', autoDestroy)\n c.rawStream.once('connect', () => {\n c.rawStream.removeListener('error', autoDestroy)\n })\n\n encryptedSocket.on('close', function () {\n if (c.passiveConnectTimeout) clearPassiveConnectTimeout(c)\n if (c.query) c.query.destroy()\n if (c.puncher) c.puncher.destroy()\n if (c.rawStream) c.rawStream.destroy()\n c.session.destroy()\n c.sleeper.resume()\n })\n\n // Safe to run in the background - never throws\n if (dht.suspended) encryptedSocket.destroy(SUSPENDED())\n else connectAndHolepunch(c, opts)\n\n return encryptedSocket\n\n function autoDestroy (err) {\n maybeDestroyEncryptedSocket(c, err)\n }\n\n function firewall (socket, port, host) {\n // Check if the traffic originated from the socket on which we're expecting relay traffic. If so,\n // we haven't hole punched yet and the other side is just sending us traffic through the relay.\n if (c.relaySocket && isRelay(c.relaySocket, socket, port, host)) {\n return false\n }\n\n if (c.onsocket) {\n c.onsocket(socket, port, host)\n } else {\n c.serverSocket = socket\n c.serverAddress = { port, host }\n }\n return false\n }\n}\n\nfunction isDone (c) {\n // we are destroying or the puncher is connected - done\n if (c.encryptedSocket.destroying || !!(c.puncher && c.puncher.connected)) {\n return true\n }\n // not destroying, but no raw stream - def not done\n if (c.encryptedSocket.rawStream === null) {\n return false\n }\n // we are relayed, but the puncher is not done yet\n if (c.relaySocket && !!(c.puncher && !c.puncher.connected && !c.puncher.destroyed)) {\n return false\n }\n // we are done\n return true\n}\n\nasync function retryRoute (c, route) {\n const ref = c.dht._socketPool.lookup(route.socket)\n\n if (!ref) {\n if (route.socket === c.dht.socket) {\n await connectThroughNode(c, route.address, c.dht.socket)\n }\n return\n }\n\n ref.active()\n\n try {\n await connectThroughNode(c, route.address, route.socket)\n } catch {\n // if error, just ignore, and continue through the existing strat\n }\n\n ref.inactive()\n}\n\nasync function connectAndHolepunch (c, opts) {\n const route = c.reusableSocket ? c.dht._socketPool.routes.get(c.remotePublicKey) : null\n\n if (route) {\n await retryRoute(c, route)\n if (isDone(c)) return\n }\n\n await findAndConnect(c, opts)\n if (isDone(c)) return\n\n if (!c.connect) { // TODO: just a quick fix for now, should retry prob\n maybeDestroyEncryptedSocket(c, HANDSHAKE_INVALID())\n return\n }\n\n await holepunch(c, opts)\n}\n\nfunction getFirstRemoteAddress (addrs, serverAddress) {\n for (const addr of addrs) {\n if (isBogon(addr.host)) continue\n return addr\n }\n\n return serverAddress\n}\n\nasync function holepunch (c, opts) {\n let { relayAddress, serverAddress, clientAddress, payload } = c.connect\n\n const remoteHolepunchable = !!(payload.holepunch && payload.holepunch.relays.length)\n\n const relayed = diffAddress(serverAddress, relayAddress)\n\n if (payload.firewall === FIREWALL.OPEN || (relayed && !remoteHolepunchable)) {\n const addr = getFirstRemoteAddress(payload.addresses4, serverAddress)\n if (addr) {\n const socket = c.dht.socket\n c.dht.stats.punches.open++\n c.onsocket(socket, addr.port, addr.host)\n return\n }\n // TODO: check all addresses also obvs\n }\n\n const onabort = () => {\n c.session.destroy()\n maybeDestroyEncryptedSocket(c, HOLEPUNCH_ABORTED())\n }\n\n if (c.firewall === FIREWALL.OPEN) {\n c.passiveConnectTimeout = setTimeout(onabort, 10000)\n return\n }\n\n // TODO: would be better to just try local addrs in the background whilst continuing with other strategies...\n if (c.lan && relayed && clientAddress.host === serverAddress.host) {\n const serverAddresses = payload.addresses4.filter(onlyPrivateHosts)\n\n if (serverAddresses.length > 0) {\n const myAddresses = Holepuncher.localAddresses(c.dht.io.serverSocket)\n const addr = Holepuncher.matchAddress(myAddresses, serverAddresses) || serverAddresses[0]\n\n const socket = c.dht.io.serverSocket\n try {\n await c.dht.ping(addr)\n } catch {\n maybeDestroyEncryptedSocket(c, HOLEPUNCH_ABORTED())\n return\n }\n c.onsocket(socket, addr.port, addr.host)\n return\n }\n }\n\n if (!remoteHolepunchable) {\n maybeDestroyEncryptedSocket(c, CANNOT_HOLEPUNCH())\n return\n }\n\n c.puncher = new Holepuncher(c.dht, c.session, true, payload.firewall)\n\n c.puncher.onconnect = c.onsocket\n c.puncher.onabort = onabort\n\n const serverRelay = pickServerRelay(payload.holepunch.relays, relayAddress)\n\n // Begin holepunching!\n\n let probe\n try {\n probe = await probeRound(c, opts.fastOpen === false ? null : serverAddress, serverRelay, true)\n } catch (err) {\n destroyPuncher(c)\n // TODO: we should retry here with some of the other relays, bail for now\n maybeDestroyEncryptedSocket(c, err)\n return\n }\n\n if (isDone(c) || !probe) return\n const { token, peerAddress } = probe\n\n // If the relay the server picked is the same as the relay the client picked,\n // then we can use the peerAddress that round one indicates the server wants to use.\n // This shaves off a roundtrip if the server chose to reroll its socket due to some NAT\n // issue with the first one it picked (ie mobile nat inconsistencies...).\n // If the relays were different, then the server would not have a UDP session open on this address\n // to the client relay, which round2 uses.\n if (!diffAddress(serverRelay.relayAddress, relayAddress) && diffAddress(serverAddress, peerAddress)) {\n serverAddress = peerAddress\n await c.puncher.openSession(serverAddress)\n if (isDone(c)) return\n }\n\n // TODO: still continue here if a local connection might work, but then do not holepunch...\n if (opts.holepunch && !opts.holepunch(c.puncher.remoteFirewall, c.puncher.nat.firewall, c.puncher.remoteAddresses, c.puncher.nat.addresses)) {\n await abort(c, serverRelay, HOLEPUNCH_ABORTED('Client aborted holepunch'))\n return\n }\n\n try {\n await roundPunch(c, serverAddress, token, relayAddress, serverRelay, false)\n } catch (err) {\n destroyPuncher(c)\n // TODO: retry with another relay?\n maybeDestroyEncryptedSocket(c, err)\n }\n}\n\nasync function findAndConnect (c, opts) {\n let attempts = 0\n let closestNodes = (opts.relayAddresses && opts.relayAddresses.length) ? opts.relayAddresses : null\n\n if (c.dht._persistent) { // check if we know the route ourself...\n const route = c.dht._router.get(c.target)\n if (route && route.relay !== null) closestNodes = [{ host: route.relay.host, port: route.relay.port }]\n }\n\n // 2 is how many parallel connect attempts we want to do, we can make this configurable\n const sem = new Semaphore(2)\n const signal = sem.signal.bind(sem)\n const tries = closestNodes !== null ? 2 : 1\n\n try {\n for (let i = 0; i < tries && !isDone(c) && !c.connect; i++) {\n c.query = c.dht.findPeer(c.target, { hash: false, session: c.session, closestNodes, onlyClosestNodes: closestNodes !== null })\n\n for await (const data of c.query) {\n await sem.wait()\n if (isDone(c)) return\n\n if (c.connect) {\n sem.signal()\n break\n }\n\n attempts++\n connectThroughNode(c, data.from, null).then(signal, signal)\n }\n\n closestNodes = null\n }\n\n c.query = null\n if (isDone(c)) return\n\n // flush the semaphore\n await sem.flush()\n if (isDone(c)) return\n } catch (err) {\n c.query = null\n maybeDestroyEncryptedSocket(c, err)\n return\n }\n\n if (!c.connect) {\n maybeDestroyEncryptedSocket(c, attempts ? PEER_CONNECTION_FAILED() : PEER_NOT_FOUND())\n }\n}\n\nasync function connectThroughNode (c, address, socket) {\n if (!c.requesting) {\n // If we have a stable server address, send it over now\n const addr = c.dht.remoteAddress()\n const localAddrs = c.lan ? Holepuncher.localAddresses(c.dht.io.serverSocket) : null\n const addresses4 = []\n\n if (addr) addresses4.push(addr)\n if (localAddrs) addresses4.push(...localAddrs)\n\n c.firewall = addr ? FIREWALL.OPEN : FIREWALL.UNKNOWN\n c.requesting = true\n c.request = await c.handshake.send({\n error: ERROR.NONE,\n firewall: c.firewall,\n holepunch: null,\n addresses4,\n addresses6: [],\n udx: {\n reusableSocket: c.reusableSocket,\n id: c.rawStream.id,\n seq: 0\n },\n secretStream: {},\n relayThrough: c.relayThrough\n ? { publicKey: c.relayThrough, token: c.relayToken }\n : null\n })\n if (isDone(c)) return\n }\n\n const { serverAddress, clientAddress, relayed, noise } = await c.dht._router.peerHandshake(c.target, { noise: c.request, socket, session: c.session }, address)\n if (isDone(c) || c.connect) return\n\n const payload = await c.handshake.recv(noise)\n if (isDone(c) || !payload) return\n\n if (payload.version !== 1) {\n maybeDestroyEncryptedSocket(c, SERVER_INCOMPATIBLE())\n return\n }\n if (payload.error !== ERROR.NONE) {\n maybeDestroyEncryptedSocket(c, SERVER_ERROR())\n return\n }\n if (!payload.udx) {\n maybeDestroyEncryptedSocket(c, SERVER_ERROR('Server did not send UDX data'))\n return\n }\n\n const hs = c.handshake.final()\n\n c.handshake = null\n c.request = null\n c.requesting = false\n c.connect = {\n relayed,\n relayAddress: address,\n clientAddress,\n serverAddress,\n payload\n }\n\n c.payload = new SecurePayload(hs.holepunchSecret)\n\n c.onsocket = function (socket, port, host) {\n if (c.rawStream === null) return // Already hole punched\n\n if (c.rawStream.connected) {\n const remoteChanging = c.rawStream.changeRemote(socket, c.connect.payload.udx.id, port, host)\n\n if (remoteChanging) remoteChanging.catch(safetyCatch)\n } else {\n c.rawStream.connect(socket, c.connect.payload.udx.id, port, host)\n c.encryptedSocket.start(c.rawStream, { handshake: hs })\n }\n\n if (c.reusableSocket && payload.udx.reusableSocket) {\n c.dht._socketPool.routes.add(c.remotePublicKey, c.rawStream)\n }\n\n if (c.puncher) {\n c.puncher.onabort = noop\n c.puncher.destroy()\n }\n\n if (c.passiveConnectTimeout) {\n clearPassiveConnectTimeout(c)\n }\n\n c.rawStream = null\n }\n\n if (payload.relayThrough || c.relayThrough) {\n relayConnection(c, c.relayThrough, payload, hs)\n }\n\n if (c.serverSocket) {\n c.onsocket(c.serverSocket, c.serverAddress.port, c.serverAddress.host)\n return\n }\n\n if (!relayed) {\n c.onsocket(socket || c.dht.socket, address.port, address.host)\n }\n\n c.session.destroy()\n}\n\nasync function updateHolepunch (c, peerAddress, relayAddr, payload) {\n const holepunch = await c.dht._router.peerHolepunch(c.target, {\n id: c.connect.payload.holepunch.id,\n payload: c.payload.encrypt(payload),\n peerAddress,\n socket: c.puncher.socket,\n session: c.session\n }, relayAddr)\n\n if (isDone(c)) return null\n\n const remotePayload = c.payload.decrypt(holepunch.payload)\n if (!remotePayload) {\n throw HOLEPUNCH_INVALID()\n }\n\n const { error, firewall, punching, addresses, remoteToken } = remotePayload\n\n if (error === ERROR.TRY_LATER && c.relayToken && payload.punching) {\n return {\n tryLater: true,\n ...holepunch,\n payload: remotePayload\n }\n }\n\n if (error !== ERROR.NONE) {\n throw REMOTE_ABORTED('Remote aborted with error code ' + error)\n }\n\n const echoed = !!(remoteToken && payload.token && b4a.equals(remoteToken, payload.token))\n\n c.puncher.updateRemote({ punching, firewall, addresses, verified: echoed ? peerAddress.host : null })\n\n return {\n tryLater: false,\n ...holepunch,\n payload: remotePayload\n }\n}\n\nasync function probeRound (c, serverAddress, serverRelay, retry) {\n // Open a quick low ttl session against what we think is the server\n if (serverAddress) await c.puncher.openSession(serverAddress)\n\n if (isDone(c)) return null\n\n const reply = await updateHolepunch(c, serverRelay.peerAddress, serverRelay.relayAddress, {\n error: ERROR.NONE,\n firewall: c.puncher.nat.firewall,\n round: c.round++,\n connected: false,\n punching: false,\n addresses: c.puncher.nat.addresses,\n remoteAddress: serverAddress,\n token: null,\n remoteToken: null\n })\n\n if (isDone(c) || !reply) return null\n\n const { peerAddress } = reply\n const { address, token } = reply.payload\n\n c.puncher.nat.add(reply.to, reply.from)\n\n // Open another quick low ttl session against what the server says their address is,\n // if they haven't said they are random yet\n if (c.puncher.remoteFirewall < FIREWALL.RANDOM && address && address.host && address.port && diffAddress(address, serverAddress)) {\n await c.puncher.openSession(address)\n if (isDone(c)) return null\n }\n\n // If the remote told us they didn't know their nat firewall yet, give them a chance to figure it out\n // They might say this to see if the \"fast mode\" punch comes through first.\n if (c.puncher.remoteFirewall === FIREWALL.UNKNOWN) {\n await c.sleeper.pause(1000)\n if (isDone(c)) return null\n }\n\n let stable = await c.puncher.analyze(false)\n if (isDone(c)) return null\n\n // If the socket seems unstable, try to make it stable by setting the \"allowReopen\" flag\n // Mostly relevant for mobile networks\n if (!stable) {\n stable = await c.puncher.analyze(true)\n if (isDone(c)) return null\n if (stable) return probeRound(c, serverAddress, serverRelay, false)\n }\n\n if ((c.puncher.remoteFirewall === FIREWALL.UNKNOWN || !token) && retry) {\n return probeRound(c, serverAddress, serverRelay, false)\n }\n\n if (c.puncher.remoteFirewall === FIREWALL.UNKNOWN || c.puncher.nat.firewall === FIREWALL.UNKNOWN) {\n await abort(c, serverRelay, HOLEPUNCH_PROBE_TIMEOUT())\n return null\n }\n\n if (c.puncher.remoteFirewall >= FIREWALL.RANDOM && c.puncher.nat.firewall >= FIREWALL.RANDOM) {\n await abort(c, serverRelay, HOLEPUNCH_DOUBLE_RANDOMIZED_NATS())\n return null\n }\n\n return { token, peerAddress }\n}\n\nasync function roundPunch (c, serverAddress, remoteToken, clientRelay, serverRelay, delayed) {\n // We are gossiping our final NAT status to the other peer now\n // so make sure we don't update our local view for now as that can make things weird\n c.puncher.nat.freeze()\n\n const isRandom = c.puncher.remoteFirewall >= FIREWALL.RANDOM || c.puncher.nat.firewall >= FIREWALL.RANDOM\n if (isRandom) {\n while (c.dht._randomPunches >= c.dht._randomPunchLimit || (Date.now() - c.dht._lastRandomPunch) < c.dht._randomPunchInterval) {\n // if no relay can help, bail\n if (!c.relayToken) throw HOLEPUNCH_ABORTED()\n\n if (!delayed) {\n delayed = true\n await updateHolepunch(c, serverAddress, clientRelay, {\n error: ERROR.NONE,\n firewall: c.puncher.nat.firewall,\n round: c.round++,\n connected: false,\n punching: false,\n addresses: c.puncher.nat.addresses,\n remoteAddress: null,\n token: c.payload.token(serverAddress),\n remoteToken\n })\n if (isDone(c)) return\n }\n\n await tryLater(c)\n if (isDone(c)) return\n }\n }\n\n // increment now, so we can commit to punching\n if (isRandom) c.dht._randomPunches++\n\n let reply\n\n try {\n // if delayed switch to the servers chosen relay - we validated anyway\n reply = await updateHolepunch(c, delayed ? serverRelay.peerAddress : serverAddress, delayed ? serverRelay.relayAddress : clientRelay, {\n error: ERROR.NONE,\n firewall: c.puncher.nat.firewall,\n round: c.round++,\n connected: false,\n punching: true,\n addresses: c.puncher.nat.addresses,\n remoteAddress: null,\n token: delayed ? null : c.payload.token(serverAddress),\n remoteToken\n })\n } finally {\n // decrement as punch increments for us\n if (isRandom) c.dht._randomPunches--\n }\n\n if (isDone(c)) return\n if (!reply) return\n\n if (reply.tryLater) {\n await tryLater(c)\n if (isDone(c)) return\n return roundPunch(c, serverAddress, remoteToken, clientRelay, serverRelay, true)\n }\n\n if (!c.puncher.remoteHolepunching) {\n throw REMOTE_NOT_HOLEPUNCHING()\n }\n\n if (!await c.puncher.punch()) {\n throw REMOTE_NOT_HOLEPUNCHABLE()\n }\n}\n\nasync function tryLater (c) {\n if (!c.relayToken) throw HOLEPUNCH_ABORTED()\n await c.sleeper.pause(10000 + Math.round(Math.random() * 10000))\n}\n\nfunction maybeDestroyEncryptedSocket (c, err) {\n if (isDone(c)) return\n if (c.encryptedSocket.rawStream) return\n if (c.relaySocket) return // waiting for the relay\n if (c.puncher && !c.puncher.destroyed) return // waiting for the puncher\n c.session.destroy()\n c.encryptedSocket.destroy(err)\n}\n\nasync function abort (c, { peerAddress, relayAddress }, err) {\n try {\n await updateHolepunch(peerAddress, relayAddress, {\n error: ERROR.ABORTED,\n firewall: FIREWALL.UNKNOWN,\n round: c.round++,\n connected: false,\n punching: false,\n addresses: null,\n remoteAddress: null,\n token: null,\n remoteToken: null\n })\n } catch {}\n\n destroyPuncher(c)\n maybeDestroyEncryptedSocket(c, err)\n}\n\nfunction relayConnection (c, relayThrough, payload, hs) {\n let isInitiator\n let publicKey\n let token\n\n if (payload.relayThrough) {\n isInitiator = false\n publicKey = payload.relayThrough.publicKey\n token = payload.relayThrough.token\n } else {\n isInitiator = true\n publicKey = relayThrough\n token = c.relayToken\n }\n\n c.relayToken = token\n c.relaySocket = c.dht.connect(publicKey)\n c.relaySocket.setKeepAlive(c.relayKeepAlive)\n c.relayClient = relay.Client.from(c.relaySocket, { id: c.relaySocket.publicKey })\n c.relayTimeout = setTimeout(onabort, 15000, null)\n\n c.relayClient\n .pair(isInitiator, token, c.rawStream)\n .on('error', onabort)\n .on('data', ondata)\n\n function ondata (remoteId) {\n if (c.relayTimeout) clearRelayTimeout(c)\n if (c.rawStream === null) {\n onabort(null)\n return\n }\n\n c.relayPaired = true\n\n const {\n remotePort,\n remoteHost,\n socket\n } = c.relaySocket.rawStream\n\n c.rawStream\n .on('close', () => c.relaySocket.destroy())\n .connect(socket, remoteId, remotePort, remoteHost)\n\n c.encryptedSocket.start(c.rawStream, { handshake: hs })\n }\n\n function onabort (err) {\n if (c.relayTimeout) clearRelayTimeout(c)\n const socket = c.relaySocket\n c.relayToken = null\n c.relaySocket = null\n if (socket) socket.destroy()\n maybeDestroyEncryptedSocket(c, err || RELAY_ABORTED())\n }\n}\n\nfunction clearPassiveConnectTimeout (c) {\n clearTimeout(c.passiveConnectTimeout)\n c.passiveConnectTimeout = null\n}\n\nfunction clearRelayTimeout (c) {\n clearTimeout(c.relayTimeout)\n c.relayTimeout = null\n}\n\nfunction destroyPuncher (c) {\n if (c.puncher) c.puncher.destroy()\n c.session.destroy()\n}\n\nfunction pickServerRelay (relays, clientRelay) {\n for (const r of relays) {\n if (!diffAddress(r.relayAddress, clientRelay)) return r\n }\n return relays[0]\n}\n\nfunction diffAddress (a, b) {\n return a.host !== b.host || a.port !== b.port\n}\n\nfunction defaultCreateHandshake (keyPair, remotePublicKey) {\n return new NoiseWrap(keyPair, remotePublicKey)\n}\n\nfunction defaultCreateSecretStream (isInitiator, rawStream, opts) {\n return new NoiseSecretStream(isInitiator, rawStream, opts)\n}\n\nfunction onlyPrivateHosts (addr) {\n return isPrivate(addr.host)\n}\n\nfunction isRelay (relaySocket, socket, port, host) {\n const stream = relaySocket.rawStream\n if (!stream) return false\n if (stream.socket !== socket) return false\n return port === stream.remotePort && host === stream.remoteHost\n}\n\nfunction selectRelay (relayThrough) {\n if (typeof relayThrough === 'function') relayThrough = relayThrough()\n if (relayThrough === null) return null\n if (Array.isArray(relayThrough)) return relayThrough[Math.floor(Math.random() * relayThrough.length)]\n return relayThrough\n}\n\nfunction noop () {}\nconst EventEmitter = require('events')\nconst b4a = require('b4a')\nconst errors = require('./errors')\n\nmodule.exports = class ConnectionPool extends EventEmitter {\n constructor (dht) {\n super()\n\n this._dht = dht\n this._servers = new Map()\n this._connecting = new Map()\n this._connections = new Map()\n }\n\n _attachServer (server) {\n const keyString = b4a.toString(server.publicKey, 'hex')\n\n this._servers.set(keyString, server)\n\n server\n .on('close', () => {\n this._servers.delete(keyString)\n })\n .on('connection', (socket) => {\n this._attachStream(socket, true)\n })\n }\n\n _attachStream (stream, opened) {\n const existing = this.get(stream.remotePublicKey)\n\n if (existing) {\n const keepNew = stream.isInitiator === existing.isInitiator || b4a.compare(stream.publicKey, stream.remotePublicKey) > 0\n\n if (keepNew) {\n let closed = false\n\n const onclose = () => {\n closed = true\n }\n\n existing\n .on('error', noop)\n .on('close', () => {\n if (closed) return\n\n stream\n .off('error', noop)\n .off('close', onclose)\n\n this._attachStream(stream, opened)\n })\n .destroy(errors.DUPLICATE_CONNECTION())\n\n stream\n .on('error', noop)\n .on('close', onclose)\n } else {\n stream\n .on('error', noop)\n .destroy(errors.DUPLICATE_CONNECTION())\n }\n\n return\n }\n\n const session = new ConnectionRef(this, stream)\n\n const keyString = b4a.toString(stream.remotePublicKey, 'hex')\n\n if (opened) {\n this._connections.set(keyString, session)\n\n stream.on('close', () => {\n this._connections.delete(keyString)\n })\n\n this.emit('connection', stream, session)\n } else {\n this._connecting.set(keyString, session)\n\n stream\n .on('error', noop)\n .on('close', () => {\n if (opened) this._connections.delete(keyString)\n else this._connecting.delete(keyString)\n })\n .on('open', () => {\n opened = true\n\n this._connecting.delete(keyString)\n this._connections.set(keyString, session)\n\n stream.off('error', noop)\n\n this.emit('connection', stream, session)\n })\n }\n\n return session\n }\n\n get connecting () {\n return this._connecting.size\n }\n\n get connections () {\n return this._connections.values()\n }\n\n has (publicKey) {\n const keyString = b4a.toString(publicKey, 'hex')\n\n return this._connections.has(keyString) || this._connecting.has(keyString)\n }\n\n get (publicKey) {\n const keyString = b4a.toString(publicKey, 'hex')\n\n const existing = this._connections.get(keyString) || this._connecting.get(keyString)\n\n return existing?._stream || null\n }\n}\n\nclass ConnectionRef {\n constructor (pool, stream) {\n this._pool = pool\n this._stream = stream\n this._refs = 0\n }\n\n active () {\n this._refs++\n }\n\n inactive () {\n this._refs--\n }\n\n release () {\n this._stream.destroy()\n }\n}\n\nfunction noop () {}\nconst crypto = require('hypercore-crypto')\n\nconst COMMANDS = exports.COMMANDS = {\n PEER_HANDSHAKE: 0,\n PEER_HOLEPUNCH: 1,\n FIND_PEER: 2,\n LOOKUP: 3,\n ANNOUNCE: 4,\n UNANNOUNCE: 5,\n MUTABLE_PUT: 6,\n MUTABLE_GET: 7,\n IMMUTABLE_PUT: 8,\n IMMUTABLE_GET: 9\n}\n\nexports.BOOTSTRAP_NODES = global.Pear?.config.dht?.bootstrap || [\n '88.99.3.86@node1.hyperdht.org:49737',\n '142.93.90.113@node2.hyperdht.org:49737',\n '138.68.147.8@node3.hyperdht.org:49737'\n]\n\nexports.KNOWN_NODES = global.Pear?.config.dht?.nodes || []\n\nexports.FIREWALL = {\n UNKNOWN: 0,\n OPEN: 1,\n CONSISTENT: 2,\n RANDOM: 3\n}\n\nexports.ERROR = {\n // noise / connection related\n NONE: 0,\n ABORTED: 1,\n VERSION_MISMATCH: 2,\n TRY_LATER: 3,\n // dht related\n SEQ_REUSED: 16,\n SEQ_TOO_LOW: 17\n}\n\nconst [\n NS_ANNOUNCE,\n NS_UNANNOUNCE,\n NS_MUTABLE_PUT,\n NS_PEER_HANDSHAKE,\n NS_PEER_HOLEPUNCH\n] = crypto.namespace('hyperswarm/dht', [\n COMMANDS.ANNOUNCE,\n COMMANDS.UNANNOUNCE,\n COMMANDS.MUTABLE_PUT,\n COMMANDS.PEER_HANDSHAKE,\n COMMANDS.PEER_HOLEPUNCH\n])\n\nexports.NS = {\n ANNOUNCE: NS_ANNOUNCE,\n UNANNOUNCE: NS_UNANNOUNCE,\n MUTABLE_PUT: NS_MUTABLE_PUT,\n PEER_HANDSHAKE: NS_PEER_HANDSHAKE,\n PEER_HOLEPUNCH: NS_PEER_HOLEPUNCH\n}\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\n\nfunction hash (data) {\n const out = b4a.allocUnsafe(32)\n sodium.crypto_generichash(out, data)\n return out\n}\n\nfunction unslabbedHash (data) {\n const out = b4a.allocUnsafeSlow(32)\n sodium.crypto_generichash(out, data)\n return out\n}\n\nfunction createKeyPair (seed) {\n const publicKey = b4a.alloc(32)\n const secretKey = b4a.alloc(64)\n if (seed) sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed)\n else sodium.crypto_sign_keypair(publicKey, secretKey)\n return { publicKey, secretKey }\n}\n\nmodule.exports = {\n hash,\n unslabbedHash,\n createKeyPair\n}\nconst b4a = require('b4a')\nconst cenc = require('compact-encoding')\n\nfunction encodeUnslab (enc, m) {\n // Faster than unslab(c.encode(enc, data)) because it avoids the mem copy.\n // Makes sense to put in compact-encoding when we need it in other modules too\n const state = cenc.state()\n enc.preencode(state, m)\n state.buffer = b4a.allocUnsafeSlow(state.end)\n enc.encode(state, m)\n return state.buffer\n}\n\nmodule.exports = {\n encodeUnslab\n}\nmodule.exports = class DHTError extends Error {\n constructor (msg, code, fn = DHTError) {\n super(`${code}: ${msg}`)\n this.code = code\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name () {\n return 'DHTError'\n }\n\n static BAD_HANDSHAKE_REPLY (msg = 'Bad handshake reply') {\n return new DHTError(msg, 'BAD_HANDSHAKE_REPLY', DHTError.BAD_HANDSHAKE_REPLY)\n }\n\n static BAD_HOLEPUNCH_REPLY (msg = 'Bad holepunch reply') {\n return new DHTError(msg, 'BAD_HOLEPUNCH_REPLY', DHTError.BAD_HOLEPUNCH_REPLY)\n }\n\n static HOLEPUNCH_ABORTED (msg = 'Holepunch aborted') {\n return new DHTError(msg, 'HOLEPUNCH_ABORTED', DHTError.HOLEPUNCH_ABORTED)\n }\n\n static HOLEPUNCH_INVALID (msg = 'Invalid holepunch payload') {\n return new DHTError(msg, 'HOLEPUNCH_INVALID', DHTError.HOLEPUNCH_INVALID)\n }\n\n static HOLEPUNCH_PROBE_TIMEOUT (msg = 'Holepunching probe did not finish in time') {\n return new DHTError(msg, 'HOLEPUNCH_PROBE_TIMEOUT', DHTError.HOLEPUNCH_PROBE_TIMEOUT)\n }\n\n static HOLEPUNCH_DOUBLE_RANDOMIZED_NATS (msg = 'Both remote and local NATs are randomized') {\n return new DHTError(msg, 'HOLEPUNCH_DOUBLE_RANDOMIZED_NATS', DHTError.HOLEPUNCH_DOUBLE_RANDOMIZED_NATS)\n }\n\n static CANNOT_HOLEPUNCH (msg = 'Cannot holepunch to remote') {\n return new DHTError(msg, 'CANNOT_HOLEPUNCH', DHTError.CANNOT_HOLEPUNCH)\n }\n\n static REMOTE_NOT_HOLEPUNCHING (msg = 'Remote is not holepunching') {\n return new DHTError(msg, 'REMOTE_NOT_HOLEPUNCHING', DHTError.REMOTE_NOT_HOLEPUNCHING)\n }\n\n static REMOTE_NOT_HOLEPUNCHABLE (msg = 'Remote is not holepunchable') {\n return new DHTError(msg, 'REMOTE_NOT_HOLEPUNCHABLE', DHTError.REMOTE_NOT_HOLEPUNCHABLE)\n }\n\n static REMOTE_ABORTED (msg = 'Remote aborted') {\n return new DHTError(msg, 'REMOTE_ABORTED', DHTError.REMOTE_ABORTED)\n }\n\n static HANDSHAKE_UNFINISHED (msg = 'Handshake did not finish') {\n return new DHTError(msg, 'HANDSHAKE_UNFINISHED', DHTError.HANDSHAKE_UNFINISHED)\n }\n\n static HANDSHAKE_INVALID (msg = 'Received invalid handshake') {\n return new DHTError(msg, 'HANDSHAKE_INVALID', DHTError.HANDSHAKE_INVALID)\n }\n\n static ALREADY_LISTENING (msg = 'Already listening') {\n return new DHTError(msg, 'ALREADY_LISTENING', DHTError.ALREADY_LISTENING)\n }\n\n static KEYPAIR_ALREADY_USED (msg = 'Keypair already used') {\n return new DHTError(msg, 'KEYPAIR_ALREADY_USED', DHTError.KEYPAIR_ALREADY_USED)\n }\n\n static NODE_DESTROYED (msg = 'Node destroyed') {\n return new DHTError(msg, 'NODE_DESTROYED', DHTError.NODE_DESTROYED)\n }\n\n static PEER_CONNECTION_FAILED (msg = 'Could not connect to peer') {\n return new DHTError(msg, 'PEER_CONNECTION_FAILED', DHTError.PEER_CONNECTION_FAILED)\n }\n\n static PEER_NOT_FOUND (msg = 'Peer not found') {\n return new DHTError(msg, 'PEER_NOT_FOUND', DHTError.PEER_NOT_FOUND)\n }\n\n static STREAM_NOT_CONNECTED (msg = 'Stream is not connected') {\n return new DHTError(msg, 'STREAM_NOT_CONNECTED', DHTError.STREAM_DISCONNECTED)\n }\n\n static SERVER_INCOMPATIBLE (msg = 'Server is using an incompatible version') {\n return new DHTError(msg, 'SERVER_INCOMPATIBLE', DHTError.SERVER_INCOMPATIBLE)\n }\n\n static SERVER_ERROR (msg = 'Server returned an error') {\n return new DHTError(msg, 'SERVER_ERROR', DHTError.SERVER_ERROR)\n }\n\n static DUPLICATE_CONNECTION (msg = 'Duplicate connection') {\n return new DHTError(msg, 'DUPLICATE_CONNECTION', DHTError.DUPLICATE_CONNECTION)\n }\n\n static RELAY_ABORTED (msg = 'Relay aborted') {\n return new DHTError(msg, 'RELAY_ABORTED', DHTError.RELAY_ABORTED)\n }\n\n static SUSPENDED (msg = 'Suspended') {\n return new DHTError(msg, 'SUSPENDED', DHTError.SUSPENDED)\n }\n}\nconst b4a = require('b4a')\nconst Nat = require('./nat')\nconst Sleeper = require('./sleeper')\nconst { FIREWALL } = require('./constants')\n\nconst BIRTHDAY_SOCKETS = 256\nconst HOLEPUNCH = b4a.from([0])\nconst HOLEPUNCH_TTL = 5\nconst DEFAULT_TTL = 64\nconst MAX_REOPENS = 3\n\nmodule.exports = class Holepuncher {\n constructor (dht, session, isInitiator, remoteFirewall = FIREWALL.UNKNOWN) {\n const holder = dht._socketPool.acquire()\n\n this.dht = dht\n this.session = session\n\n this.nat = new Nat(dht, session, holder.socket)\n this.nat.autoSample()\n\n this.isInitiator = isInitiator\n\n // events\n this.onconnect = noop\n this.onabort = noop\n\n this.punching = false\n this.connected = false\n this.destroyed = false\n this.randomized = false\n\n // track remote state\n this.remoteFirewall = remoteFirewall\n this.remoteAddresses = []\n this.remoteHolepunching = false\n\n this._sleeper = new Sleeper()\n this._reopening = null\n this._timeout = null\n this._punching = null\n this._allHolders = []\n this._holder = this._addRef(holder)\n }\n\n get socket () {\n return this._holder.socket\n }\n\n updateRemote ({ punching, firewall, addresses, verified }) {\n const remoteAddresses = []\n\n if (addresses) {\n for (const addr of addresses) {\n remoteAddresses.push({\n host: addr.host,\n port: addr.port,\n verified: (verified === addr.host) || this._isVerified(addr.host)\n })\n }\n }\n\n this.remoteFirewall = firewall\n this.remoteAddresses = remoteAddresses\n this.remoteHolepunching = punching\n }\n\n _isVerified (host) {\n for (const addr of this.remoteAddresses) {\n if (addr.verified && addr.host === host) {\n return true\n }\n }\n return false\n }\n\n ping (addr, socket = this._holder.socket) {\n return holepunch(socket, addr, false)\n }\n\n openSession (addr, socket = this._holder.socket) {\n return holepunch(socket, addr, true)\n }\n\n async analyze (allowReopen) {\n await this.nat.analyzing\n if (this._unstable()) {\n if (!allowReopen) return false\n if (!this._reopening) this._reopening = this._reopen()\n return this._reopening\n }\n return true\n }\n\n _unstable () {\n // TODO!!: We need an additional heuristic here... If we were NOT random in the past we should also do this.\n const firewall = this.nat.firewall\n return (this.remoteFirewall >= FIREWALL.RANDOM && firewall >= FIREWALL.RANDOM) || firewall === FIREWALL.UNKNOWN\n }\n\n _reset () {\n const prev = this._holder\n\n this._allHolders.pop()\n this._holder = this._addRef(this.dht._socketPool.acquire())\n\n prev.release()\n this.nat.destroy()\n\n this.nat = new Nat(this.dht, this.session, this._holder.socket)\n // TODO: maybe make auto sampling configurable somehow?\n this.nat.autoSample()\n }\n\n _addRef (ref) {\n this._allHolders.push(ref)\n ref.onholepunchmessage = (msg, rinfo) => this._onholepunchmessage(msg, rinfo, ref)\n return ref\n }\n\n _onholepunchmessage (_, addr, ref) {\n if (!this.isInitiator) { // TODO: we don't need this if we had a way to connect a socket to many hosts\n holepunch(ref.socket, addr, false) // never fails\n return\n }\n\n if (this.connected) return\n\n this.connected = true\n this.punching = false\n\n for (const r of this._allHolders) {\n if (r === ref) continue\n r.release()\n }\n\n this._allHolders[0] = ref\n while (this._allHolders.length > 1) this._allHolders.pop()\n\n this._decrementRandomized()\n this.onconnect(ref.socket, addr.port, addr.host)\n }\n\n _done () {\n return this.destroyed || this.connected\n }\n\n async _reopen () {\n for (let i = 0; this._unstable() && i < MAX_REOPENS && !this._done() && !this.punching; i++) {\n this._reset()\n await this.nat.analyzing\n }\n\n return coerceFirewall(this.nat.firewall) === FIREWALL.CONSISTENT\n }\n\n punch () {\n if (!this._punching) this._punching = this._punch()\n return this._punching\n }\n\n async _punch () {\n if (this._done() || !this.remoteAddresses.length) return false\n\n this.punching = true\n\n // Coerce into consistency for now. Obvs we could make this this more efficient if we use that info\n // but that's seldomly used since those will just use tcp most of the time.\n\n const local = coerceFirewall(this.nat.firewall)\n const remote = coerceFirewall(this.remoteFirewall)\n\n // Note that most of these async functions are meant to run in the background\n // which is why we don't await them here and why they are not allowed to throw\n\n let remoteVerifiedAddress = null\n for (const addr of this.remoteAddresses) {\n if (addr.verified) {\n remoteVerifiedAddress = addr\n break\n }\n }\n\n if (local === FIREWALL.CONSISTENT && remote === FIREWALL.CONSISTENT) {\n this.dht.stats.punches.consistent++\n this._consistentProbe()\n return true\n }\n\n if (!remoteVerifiedAddress) return false\n\n if (local === FIREWALL.CONSISTENT && remote >= FIREWALL.RANDOM) {\n this.dht.stats.punches.random++\n this._incrementRandomized()\n this._randomProbes(remoteVerifiedAddress)\n return true\n }\n\n if (local >= FIREWALL.RANDOM && remote === FIREWALL.CONSISTENT) {\n this.dht.stats.punches.random++\n this._incrementRandomized()\n await this._openBirthdaySockets(remoteVerifiedAddress)\n if (this.punching) this._keepAliveRandomNat(remoteVerifiedAddress)\n return true\n }\n\n return false\n }\n\n // Note that this never throws so it is safe to run in the background\n async _consistentProbe () {\n // Here we do the sleep first because the \"fast open\" mode in the server just fired a ping\n if (!this.isInitiator) await this._sleeper.pause(1000)\n\n let tries = 0\n\n while (this.punching && tries++ < 10) {\n for (const addr of this.remoteAddresses) {\n // only try unverified addresses every 4 ticks\n if (!addr.verified && ((tries & 3) !== 0)) continue\n await holepunch(this._holder.socket, addr, false)\n }\n if (this.punching) await this._sleeper.pause(1000)\n }\n\n this._autoDestroy()\n }\n\n // Note that this never throws so it is safe to run in the background\n async _randomProbes (remoteAddr) {\n let tries = 1750 // ~35s\n\n while (this.punching && tries-- > 0) {\n const addr = { host: remoteAddr.host, port: randomPort() }\n await holepunch(this._holder.socket, addr, false)\n if (this.punching) await this._sleeper.pause(20)\n }\n\n this._autoDestroy()\n }\n\n // Note that this never throws so it is safe to run in the background\n async _keepAliveRandomNat (remoteAddr) {\n let i = 0\n let lowTTLRounds = 1\n\n // TODO: experiment with this here. We just bursted all the messages in\n // openOtherSockets to ensure the sockets are open, so it's potentially\n // a good idea to slow down for a bit.\n await this._sleeper.pause(100)\n\n let tries = 1750 // ~35s\n\n while (this.punching && tries-- > 0) {\n if (i === this._allHolders.length) {\n i = 0\n if (lowTTLRounds > 0) lowTTLRounds--\n }\n\n await holepunch(this._allHolders[i++].socket, remoteAddr, lowTTLRounds > 0)\n if (this.punching) await this._sleeper.pause(20)\n }\n\n this._autoDestroy()\n }\n\n async _openBirthdaySockets (remoteAddr) {\n while (this.punching && this._allHolders.length < BIRTHDAY_SOCKETS) {\n const ref = this._addRef(this.dht._socketPool.acquire())\n await holepunch(ref.socket, remoteAddr, HOLEPUNCH_TTL)\n }\n }\n\n _autoDestroy () {\n if (!this.connected) this.destroy()\n }\n\n _incrementRandomized () {\n if (!this.randomized) {\n this.randomized = true\n this.dht._randomPunches++\n }\n }\n\n _decrementRandomized () {\n if (this.randomized) {\n this.dht._lastRandomPunch = Date.now()\n this.randomized = false\n this.dht._randomPunches--\n }\n }\n\n destroy () {\n if (this.destroyed) return\n this.destroyed = true\n this.punching = false\n\n for (const ref of this._allHolders) ref.release()\n this._allHolders = []\n this.nat.destroy()\n\n if (!this.connected) {\n this._decrementRandomized()\n this.onabort()\n }\n }\n\n static ping (socket, addr) {\n return holepunch(socket, addr, false)\n }\n\n static localAddresses (socket) {\n return localAddresses(socket)\n }\n\n static matchAddress (myAddresses, externalAddresses) {\n return matchAddress(myAddresses, externalAddresses)\n }\n}\n\nfunction holepunch (socket, addr, lowTTL) {\n return socket.send(HOLEPUNCH, addr.port, addr.host, lowTTL ? HOLEPUNCH_TTL : DEFAULT_TTL)\n}\n\nfunction randomPort () {\n return 1000 + (Math.random() * 64536) | 0\n}\n\nfunction coerceFirewall (fw) {\n return fw === FIREWALL.OPEN ? FIREWALL.CONSISTENT : fw\n}\n\nfunction localAddresses (socket) {\n const addrs = []\n const { host, port } = socket.address()\n\n if (host === '127.0.0.1') return [{ host, port }]\n\n for (const n of socket.udx.networkInterfaces()) {\n if (n.family !== 4 || n.internal) continue\n\n addrs.push({ host: n.host, port })\n }\n\n if (addrs.length === 0) {\n addrs.push({ host: '127.0.0.1', port })\n }\n\n return addrs\n}\n\nfunction matchAddress (localAddresses, remoteLocalAddresses) {\n if (remoteLocalAddresses.length === 0) return null\n\n let best = { segment: 1, addr: null }\n\n for (const localAddress of localAddresses) {\n // => 192.168.122.238\n const a = localAddress.host.split('.')\n\n for (const remoteAddress of remoteLocalAddresses) {\n // => 192.168.0.23\n // => 192.168.122.1\n const b = remoteAddress.host.split('.')\n\n // Matches 192.*.*.*\n if (a[0] === b[0]) {\n if (best.segment === 1) best = { segment: 2, addr: remoteAddress }\n\n // Matches 192.168.*.*\n if (a[1] === b[1]) {\n if (best.segment === 2) best = { segment: 3, addr: remoteAddress }\n\n // Matches 192.168.122.*\n if (a[2] === b[2]) return remoteAddress\n }\n }\n }\n }\n\n return best.addr\n}\n\nfunction noop () {}\nconst c = require('compact-encoding')\nconst net = require('compact-encoding-net')\n\nconst ipv4 = {\n ...net.ipv4Address,\n decode (state) {\n const ip = net.ipv4Address.decode(state)\n return {\n host: ip.host,\n port: ip.port\n }\n }\n}\n\nconst ipv4Array = c.array(ipv4)\n\nconst ipv6 = {\n ...net.ipv6Address,\n decode (state) {\n const ip = net.ipv6Address.decode(state)\n return {\n host: ip.host,\n port: ip.port\n }\n }\n}\n\nconst ipv6Array = c.array(ipv6)\n\nexports.handshake = {\n preencode (state, m) {\n state.end += 1 + 1 + (m.peerAddress ? 6 : 0) + (m.relayAddress ? 6 : 0)\n c.buffer.preencode(state, m.noise)\n },\n encode (state, m) {\n const flags = (m.peerAddress ? 1 : 0) | (m.relayAddress ? 2 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.mode)\n c.buffer.encode(state, m.noise)\n\n if (m.peerAddress) ipv4.encode(state, m.peerAddress)\n if (m.relayAddress) ipv4.encode(state, m.relayAddress)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n mode: c.uint.decode(state),\n noise: c.buffer.decode(state),\n peerAddress: (flags & 1) ? ipv4.decode(state) : null,\n relayAddress: (flags & 2) ? ipv4.decode(state) : null\n }\n }\n}\n\nconst relayInfo = {\n preencode (state, m) {\n state.end += 12\n },\n encode (state, m) {\n ipv4.encode(state, m.relayAddress)\n ipv4.encode(state, m.peerAddress)\n },\n decode (state) {\n return {\n relayAddress: ipv4.decode(state),\n peerAddress: ipv4.decode(state)\n }\n }\n}\n\nconst relayInfoArray = c.array(relayInfo)\n\nconst holepunchInfo = {\n preencode (state, m) {\n c.uint.preencode(state, m.id)\n relayInfoArray.preencode(state, m.relays)\n },\n encode (state, m) {\n c.uint.encode(state, m.id)\n relayInfoArray.encode(state, m.relays)\n },\n decode (state) {\n return {\n id: c.uint.decode(state),\n relays: relayInfoArray.decode(state)\n }\n }\n}\n\nconst udxInfo = {\n preencode (state, m) {\n state.end += 2 // version + features\n c.uint.preencode(state, m.id)\n c.uint.preencode(state, m.seq)\n },\n encode (state, m) {\n c.uint.encode(state, 1)\n c.uint.encode(state, m.reusableSocket ? 1 : 0)\n c.uint.encode(state, m.id)\n c.uint.encode(state, m.seq)\n },\n decode (state) {\n const version = c.uint.decode(state)\n const features = c.uint.decode(state)\n\n return {\n version,\n reusableSocket: (features & 1) !== 0,\n id: c.uint.decode(state),\n seq: c.uint.decode(state)\n }\n }\n}\n\nconst secretStreamInfo = {\n preencode (state, m) {\n c.uint.preencode(state, 1)\n },\n encode (state, m) {\n c.uint.encode(state, 1)\n },\n decode (state) {\n return {\n version: c.uint.decode(state)\n }\n }\n}\n\nconst relayThroughInfo = {\n preencode (state, m) {\n c.uint.preencode(state, 1) // version\n c.uint.preencode(state, 0) // flags\n c.fixed32.preencode(state, m.publicKey)\n c.fixed32.preencode(state, m.token)\n },\n encode (state, m) {\n c.uint.encode(state, 1)\n c.uint.encode(state, 0)\n c.fixed32.encode(state, m.publicKey)\n c.fixed32.encode(state, m.token)\n },\n decode (state) {\n const version = c.uint.decode(state)\n c.uint.decode(state)\n\n return {\n version,\n publicKey: c.fixed32.decode(state),\n token: c.fixed32.decode(state)\n }\n }\n}\n\nexports.noisePayload = {\n preencode (state, m) {\n state.end += 4 // version + flags + error + firewall\n if (m.holepunch) holepunchInfo.preencode(state, m.holepunch)\n if (m.addresses4 && m.addresses4.length) ipv4Array.preencode(state, m.addresses4)\n if (m.addresses6 && m.addresses6.length) ipv6Array.preencode(state, m.addresses6)\n if (m.udx) udxInfo.preencode(state, m.udx)\n if (m.secretStream) secretStreamInfo.preencode(state, m.secretStream)\n if (m.relayThrough) relayThroughInfo.preencode(state, m.relayThrough)\n },\n encode (state, m) {\n let flags = 0\n\n if (m.holepunch) flags |= 1\n if (m.addresses4 && m.addresses4.length) flags |= 2\n if (m.addresses6 && m.addresses6.length) flags |= 4\n if (m.udx) flags |= 8\n if (m.secretStream) flags |= 16\n if (m.relayThrough) flags |= 32\n\n c.uint.encode(state, 1) // version\n c.uint.encode(state, flags)\n c.uint.encode(state, m.error)\n c.uint.encode(state, m.firewall)\n\n if (m.holepunch) holepunchInfo.encode(state, m.holepunch)\n if (m.addresses4 && m.addresses4.length) ipv4Array.encode(state, m.addresses4)\n if (m.addresses6 && m.addresses6.length) ipv6Array.encode(state, m.addresses6)\n if (m.udx) udxInfo.encode(state, m.udx)\n if (m.secretStream) secretStreamInfo.encode(state, m.secretStream)\n if (m.relayThrough) relayThroughInfo.encode(state, m.relayThrough)\n },\n decode (state) {\n const version = c.uint.decode(state)\n\n if (version !== 1) {\n // Do not attempt to decode but return this back to the user so they can\n // actually handle it\n return {\n version,\n error: 0,\n firewall: 0,\n holepunch: null,\n addresses4: [],\n addresses6: [],\n udx: null,\n secretStream: null,\n relayThrough: null\n }\n }\n\n const flags = c.uint.decode(state)\n\n return {\n version,\n error: c.uint.decode(state),\n firewall: c.uint.decode(state),\n holepunch: (flags & 1) !== 0 ? holepunchInfo.decode(state) : null,\n addresses4: (flags & 2) !== 0 ? ipv4Array.decode(state) : [],\n addresses6: (flags & 4) !== 0 ? ipv6Array.decode(state) : [],\n udx: (flags & 8) !== 0 ? udxInfo.decode(state) : null,\n secretStream: (flags & 16) !== 0 ? secretStreamInfo.decode(state) : null,\n relayThrough: (flags & 32) !== 0 ? relayThroughInfo.decode(state) : null\n }\n }\n}\n\nexports.holepunch = {\n preencode (state, m) {\n state.end += 2\n c.uint.preencode(state, m.id)\n c.buffer.preencode(state, m.payload)\n if (m.peerAddress) ipv4.preencode(state, m.peerAddress)\n },\n encode (state, m) {\n const flags = m.peerAddress ? 1 : 0\n c.uint.encode(state, flags)\n c.uint.encode(state, m.mode)\n c.uint.encode(state, m.id)\n c.buffer.encode(state, m.payload)\n if (m.peerAddress) ipv4.encode(state, m.peerAddress)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n return {\n mode: c.uint.decode(state),\n id: c.uint.decode(state),\n payload: c.buffer.decode(state),\n peerAddress: (flags & 1) ? ipv4.decode(state) : null\n }\n }\n}\n\nexports.holepunchPayload = {\n preencode (state, m) {\n state.end += 4 // flags + error + firewall + round\n if (m.addresses) ipv4Array.preencode(state, m.addresses)\n if (m.remoteAddress) state.end += 6\n if (m.token) state.end += 32\n if (m.remoteToken) state.end += 32\n },\n encode (state, m) {\n const flags = (m.connected ? 1 : 0) |\n (m.punching ? 2 : 0) |\n (m.addresses ? 4 : 0) |\n (m.remoteAddress ? 8 : 0) |\n (m.token ? 16 : 0) |\n (m.remoteToken ? 32 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.error)\n c.uint.encode(state, m.firewall)\n c.uint.encode(state, m.round)\n\n if (m.addresses) ipv4Array.encode(state, m.addresses)\n if (m.remoteAddress) ipv4.encode(state, m.remoteAddress)\n if (m.token) c.fixed32.encode(state, m.token)\n if (m.remoteToken) c.fixed32.encode(state, m.remoteToken)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n error: c.uint.decode(state),\n firewall: c.uint.decode(state),\n round: c.uint.decode(state),\n connected: (flags & 1) !== 0,\n punching: (flags & 2) !== 0,\n addresses: (flags & 4) !== 0 ? ipv4Array.decode(state) : null,\n remoteAddress: (flags & 8) !== 0 ? ipv4.decode(state) : null,\n token: (flags & 16) !== 0 ? c.fixed32.decode(state) : null,\n remoteToken: (flags & 32) !== 0 ? c.fixed32.decode(state) : null\n }\n }\n}\n\nconst peer = exports.peer = {\n preencode (state, m) {\n state.end += 32\n ipv4Array.preencode(state, m.relayAddresses)\n },\n encode (state, m) {\n c.fixed32.encode(state, m.publicKey)\n ipv4Array.encode(state, m.relayAddresses)\n },\n decode (state) {\n return {\n publicKey: c.fixed32.decode(state),\n relayAddresses: ipv4Array.decode(state)\n }\n }\n}\n\nexports.peers = c.array(peer)\n\nexports.announce = {\n preencode (state, m) {\n state.end++ // flags\n if (m.peer) peer.preencode(state, m.peer)\n if (m.refresh) state.end += 32\n if (m.signature) state.end += 64\n },\n encode (state, m) {\n const flags = (m.peer ? 1 : 0) | (m.refresh ? 2 : 0) | (m.signature ? 4 : 0)\n c.uint.encode(state, flags)\n if (m.peer) peer.encode(state, m.peer)\n if (m.refresh) c.fixed32.encode(state, m.refresh)\n if (m.signature) c.fixed64.encode(state, m.signature)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n peer: (flags & 1) !== 0 ? peer.decode(state) : null,\n refresh: (flags & 2) !== 0 ? c.fixed32.decode(state) : null,\n signature: (flags & 4) !== 0 ? c.fixed64.decode(state) : null\n }\n }\n}\n\nexports.mutableSignable = {\n preencode (state, m) {\n c.uint.preencode(state, m.seq)\n c.buffer.preencode(state, m.value)\n },\n encode (state, m) {\n c.uint.encode(state, m.seq)\n c.buffer.encode(state, m.value)\n },\n decode (state) {\n return {\n seq: c.uint.decode(state),\n value: c.buffer.decode(state)\n }\n }\n}\n\nexports.mutablePutRequest = {\n preencode (state, m) {\n c.fixed32.preencode(state, m.publicKey)\n c.uint.preencode(state, m.seq)\n c.buffer.preencode(state, m.value)\n c.fixed64.preencode(state, m.signature)\n },\n encode (state, m) {\n c.fixed32.encode(state, m.publicKey)\n c.uint.encode(state, m.seq)\n c.buffer.encode(state, m.value)\n c.fixed64.encode(state, m.signature)\n },\n decode (state) {\n return {\n publicKey: c.fixed32.decode(state),\n seq: c.uint.decode(state),\n value: c.buffer.decode(state),\n signature: c.fixed64.decode(state)\n }\n }\n}\n\nexports.mutableGetResponse = {\n preencode (state, m) {\n c.uint.preencode(state, m.seq)\n c.buffer.preencode(state, m.value)\n c.fixed64.preencode(state, m.signature)\n },\n encode (state, m) {\n c.uint.encode(state, m.seq)\n c.buffer.encode(state, m.value)\n c.fixed64.encode(state, m.signature)\n },\n decode (state) {\n return {\n seq: c.uint.decode(state),\n value: c.buffer.decode(state),\n signature: c.fixed64.decode(state)\n }\n }\n}\nconst { FIREWALL } = require('../lib/constants')\n\nmodule.exports = class Nat {\n constructor (dht, session, socket) {\n this._samplesHost = []\n this._samplesFull = []\n this._visited = new Map()\n this._resolve = null\n this._minSamples = 4\n this._autoSampling = false\n\n this.dht = dht\n this.session = session\n this.socket = socket\n\n this.sampled = 0\n this.firewall = dht.firewalled ? FIREWALL.UNKNOWN : FIREWALL.OPEN\n this.addresses = null\n\n this.analyzing = new Promise((resolve) => { this._resolve = resolve })\n }\n\n autoSample (retry = true) {\n if (this._autoSampling) return\n this._autoSampling = true\n\n const self = this\n const socket = this.socket\n const maxPings = this._minSamples\n\n let skip = this.dht.nodes.length >= 8 ? 5 : 0\n let pending = 0\n\n // TODO: it would be best to pick the nodes to help us based on latency to us\n // That should reduce connect latency in general. We should investigate tracking that later on.\n\n // TODO 2: try to pick nodes with different IPs as well, as that'll help multi IP cell connections...\n // If we expose this from the nat sampler then the DHT should be able to help us filter out scams as well...\n\n for (let node = this.dht.nodes.latest; node && this.sampled + pending < maxPings; node = node.prev) {\n if (skip > 0) {\n skip--\n continue\n }\n\n const ref = node.host + ':' + node.port\n\n if (this._visited.has(ref)) continue\n this._visited.set(ref, 1)\n\n pending++\n this.session.ping(node, { socket, retry: false }).then(onpong, onskip)\n }\n\n pending++\n onskip()\n\n function onpong (res) {\n self.add(res.to, res.from)\n onskip()\n }\n\n function onskip () {\n if (--pending === 0 && self.sampled < self._minSamples) {\n if (retry) {\n self._autoSampling = false\n self.autoSample(false)\n return\n }\n self._resolve()\n }\n }\n }\n\n destroy () {\n this._autoSampling = true\n this._minSamples = 0\n this._resolve()\n }\n\n unfreeze () {\n this.frozen = false\n this._updateFirewall()\n this._updateAddresses()\n }\n\n freeze () {\n this.frozen = true\n }\n\n _updateFirewall () {\n if (!this.dht.firewalled) {\n this.firewall = FIREWALL.OPEN\n return\n }\n\n if (this.sampled < 3) return\n\n const max = this._samplesFull[0].hits\n\n if (max >= 3) {\n this.firewall = FIREWALL.CONSISTENT\n return\n }\n\n if (max === 1) {\n this.firewall = FIREWALL.RANDOM\n return\n }\n\n // else max === 2\n\n // 1 host, >= 4 total samples ie, 2 bad ones -> random\n if (this._samplesHost.length === 1 && this.sampled > 3) {\n this.firewall = FIREWALL.RANDOM\n return\n }\n\n // double hit on two different ips -> assume consistent\n if (this._samplesHost.length > 1 && this._samplesFull[1].hits > 1) {\n this.firewall = FIREWALL.CONSISTENT\n return\n }\n\n // (4 is just means - all the samples we expect) - no decision - assume random\n if (this.sampled > 4) {\n this.firewall = FIREWALL.RANDOM\n }\n }\n\n _updateAddresses () {\n if (this.firewall === FIREWALL.UNKNOWN) {\n this.addresses = null\n return\n }\n\n if (this.firewall === FIREWALL.RANDOM) {\n this.addresses = [this._samplesHost[0]]\n return\n }\n\n if (this.firewall === FIREWALL.CONSISTENT) {\n this.addresses = []\n for (const addr of this._samplesFull) {\n if (addr.hits >= 2 || this.addresses.length < 2) this.addresses.push(addr)\n }\n }\n }\n\n update () {\n if (this.dht.firewalled && this.firewall === FIREWALL.OPEN) {\n this.firewall = FIREWALL.UNKNOWN\n }\n this._updateFirewall()\n this._updateAddresses()\n }\n\n add (addr, from) {\n const ref = from.host + ':' + from.port\n\n if (this._visited.get(ref) === 2) return\n this._visited.set(ref, 2)\n\n addSample(this._samplesHost, addr.host, 0)\n addSample(this._samplesFull, addr.host, addr.port)\n\n if ((++this.sampled >= 3 || !this.dht.firewalled) && !this.frozen) {\n this.update()\n }\n\n if ((this.firewall === FIREWALL.CONSISTENT || this.firewall === FIREWALL.OPEN)) {\n this._resolve()\n } else if (this.sampled >= this._minSamples) {\n this._resolve()\n }\n }\n}\n\nfunction addSample (samples, host, port) {\n for (let i = 0; i < samples.length; i++) {\n const s = samples[i]\n\n if (s.port !== port || s.host !== host) continue\n s.hits++\n\n for (; i > 0; i--) {\n const prev = samples[i - 1]\n if (prev.hits >= s.hits) return\n samples[i - 1] = s\n samples[i] = prev\n }\n\n return\n }\n\n samples.push({\n host,\n port,\n hits: 1\n })\n}\nconst NoiseSecretStream = require('@hyperswarm/secret-stream')\nconst NoiseHandshake = require('noise-handshake')\nconst curve = require('noise-curve-ed')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst sodium = require('sodium-universal')\nconst m = require('./messages')\nconst { NS } = require('./constants')\nconst { HANDSHAKE_UNFINISHED } = require('./errors')\n\nconst NOISE_PROLOUGE = NS.PEER_HANDSHAKE\n\nmodule.exports = class NoiseWrap {\n constructor (keyPair, remotePublicKey) {\n this.isInitiator = !!remotePublicKey\n this.remotePublicKey = remotePublicKey\n this.keyPair = keyPair\n this.handshake = new NoiseHandshake('IK', this.isInitiator, keyPair, { curve })\n this.handshake.initialise(NOISE_PROLOUGE, remotePublicKey)\n }\n\n send (payload) {\n const buf = c.encode(m.noisePayload, payload)\n return this.handshake.send(buf)\n }\n\n recv (buf) {\n const payload = c.decode(m.noisePayload, this.handshake.recv(buf))\n this.remotePublicKey = b4a.toBuffer(this.handshake.rs)\n return payload\n }\n\n final () {\n if (!this.handshake.complete) throw HANDSHAKE_UNFINISHED()\n\n const holepunchSecret = b4a.allocUnsafe(32)\n\n sodium.crypto_generichash(holepunchSecret, NS.PEER_HOLEPUNCH, this.handshake.hash)\n\n return {\n isInitiator: this.isInitiator,\n publicKey: this.keyPair.publicKey,\n streamId: this.streamId,\n remotePublicKey: this.remotePublicKey,\n remoteId: NoiseSecretStream.id(this.handshake.hash, !this.isInitiator),\n holepunchSecret,\n hash: b4a.toBuffer(this.handshake.hash),\n rx: b4a.toBuffer(this.handshake.rx),\n tx: b4a.toBuffer(this.handshake.tx)\n }\n }\n}\nconst c = require('compact-encoding')\nconst sodium = require('sodium-universal')\nconst RecordCache = require('record-cache')\nconst Cache = require('xache')\nconst b4a = require('b4a')\nconst unslab = require('unslab')\n\nconst { encodeUnslab } = require('./encode')\nconst m = require('./messages')\nconst { NS, ERROR } = require('./constants')\n\nconst EMPTY = b4a.alloc(0)\nconst TMP = b4a.allocUnsafe(32)\n\nconst rawArray = c.array(c.raw)\n\nmodule.exports = class Persistent {\n constructor (dht, opts) {\n this.dht = dht\n this.records = new RecordCache(opts.records)\n this.refreshes = new Cache(opts.refreshes)\n this.mutables = new Cache(opts.mutables)\n this.immutables = new Cache(opts.immutables)\n }\n\n onlookup (req) {\n if (!req.target) return\n\n const k = b4a.toString(req.target, 'hex')\n const records = this.records.get(k, 20)\n const fwd = this.dht._router.get(k)\n\n if (fwd && records.length < 20) records.push(fwd.record)\n\n req.reply(records.length ? c.encode(rawArray, records) : null)\n }\n\n onfindpeer (req) {\n if (!req.target) return\n const fwd = this.dht._router.get(req.target)\n req.reply(fwd ? fwd.record : null)\n }\n\n unannounce (target, publicKey) {\n const k = b4a.toString(target, 'hex')\n sodium.crypto_generichash(TMP, publicKey)\n\n if (b4a.equals(TMP, target)) this.dht._router.delete(k)\n this.records.remove(k, publicKey)\n }\n\n onunannounce (req) {\n if (!req.target || !req.token) return\n\n const unann = decode(m.announce, req.value)\n if (unann === null) return\n\n const { peer, signature } = unann\n if (!peer || !signature) return\n\n const signable = annSignable(req.target, req.token, this.dht.id, unann, NS.UNANNOUNCE)\n\n if (!sodium.crypto_sign_verify_detached(signature, signable, peer.publicKey)) {\n return\n }\n\n this.unannounce(req.target, peer.publicKey)\n req.reply(null, { token: false, closerNodes: false })\n }\n\n _onrefresh (token, req) {\n sodium.crypto_generichash(TMP, token)\n const activeRefresh = b4a.toString(TMP, 'hex')\n\n const r = this.refreshes.get(activeRefresh)\n if (!r) return\n\n const { announceSelf, k, record } = r\n const publicKey = record.subarray(0, 32)\n\n if (announceSelf) {\n this.dht._router.set(k, {\n relay: req.from,\n record,\n onconnect: null,\n onholepunch: null\n })\n this.records.remove(k, publicKey)\n } else {\n this.records.add(k, publicKey, record)\n }\n\n this.refreshes.delete(activeRefresh)\n this.refreshes.set(b4a.toString(token, 'hex'), r)\n\n req.reply(null, { token: false, closerNodes: false })\n }\n\n onannounce (req) {\n if (!req.target || !req.token || !this.dht.id) return\n\n const ann = decode(m.announce, req.value)\n if (ann === null) return\n\n const signable = annSignable(req.target, req.token, this.dht.id, ann, NS.ANNOUNCE)\n const { peer, refresh, signature } = ann\n\n if (!peer) {\n if (!refresh) return\n this._onrefresh(refresh, req)\n return\n }\n\n if (!signature || !sodium.crypto_sign_verify_detached(signature, signable, peer.publicKey)) {\n return\n }\n\n // TODO: it would be potentially be more optimal to allow more than 3 addresses here for a findPeer response\n // and only use max 3 for a lookup reply\n if (peer.relayAddresses.length > 3) {\n peer.relayAddresses = peer.relayAddresses.slice(0, 3)\n }\n\n sodium.crypto_generichash(TMP, peer.publicKey)\n\n const k = b4a.toString(req.target, 'hex')\n const announceSelf = b4a.equals(TMP, req.target)\n const record = encodeUnslab(m.peer, peer)\n\n if (announceSelf) {\n this.dht._router.set(k, {\n relay: req.from,\n record,\n onconnect: null,\n onholepunch: null\n })\n this.records.remove(k, peer.publicKey)\n } else {\n this.records.add(k, peer.publicKey, record)\n }\n\n if (refresh) {\n this.refreshes.set(b4a.toString(refresh, 'hex'), { k, record, announceSelf })\n }\n\n req.reply(null, { token: false, closerNodes: false })\n }\n\n onmutableget (req) {\n if (!req.target || !req.value) return\n\n let seq = 0\n try {\n seq = c.decode(c.uint, req.value)\n } catch {\n return\n }\n\n const k = b4a.toString(req.target, 'hex')\n const value = this.mutables.get(k)\n\n if (!value) {\n req.reply(null)\n return\n }\n\n const localSeq = c.decode(c.uint, value)\n req.reply(localSeq < seq ? null : value)\n }\n\n onmutableput (req) {\n if (!req.target || !req.token || !req.value) return\n\n const p = decode(m.mutablePutRequest, req.value)\n if (!p) return\n\n const { publicKey, seq, value, signature } = p\n\n const hash = b4a.allocUnsafe(32)\n sodium.crypto_generichash(hash, publicKey)\n if (!b4a.equals(hash, req.target)) return\n\n if (!value || !verifyMutable(signature, seq, value, publicKey)) return\n\n const k = b4a.toString(hash, 'hex')\n const local = this.mutables.get(k)\n\n if (local) {\n const existing = c.decode(m.mutableGetResponse, local)\n if (existing.value && existing.seq === seq && b4a.compare(value, existing.value) !== 0) {\n req.error(ERROR.SEQ_REUSED)\n return\n }\n if (seq < existing.seq) {\n req.error(ERROR.SEQ_TOO_LOW)\n return\n }\n }\n\n this.mutables.set(k, encodeUnslab(m.mutableGetResponse, { seq, value, signature }))\n req.reply(null)\n }\n\n onimmutableget (req) {\n if (!req.target) return\n\n const k = b4a.toString(req.target, 'hex')\n const value = this.immutables.get(k)\n\n req.reply(value || null)\n }\n\n onimmutableput (req) {\n if (!req.target || !req.token || !req.value) return\n\n const hash = b4a.alloc(32)\n sodium.crypto_generichash(hash, req.value)\n if (!b4a.equals(hash, req.target)) return\n\n const k = b4a.toString(hash, 'hex')\n this.immutables.set(k, unslab(req.value))\n\n req.reply(null)\n }\n\n destroy () {\n this.records.destroy()\n this.refreshes.destroy()\n this.mutables.destroy()\n this.immutables.destroy()\n }\n\n static signMutable (seq, value, keyPair) {\n const signable = b4a.allocUnsafe(32 + 32)\n const hash = signable.subarray(32)\n\n signable.set(NS.MUTABLE_PUT, 0)\n\n sodium.crypto_generichash(hash, c.encode(m.mutableSignable, { seq, value }))\n return sign(signable, keyPair)\n }\n\n static verifyMutable (signature, seq, value, publicKey) {\n return verifyMutable(signature, seq, value, publicKey)\n }\n\n static signAnnounce (target, token, id, ann, keyPair) {\n return sign(annSignable(target, token, id, ann, NS.ANNOUNCE), keyPair)\n }\n\n static signUnannounce (target, token, id, ann, keyPair) {\n return sign(annSignable(target, token, id, ann, NS.UNANNOUNCE), keyPair)\n }\n}\n\nfunction verifyMutable (signature, seq, value, publicKey) {\n const signable = b4a.allocUnsafe(32 + 32)\n const hash = signable.subarray(32)\n\n signable.set(NS.MUTABLE_PUT, 0)\n\n sodium.crypto_generichash(hash, c.encode(m.mutableSignable, { seq, value }))\n return sodium.crypto_sign_verify_detached(signature, signable, publicKey)\n}\n\nfunction annSignable (target, token, id, ann, ns) {\n const signable = b4a.allocUnsafe(32 + 32)\n const hash = signable.subarray(32)\n\n signable.set(ns, 0)\n\n sodium.crypto_generichash_batch(hash, [\n target,\n id,\n token,\n c.encode(m.peer, ann.peer), // note that this is the partial encoding of the announce message so we could just use that for perf\n ann.refresh || EMPTY\n ])\n\n return signable\n}\n\nfunction sign (signable, keyPair) {\n if (keyPair.sign) {\n return keyPair.sign(signable)\n }\n const secretKey = keyPair.secretKey ? keyPair.secretKey : keyPair\n const signature = b4a.allocUnsafe(64)\n sodium.crypto_sign_detached(signature, signable, secretKey)\n return signature\n}\n\nfunction decode (enc, val) {\n try {\n return val && c.decode(enc, val)\n } catch (err) {\n return null\n }\n}\nmodule.exports = class RawStreamSet {\n constructor (dht) {\n this._dht = dht\n\n this._prefix = 16 - 1 // 16 is the default stream-set side in udx\n this._streams = new Map()\n }\n\n add (opts) {\n const self = this\n\n // TODO: we should prob have a udx helper for id generation, given the slight complexity\n // of the below. requires a PRNG in udx tho.\n\n let id = 0\n\n while (true) {\n id = (Math.random() * 0x100000000) >>> 0\n\n if (this._streams.has(id & this._prefix)) continue\n break\n }\n\n // always have ~50% change of rolling a free one\n if (2 * this._streams.size >= this._prefix) {\n // ie 0b11111 = 0b1111 + 1 + 0b1111\n this._prefix = 2 * this._prefix + 1\n\n // move the prefixes over\n const next = new Map()\n for (const stream of this._streams.values()) {\n next.set(stream.id & this._prefix, stream)\n }\n this._streams = next\n }\n\n const stream = this._dht.udx.createStream(id, opts)\n this._streams.set(id & this._prefix, stream)\n\n stream.on('close', onclose)\n\n return stream\n\n function onclose () {\n self._streams.delete(id & self._prefix)\n }\n }\n\n async clear () {\n const destroying = []\n\n for (const stream of this._streams.values()) {\n destroying.push(new Promise((resolve) => stream\n .once('close', resolve)\n .destroy()\n ))\n }\n\n await Promise.allSettled(destroying)\n }\n}\nconst c = require('compact-encoding')\nconst Cache = require('xache')\nconst safetyCatch = require('safety-catch')\nconst b4a = require('b4a')\nconst { handshake, holepunch } = require('./messages')\nconst { COMMANDS } = require('./constants')\nconst { BAD_HANDSHAKE_REPLY, BAD_HOLEPUNCH_REPLY } = require('./errors')\n\nconst FROM_CLIENT = 0\nconst FROM_SERVER = 1\nconst FROM_RELAY = 2\nconst FROM_SECOND_RELAY = 3\nconst REPLY = 4\n\n// TODO: While the current design is very trustless in regards to clients/servers trusting the DHT,\n// we should add a bunch of rate limits everywhere, especially including here to avoid bad users\n// using a DHT node to relay traffic indiscriminately using the connect/holepunch messages.\n// That's mostly from an abuse POV as none of the messsages do amplication.\n\nmodule.exports = class Router {\n constructor (dht, opts) {\n this.dht = dht\n this.forwards = new Cache(opts.forwards)\n }\n\n set (target, state) {\n if (state.onpeerhandshake) {\n this.forwards.retain(toString(target), state)\n } else {\n this.forwards.set(toString(target), state)\n }\n }\n\n get (target) {\n return this.forwards.get(toString(target))\n }\n\n delete (target) {\n this.forwards.delete(toString(target))\n }\n\n destroy () {\n this.forwards.destroy()\n }\n\n async peerHandshake (target, { noise, peerAddress, relayAddress, socket, session }, to) {\n const dht = this.dht\n\n const requestValue = c.encode(handshake, {\n mode: FROM_CLIENT,\n noise,\n peerAddress,\n relayAddress\n })\n\n const res = await dht.request({ command: COMMANDS.PEER_HANDSHAKE, target, value: requestValue }, to, { socket, session })\n\n const hs = decode(handshake, res.value)\n if (!hs || hs.mode !== REPLY || (to.host !== res.from.host || to.port !== res.from.port) || !hs.noise) {\n throw BAD_HANDSHAKE_REPLY()\n }\n\n return {\n noise: hs.noise,\n relayed: !!hs.peerAddress,\n serverAddress: hs.peerAddress || to,\n clientAddress: res.to\n }\n }\n\n async onpeerhandshake (req) {\n const hs = req.value && decode(handshake, req.value)\n if (!hs) return\n\n const { mode, noise, peerAddress, relayAddress } = hs\n\n const state = req.target && this.get(req.target)\n const isServer = !!(state && state.onpeerhandshake)\n const relay = state && state.relay\n\n if (isServer) {\n let reply = null\n try {\n reply = noise && await state.onpeerhandshake({ noise, peerAddress }, req)\n } catch (e) {\n safetyCatch(e)\n return\n }\n if (!reply || !reply.noise) return\n const opts = { socket: reply.socket, closerNodes: false, token: false }\n\n switch (mode) {\n case FROM_CLIENT: {\n req.reply(c.encode(handshake, { mode: REPLY, noise: reply.noise, peerAddress: null }), opts)\n return\n }\n case FROM_RELAY: {\n req.relay(c.encode(handshake, { mode: FROM_SERVER, noise: reply.noise, peerAddress }), req.from, opts)\n return\n }\n case FROM_SECOND_RELAY: {\n if (!relayAddress) return\n req.relay(c.encode(handshake, { mode: FROM_SERVER, noise: reply.noise, peerAddress }), relayAddress, opts)\n return // eslint-disable-line\n }\n }\n } else {\n switch (mode) {\n case FROM_CLIENT: {\n // TODO: if no relay is known route closer to the target instead of timing out\n if (!noise) return\n if (!relay && !relayAddress) { // help the user route\n req.reply(null, { token: false, closerNodes: true })\n return\n }\n req.relay(c.encode(handshake, { mode: FROM_RELAY, noise, peerAddress: req.from, relayAddress: null }), relayAddress || relay)\n return\n }\n case FROM_RELAY: {\n if (!relay || !noise) return\n req.relay(c.encode(handshake, { mode: FROM_SECOND_RELAY, noise, peerAddress, relayAddress: req.from }), relay)\n return\n }\n case FROM_SERVER: {\n if (!peerAddress || !noise) return\n req.reply(c.encode(handshake, { mode: REPLY, noise, peerAddress: req.from, relayAddress: null }), { to: peerAddress, closerNodes: false, token: false })\n return // eslint-disable-line\n }\n }\n }\n }\n\n async peerHolepunch (target, { id, payload, peerAddress, socket, session }, to) {\n const dht = this.dht\n const requestValue = c.encode(holepunch, {\n mode: FROM_CLIENT,\n id,\n payload,\n peerAddress\n })\n\n const res = await dht.request({ command: COMMANDS.PEER_HOLEPUNCH, target, value: requestValue }, to, { socket, session })\n\n const hp = decode(holepunch, res.value)\n if (!hp || hp.mode !== REPLY || (to.host !== res.from.host || to.port !== res.from.port)) {\n throw BAD_HOLEPUNCH_REPLY()\n }\n\n return {\n from: res.from,\n to: res.to,\n payload: hp.payload,\n peerAddress: hp.peerAddress || to\n }\n }\n\n async onpeerholepunch (req) {\n const hp = req.value && decode(holepunch, req.value)\n if (!hp) return\n\n const { mode, id, payload, peerAddress } = hp\n\n const state = req.target && this.get(req.target)\n const isServer = !!(state && state.onpeerholepunch)\n const relay = state && state.relay\n\n switch (mode) {\n case FROM_CLIENT: {\n if (!peerAddress && !relay) return\n req.relay(c.encode(holepunch, { mode: FROM_RELAY, id, payload, peerAddress: req.from }), peerAddress || relay)\n return\n }\n case FROM_RELAY: {\n if (!isServer || !peerAddress) return\n let reply = null\n try {\n reply = await state.onpeerholepunch({ id, payload, peerAddress }, req)\n } catch (e) {\n safetyCatch(e)\n return\n }\n if (!reply) return\n const opts = { socket: reply.socket, closerNodes: false, token: false }\n req.relay(c.encode(holepunch, { mode: FROM_SERVER, id: 0, payload: reply.payload, peerAddress }), req.from, opts)\n return\n }\n case FROM_SERVER: {\n req.reply(c.encode(holepunch, { mode: REPLY, id, payload, peerAddress: req.from }), { to: peerAddress, closerNodes: false, token: false })\n return // eslint-disable-line\n }\n }\n }\n}\n\nfunction decode (enc, val) {\n try {\n return c.decode(enc, val)\n } catch {\n return null\n }\n}\n\nfunction toString (t) {\n return typeof t === 'string' ? t : b4a.toString(t, 'hex')\n}\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\nconst { holepunchPayload } = require('./messages')\n\nmodule.exports = class HolepunchPayload {\n constructor (holepunchSecret) {\n this._sharedSecret = holepunchSecret\n this._localSecret = b4a.allocUnsafe(32)\n\n sodium.randombytes_buf(this._localSecret)\n }\n\n decrypt (buffer) {\n const state = { start: 24, end: buffer.byteLength - 16, buffer }\n\n if (state.end <= state.start) return null\n\n const nonce = buffer.subarray(0, 24)\n const msg = state.buffer.subarray(state.start, state.end)\n const cipher = state.buffer.subarray(state.start)\n\n if (!sodium.crypto_secretbox_open_easy(msg, cipher, nonce, this._sharedSecret)) return null\n\n try {\n return holepunchPayload.decode(state)\n } catch {\n return null\n }\n }\n\n encrypt (payload) {\n const state = { start: 24, end: 24, buffer: null }\n holepunchPayload.preencode(state, payload)\n state.buffer = b4a.allocUnsafe(state.end + 16)\n\n const nonce = state.buffer.subarray(0, 24)\n const msg = state.buffer.subarray(state.start, state.end)\n const cipher = state.buffer.subarray(state.start)\n\n holepunchPayload.encode(state, payload)\n sodium.randombytes_buf(nonce)\n sodium.crypto_secretbox_easy(cipher, msg, nonce, this._sharedSecret)\n\n return state.buffer\n }\n\n token (addr) {\n const out = b4a.allocUnsafe(32)\n sodium.crypto_generichash(out, b4a.from(addr.host), this._localSecret)\n return out\n }\n}\nconst DONE = Promise.resolve(true)\nconst DESTROYED = Promise.resolve(false)\n\nmodule.exports = class Semaphore {\n constructor (limit = 1) {\n this.limit = limit\n this.active = 0\n this.waiting = []\n this.destroyed = false\n\n this._onwait = (resolve) => { this.waiting.push(resolve) }\n }\n\n wait () {\n if (this.destroyed === true) return DESTROYED\n\n if (this.active < this.limit && this.waiting.length === 0) {\n this.active++\n return DONE\n }\n\n return new Promise(this._onwait)\n }\n\n signal () {\n if (this.destroyed === true) return\n\n this.active--\n while (this.active < this.limit && this.waiting.length > 0 && this.destroyed === false) {\n this.active++\n this.waiting.shift()(true)\n }\n }\n\n async flush () {\n if (this.destroyed === true) return\n this.limit = 1\n await this.wait()\n this.signal()\n }\n\n destroy () {\n this.destroyed = true\n this.active = 0\n while (this.waiting.length) this.waiting.pop()(false)\n }\n}\nconst { EventEmitter } = require('events')\nconst safetyCatch = require('safety-catch')\nconst NoiseSecretStream = require('@hyperswarm/secret-stream')\nconst b4a = require('b4a')\nconst relay = require('blind-relay')\nconst NoiseWrap = require('./noise-wrap')\nconst Announcer = require('./announcer')\nconst { FIREWALL, ERROR } = require('./constants')\nconst { unslabbedHash } = require('./crypto')\nconst SecurePayload = require('./secure-payload')\nconst Holepuncher = require('./holepuncher')\nconst { isPrivate } = require('bogon')\nconst { ALREADY_LISTENING, NODE_DESTROYED, KEYPAIR_ALREADY_USED } = require('./errors')\n\nconst HANDSHAKE_CLEAR_WAIT = 10000\nconst HANDSHAKE_INITIAL_TIMEOUT = 10000\n\nmodule.exports = class Server extends EventEmitter {\n constructor (dht, opts = {}) {\n super()\n\n this.dht = dht\n this.target = null\n\n this.closed = false\n this.firewall = opts.firewall || (() => false)\n this.holepunch = opts.holepunch || (() => true)\n this.relayThrough = opts.relayThrough || null\n this.relayKeepAlive = opts.relayKeepAlive || 5000\n this.pool = opts.pool || null\n this.createHandshake = opts.createHandshake || defaultCreateHandshake\n this.createSecretStream = opts.createSecretStream || defaultCreateSecretStream\n this.suspended = false\n\n this._shareLocalAddress = opts.shareLocalAddress !== false\n this._reusableSocket = !!opts.reusableSocket\n this._neverPunch = opts.holepunch === false // useful for fully disabling punching\n this._keyPair = null\n this._announcer = null\n this._connects = new Map()\n this._holepunches = []\n this._listening = null\n this._closing = null\n }\n\n get listening () {\n return this._listening !== null\n }\n\n get publicKey () {\n return this._keyPair && this._keyPair.publicKey\n }\n\n get relayAddresses () {\n return this._announcer ? this._announcer.relayAddresses : []\n }\n\n onconnection (encryptedSocket) {\n this.emit('connection', encryptedSocket)\n }\n\n async suspend ({ log = noop } = {}) {\n log('Suspending hyperdht server')\n if (this._listening !== null) await this._listening\n log('Suspending hyperdht server (post listening)')\n this.suspended = true\n this._clearAll()\n return this._announcer ? this._announcer.suspend({ log }) : Promise.resolve()\n }\n\n async resume () {\n if (this._listening !== null) await this._listening\n this.suspended = false\n return this._announcer ? this._announcer.resume() : Promise.resolve()\n }\n\n address () {\n if (!this._keyPair) return null\n\n return {\n publicKey: this._keyPair.publicKey,\n host: this.dht.host,\n port: this.dht.port\n }\n }\n\n close () {\n if (this._closing) return this._closing\n this._closing = this._close()\n return this._closing\n }\n\n _gc () {\n this.dht.listening.delete(this)\n if (this.target) this.dht._router.delete(this.target)\n }\n\n async _stopListening () {\n try {\n if (this._announcer) await this._announcer.stop()\n } catch {\n // ignore\n }\n\n this._announcer = null\n this._listening = null\n this._keyPair = null\n }\n\n async _close () {\n if (this._listening === null) {\n this.closed = true\n this.emit('close')\n return\n }\n\n try {\n await this._listening\n } catch {}\n\n this._gc()\n this._clearAll()\n\n await this._stopListening()\n\n this.closed = true\n this.emit('close')\n }\n\n _clearAll () {\n while (this._holepunches.length > 0) {\n const h = this._holepunches.pop()\n if (h && h.puncher) h.puncher.destroy()\n if (h && h.clearing) clearTimeout(h.clearing)\n if (h && h.prepunching) clearTimeout(h.prepunching)\n if (h && h.rawStream) h.rawStream.destroy()\n }\n\n this._connects.clear()\n }\n\n async listen (keyPair = this.dht.defaultKeyPair, opts = {}) {\n if (this._listening !== null) throw ALREADY_LISTENING()\n if (this.dht.destroyed) throw NODE_DESTROYED()\n\n this._listening = this._listen(keyPair, opts)\n await this._listening\n return this\n }\n\n async _listen (keyPair, opts) {\n // From now on, the DHT object which created me is responsible for closing me\n this.dht.listening.add(this)\n\n try {\n await this.dht.bind()\n if (this._closing) return\n\n for (const s of this.dht.listening) {\n if (s._keyPair && b4a.equals(s._keyPair.publicKey, keyPair.publicKey)) {\n throw KEYPAIR_ALREADY_USED()\n }\n }\n\n this.target = unslabbedHash(keyPair.publicKey)\n this._keyPair = keyPair\n this._announcer = new Announcer(this.dht, keyPair, this.target, opts)\n\n this.dht._router.set(this.target, {\n relay: null,\n record: this._announcer.record,\n onpeerhandshake: this._onpeerhandshake.bind(this),\n onpeerholepunch: this._onpeerholepunch.bind(this)\n })\n\n // warm it up for now\n this._localAddresses().catch(safetyCatch)\n\n await this._announcer.start()\n } catch (err) {\n await this._stopListening()\n this._gc()\n throw err\n }\n\n if (this._closing) return\n if (this.suspended) await this._announcer.suspend()\n\n if (this._closing) return\n if (this.dht.destroyed) throw NODE_DESTROYED()\n\n if (this.pool) this.pool._attachServer(this)\n\n this.emit('listening')\n }\n\n refresh () {\n if (this._announcer) this._announcer.refresh()\n }\n\n notifyOnline () {\n if (this._announcer) this._announcer.online.notify()\n }\n\n _localAddresses () {\n return this.dht.validateLocalAddresses(Holepuncher.localAddresses(this.dht.io.serverSocket))\n }\n\n async _addHandshake (k, noise, clientAddress, { from, to: serverAddress, socket }, direct) {\n let id = this._holepunches.indexOf(null)\n if (id === -1) id = this._holepunches.push(null) - 1\n\n const hs = {\n round: 0,\n reply: null,\n puncher: null,\n payload: null,\n rawStream: null,\n encryptedSocket: null,\n prepunching: null,\n firewalled: true,\n clearing: null,\n onsocket: null,\n\n // Relay state\n relayTimeout: null,\n relayToken: null,\n relaySocket: null,\n relayClient: null,\n relayPaired: false\n }\n\n this._holepunches[id] = hs\n\n const handshake = this.createHandshake(this._keyPair, null)\n\n let remotePayload\n try {\n remotePayload = await handshake.recv(noise)\n } catch (err) {\n safetyCatch(err)\n this._clearLater(hs, id, k)\n return null\n }\n\n if (this._closing || this.suspended) return null\n\n try {\n hs.firewalled = await this.firewall(handshake.remotePublicKey, remotePayload, clientAddress)\n } catch (err) {\n safetyCatch(err)\n }\n\n if (this._closing || this.suspended) return null\n\n if (hs.firewalled) {\n this._clearLater(hs, id, k)\n return null\n }\n\n const error = remotePayload.version === 1\n ? (remotePayload.udx ? ERROR.NONE : ERROR.ABORTED)\n : ERROR.VERSION_MISMATCH\n\n const addresses = []\n const ourRemoteAddr = this.dht.remoteAddress()\n const ourLocalAddrs = this._shareLocalAddress ? await this._localAddresses() : null\n\n if (this._closing || this.suspended) return null\n\n if (ourRemoteAddr) addresses.push(ourRemoteAddr)\n if (ourLocalAddrs) addresses.push(...ourLocalAddrs)\n\n if (error === ERROR.NONE) {\n hs.rawStream = this.dht.createRawStream({\n framed: true,\n firewall (socket, port, host) {\n // Check if the traffic originated from the socket on which we're expecting relay traffic. If so,\n // we haven't hole punched yet and the other side is just sending us traffic through the relay.\n if (hs.relaySocket && isRelay(hs.relaySocket, socket, port, host)) {\n return false\n }\n\n hs.onsocket(socket, port, host)\n return false\n }\n })\n\n hs.rawStream.on('error', autoDestroy)\n\n hs.onsocket = (socket, port, host) => {\n if (hs.rawStream === null) return // Already hole punched\n\n this._clearLater(hs, id, k)\n\n if (hs.prepunching) {\n clearTimeout(hs.prepunching)\n hs.prepunching = null\n }\n\n if (this._reusableSocket && remotePayload.udx.reusableSocket) {\n this.dht._socketPool.routes.add(handshake.remotePublicKey, hs.rawStream)\n }\n\n hs.rawStream.removeListener('error', autoDestroy)\n\n if (hs.rawStream.connected) {\n const remoteChanging = hs.rawStream.changeRemote(socket, remotePayload.udx.id, port, host)\n\n if (remoteChanging) remoteChanging.catch(safetyCatch)\n } else {\n hs.rawStream.connect(socket, remotePayload.udx.id, port, host)\n hs.encryptedSocket = this.createSecretStream(false, hs.rawStream, {\n handshake: h,\n keepAlive: this.dht.connectionKeepAlive\n })\n\n this.onconnection(hs.encryptedSocket)\n }\n\n if (hs.puncher) {\n hs.puncher.onabort = noop\n hs.puncher.destroy()\n }\n\n hs.rawStream = null\n }\n\n function autoDestroy () {\n if (hs.puncher) hs.puncher.destroy()\n }\n }\n\n const relayThrough = selectRelay(this.relayThrough)\n\n if (relayThrough) hs.relayToken = relay.token()\n\n try {\n hs.reply = await handshake.send({\n error,\n firewall: ourRemoteAddr ? FIREWALL.OPEN : FIREWALL.UNKNOWN,\n holepunch: ourRemoteAddr ? null : { id, relays: this._announcer.relays },\n addresses4: addresses,\n addresses6: null,\n udx: {\n reusableSocket: this._reusableSocket,\n id: hs.rawStream ? hs.rawStream.id : 0,\n seq: 0\n },\n secretStream: {},\n relayThrough: relayThrough\n ? { publicKey: relayThrough, token: hs.relayToken }\n : null\n })\n } catch (err) {\n safetyCatch(err)\n hs.rawStream.destroy()\n this._clearLater(hs, id, k)\n return null\n }\n\n if (this._closing || this.suspended) {\n hs.rawStream.destroy()\n return null\n }\n\n const h = handshake.final()\n\n if (error !== ERROR.NONE) {\n hs.rawStream.destroy()\n this._clearLater(hs, id, k)\n return hs\n }\n\n if (relayThrough || remotePayload.relayThrough) {\n this._relayConnection(hs, relayThrough, remotePayload, h)\n }\n\n if (remotePayload.firewall === FIREWALL.OPEN || direct) {\n const sock = direct ? socket : this.dht.socket\n this.dht.stats.punches.open++\n hs.onsocket(sock, clientAddress.port, clientAddress.host)\n return hs\n }\n\n const onabort = () => {\n if (hs.prepunching) clearTimeout(hs.prepunching)\n hs.prepunching = null\n hs.rawStream.on('close', () => this._clearLater(hs, id, k))\n if (hs.relayToken === null) hs.rawStream.destroy()\n }\n\n if (!direct && clientAddress.host === serverAddress.host) {\n const clientAddresses = remotePayload.addresses4.filter(onlyPrivateHosts)\n\n if (clientAddresses.length > 0 && this._shareLocalAddress) {\n const myAddresses = await this._localAddresses()\n const addr = Holepuncher.matchAddress(myAddresses, clientAddresses)\n\n if (addr) {\n hs.prepunching = setTimeout(onabort, HANDSHAKE_INITIAL_TIMEOUT)\n return hs\n }\n }\n }\n\n if (this._closing || this.suspended) return null\n\n if (ourRemoteAddr || this._neverPunch) {\n hs.prepunching = setTimeout(onabort, HANDSHAKE_INITIAL_TIMEOUT)\n return hs\n }\n\n hs.payload = new SecurePayload(h.holepunchSecret)\n hs.puncher = new Holepuncher(this.dht, this.dht.session(), false, remotePayload.firewall)\n\n hs.puncher.onconnect = hs.onsocket\n hs.puncher.onabort = onabort\n hs.prepunching = setTimeout(hs.puncher.destroy.bind(hs.puncher), HANDSHAKE_INITIAL_TIMEOUT)\n\n return hs\n }\n\n _clearLater (hs, id, k) {\n if (hs.clearing) return\n hs.clearing = setTimeout(() => this._clear(hs, id, k), HANDSHAKE_CLEAR_WAIT)\n }\n\n _clear (hs, id, k) {\n if (id >= this._holepunches.length || this._holepunches[id] !== hs) return\n if (hs.clearing) clearTimeout(hs.clearing)\n\n this._holepunches[id] = null\n while (this._holepunches.length > 0 && this._holepunches[this._holepunches.length - 1] === null) {\n this._holepunches.pop()\n }\n this._connects.delete(k)\n }\n\n async _onpeerhandshake ({ noise, peerAddress }, req) {\n const k = b4a.toString(noise, 'hex')\n\n // The next couple of statements MUST run within the same tick to prevent\n // a malicious peer from flooding us with handshakes.\n let p = this._connects.get(k)\n if (!p) {\n p = this._addHandshake(k, noise, peerAddress || req.from, req, !peerAddress)\n this._connects.set(k, p)\n }\n\n const h = await p\n if (!h) return null\n\n if (this._closing !== null || this.suspended) return null\n\n return { socket: h.puncher && h.puncher.socket, noise: h.reply }\n }\n\n async _onpeerholepunch ({ id, peerAddress, payload }, req) {\n const h = id < this._holepunches.length ? this._holepunches[id] : null\n if (!h) return null\n\n if (!peerAddress || this._closing !== null || this.suspended) return null\n\n const p = h.puncher\n if (!p || !p.socket) return this._abort(h) // not opened\n\n const remotePayload = h.payload.decrypt(payload)\n if (!remotePayload) return null\n\n const isServerRelay = this._announcer.isRelay(req.from)\n const { error, firewall, round, punching, addresses, remoteAddress, remoteToken } = remotePayload\n\n if (error !== ERROR.NONE) {\n // We actually do not need to set the round here, but just do it for consistency.\n if (round >= h.round) h.round = round\n return this._abort(h)\n }\n\n const token = h.payload.token(peerAddress)\n const echoed = isServerRelay && !!remoteToken && b4a.equals(token, remoteToken)\n\n // Update our heuristics here\n if (req.socket === p.socket) {\n p.nat.add(req.to, req.from)\n }\n\n if (round >= h.round) {\n h.round = round\n p.updateRemote({ punching, firewall, addresses, verified: echoed ? peerAddress.host : null })\n }\n\n // Wait for the analyzer to reach a conclusion...\n let stable = await p.analyze(false)\n if (p.destroyed) return null\n\n if (!p.remoteHolepunching && !stable) {\n stable = await p.analyze(true)\n if (p.destroyed) return null\n if (!stable) return this._abort(h)\n }\n\n // Fast mode! If we are consistent and the remote has opened a session to us (remoteAddress)\n // then fire a quick punch back. Note the await here just waits for the udp socket to flush.\n if (isConsistent(p.nat.firewall) && remoteAddress && hasSameAddr(p.nat.addresses, remoteAddress)) {\n await p.ping(peerAddress)\n if (p.destroyed) return null\n }\n\n // Remote said they are punching (or willing to), so we will punch as well.\n // Note that this returns when the punching has STARTED, so no guarantee\n // we will have a connection after this promise etc.\n if (p.remoteHolepunching) {\n // TODO: still continue here if a local connection might work, but then do not holepunch...\n if (!this.holepunch(p.remoteFirewall, p.nat.firewall, p.remoteAddresses, p.nat.addresses)) {\n return p.destroyed ? null : this._abort(h)\n }\n\n if (h.prepunching) {\n clearTimeout(h.prepunching)\n h.prepunching = null\n }\n\n if (p.remoteFirewall >= FIREWALL.RANDOM || p.nat.firewall >= FIREWALL.RANDOM) {\n if (this.dht._randomPunches >= this.dht._randomPunchLimit || (Date.now() - this.dht._lastRandomPunch) < this.dht._randomPunchInterval) {\n if (!h.relayToken) return this._abort(h, ERROR.TRY_LATER)\n return {\n socket: p.socket,\n payload: h.payload.encrypt({\n error: ERROR.TRY_LATER,\n firewall: p.nat.firewall,\n round: h.round,\n connected: p.connected,\n punching: p.punching,\n addresses: p.nat.addresses,\n remoteAddress: null,\n token: isServerRelay ? token : null,\n remoteToken: remotePayload.token\n })\n }\n }\n }\n\n const punching = await p.punch()\n if (p.destroyed) return null\n if (!punching) return this._abort(h)\n }\n\n // Freeze that analysis as soon as we have a result we are giving to the other peer\n if (p.nat.firewall !== FIREWALL.UNKNOWN) {\n p.nat.freeze()\n }\n\n return {\n socket: p.socket,\n payload: h.payload.encrypt({\n error: ERROR.NONE,\n firewall: p.nat.firewall,\n round: h.round,\n connected: p.connected,\n punching: p.punching,\n addresses: p.nat.addresses,\n remoteAddress: null,\n token: isServerRelay ? token : null,\n remoteToken: remotePayload.token\n })\n }\n }\n\n _abort (h, error = ERROR.ABORTED) {\n if (!h.payload) {\n if (h.puncher) h.puncher.destroy()\n return null\n }\n\n const payload = h.payload.encrypt({\n error,\n firewall: FIREWALL.UNKNOWN,\n round: h.round,\n connected: false,\n punching: false,\n addresses: null,\n remoteAddress: null,\n token: null,\n remoteToken: null\n })\n\n h.puncher.destroy()\n\n return { socket: this.dht.socket, payload }\n }\n\n _relayConnection (hs, relayThrough, remotePayload, h) {\n let isInitiator\n let publicKey\n let token\n\n if (relayThrough) {\n isInitiator = true\n publicKey = relayThrough\n token = hs.relayToken\n } else {\n isInitiator = false\n publicKey = remotePayload.relayThrough.publicKey\n token = remotePayload.relayThrough.token\n }\n\n hs.relayToken = token\n hs.relaySocket = this.dht.connect(publicKey)\n hs.relaySocket.setKeepAlive(this.relayKeepAlive)\n hs.relayClient = relay.Client.from(hs.relaySocket, { id: hs.relaySocket.publicKey })\n hs.relayTimeout = setTimeout(onabort, 15000)\n\n hs.relayClient\n .pair(isInitiator, token, hs.rawStream)\n .on('error', onabort)\n .on('data', (remoteId) => {\n if (hs.relayTimeout) clearRelayTimeout(hs)\n if (hs.rawStream === null) {\n onabort(null)\n return\n }\n\n hs.relayPaired = true\n\n if (hs.prepunching) clearTimeout(hs.prepunching)\n hs.prepunching = null\n\n const {\n remotePort,\n remoteHost,\n socket\n } = hs.relaySocket.rawStream\n\n hs.rawStream\n .on('close', () => hs.relaySocket.destroy())\n .connect(socket, remoteId, remotePort, remoteHost)\n\n hs.encryptedSocket = this.createSecretStream(false, hs.rawStream, { handshake: h })\n\n this.onconnection(hs.encryptedSocket)\n })\n\n function onabort () {\n if (hs.relayTimeout) clearRelayTimeout(hs)\n const socket = hs.relaySocket\n hs.relayToken = null\n hs.relaySocket = null\n if (socket) socket.destroy()\n }\n }\n}\n\nfunction clearRelayTimeout (hs) {\n clearTimeout(hs.relayTimeout)\n hs.relayTimeout = null\n}\n\nfunction isConsistent (fw) {\n return fw === FIREWALL.OPEN || fw === FIREWALL.CONSISTENT\n}\n\nfunction hasSameAddr (addrs, other) {\n if (addrs === null) return false\n\n for (const addr of addrs) {\n if (addr.port === other.port && addr.host === other.host) return true\n }\n return false\n}\n\nfunction defaultCreateHandshake (keyPair, remotePublicKey) {\n return new NoiseWrap(keyPair, remotePublicKey)\n}\n\nfunction defaultCreateSecretStream (isInitiator, rawStream, opts) {\n return new NoiseSecretStream(isInitiator, rawStream, opts)\n}\n\nfunction onlyPrivateHosts (addr) {\n return isPrivate(addr.host)\n}\n\nfunction isRelay (relaySocket, socket, port, host) {\n const stream = relaySocket.rawStream\n if (!stream) return false\n if (stream.socket !== socket) return false\n return port === stream.remotePort && host === stream.remoteHost\n}\n\nfunction selectRelay (relayThrough) {\n if (typeof relayThrough === 'function') relayThrough = relayThrough()\n if (relayThrough === null) return null\n if (Array.isArray(relayThrough)) return relayThrough[Math.floor(Math.random() * relayThrough.length)]\n return relayThrough\n}\n\nfunction noop () {}\nmodule.exports = class Sleeper {\n constructor () {\n this._timeout = null\n this._resolve = null\n\n this._start = (resolve) => {\n this._resolve = resolve\n }\n\n this._trigger = () => {\n if (this._resolve === null) return\n const resolve = this._resolve\n this._timeout = null\n this._resolve = null\n resolve()\n }\n }\n\n pause (ms) {\n const p = new Promise(this._start)\n if (this._timeout !== null) {\n clearTimeout(this._timeout)\n this._trigger()\n }\n this._timeout = setTimeout(this._trigger, ms)\n return p\n }\n\n resume () {\n if (this._timeout !== null) {\n clearTimeout(this._timeout)\n this._trigger()\n }\n }\n}\nconst b4a = require('b4a')\n\nconst LINGER_TIME = 3000\n\nmodule.exports = class SocketPool {\n constructor (dht, host) {\n this._dht = dht\n this._sockets = new Map()\n this._lingering = new Set() // updated by the ref\n this._host = host\n\n this.routes = new SocketRoutes(this)\n }\n\n _onmessage (ref, data, address) {\n this._dht.onmessage(ref.socket, data, address)\n }\n\n _add (ref) {\n this._sockets.set(ref.socket, ref)\n }\n\n _remove (ref) {\n this._sockets.delete(ref.socket)\n this._lingering.delete(ref)\n }\n\n lookup (socket) {\n return this._sockets.get(socket) || null\n }\n\n setReusable (socket, bool) {\n const ref = this.lookup(socket)\n if (ref) ref.reusable = bool\n }\n\n acquire () {\n // TODO: Enable socket reuse\n return new SocketRef(this)\n }\n\n async destroy () {\n const closing = []\n\n for (const ref of this._sockets.values()) {\n ref._unlinger()\n closing.push(ref.socket.close())\n }\n\n await Promise.allSettled(closing)\n }\n}\n\nclass SocketRoutes {\n constructor (pool) {\n this._pool = pool\n this._routes = new Map()\n }\n\n add (publicKey, rawStream) {\n if (rawStream.socket) this._onconnect(publicKey, rawStream)\n else rawStream.on('connect', this._onconnect.bind(this, publicKey, rawStream))\n }\n\n get (publicKey) {\n const id = b4a.toString(publicKey, 'hex')\n const route = this._routes.get(id)\n if (!route) return null\n return route\n }\n\n _onconnect (publicKey, rawStream) {\n const id = b4a.toString(publicKey, 'hex')\n const socket = rawStream.socket\n\n let route = this._routes.get(id)\n\n if (!route) {\n const gc = () => {\n if (this._routes.get(id) === route) this._routes.delete(id)\n socket.removeListener('close', gc)\n }\n\n route = {\n socket,\n address: { host: rawStream.remoteHost, port: rawStream.remotePort },\n gc\n }\n\n this._routes.set(id, route)\n socket.on('close', gc)\n }\n\n this._pool.setReusable(socket, true)\n\n rawStream.on('error', () => {\n this._pool.setReusable(socket, false)\n if (!route) route = this._routes.get(id)\n if (route && route.socket === socket) route.gc()\n })\n }\n}\n\n// TODO: we should just make some \"user data\" object on udx to allow to attach this info\nclass SocketRef {\n constructor (pool) {\n this._pool = pool\n\n // Events\n this.onholepunchmessage = noop\n\n // Whether it should teardown immediately or wait a bit\n this.reusable = false\n\n this.socket = pool._dht.udx.createSocket()\n this.socket\n .on('close', this._onclose.bind(this))\n .on('message', this._onmessage.bind(this))\n .on('idle', this._onidle.bind(this))\n .on('busy', this._onbusy.bind(this))\n .bind(0, this._pool._host)\n\n this._refs = 1\n this._released = false\n this._closed = false\n\n this._timeout = null\n this._wasBusy = false\n\n this._pool._add(this)\n }\n\n _onclose () {\n this._pool._remove(this)\n }\n\n _onmessage (data, address) {\n if (data.byteLength > 1) {\n this._pool._onmessage(this, data, address)\n } else {\n this.onholepunchmessage(data, address, this)\n }\n }\n\n _onidle () {\n this._closeMaybe()\n }\n\n _onbusy () {\n this._wasBusy = true\n this._unlinger()\n }\n\n _reset () {\n this.onholepunchmessage = noop\n }\n\n _closeMaybe () {\n if (this._refs === 0 && this.socket.idle && !this._timeout) this._close()\n }\n\n _lingeringClose () {\n this._pool._lingering.delete(this)\n this._timeout = null\n this._closeMaybe()\n }\n\n _close () {\n this._unlinger()\n\n if (this.reusable && this._wasBusy) {\n this._wasBusy = false\n this._pool._lingering.add(this)\n this._timeout = setTimeout(this._lingeringClose.bind(this), LINGER_TIME)\n return\n }\n\n this._closed = true\n this.socket.close()\n }\n\n _unlinger () {\n if (this._timeout !== null) {\n clearTimeout(this._timeout)\n this._pool._lingering.delete(this)\n this._timeout = null\n }\n }\n\n get free () {\n return this._refs === 0\n }\n\n active () {\n this._refs++\n this._unlinger()\n }\n\n inactive () {\n this._refs--\n this._closeMaybe()\n }\n\n address () {\n return this.socket.address()\n }\n\n release () {\n if (this._released) return\n\n this._released = true\n this._reset()\n\n this._refs--\n this._closeMaybe()\n }\n}\n\nfunction noop () {}\n{\n \"name\": \"hyperdht\",\n \"version\": \"6.20.5\",\n \"description\": \"The DHT powering Hyperswarm\",\n \"main\": \"index.js\",\n \"browser\": {\n \"index.js\": \"browser.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"bin\": {\n \"hyperdht\": \"./bin.js\"\n },\n \"files\": [\n \"index.js\",\n \"browser.js\",\n \"testnet.js\",\n \"bin.js\",\n \"lib/**.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n },\n \"child_process\": {\n \"bare\": \"bare-node-child-process\",\n \"default\": \"child_process\"\n }\n },\n \"dependencies\": {\n \"@hyperswarm/secret-stream\": \"^6.6.2\",\n \"b4a\": \"^1.3.1\",\n \"bare-events\": \"^2.2.0\",\n \"blind-relay\": \"^1.3.0\",\n \"bogon\": \"^1.0.0\",\n \"compact-encoding\": \"^2.4.1\",\n \"compact-encoding-net\": \"^1.0.1\",\n \"dht-rpc\": \"^6.15.1\",\n \"hypercore-crypto\": \"^3.3.0\",\n \"hypercore-id-encoding\": \"^1.2.0\",\n \"noise-curve-ed\": \"^2.0.0\",\n \"noise-handshake\": \"^4.0.0\",\n \"record-cache\": \"^1.1.1\",\n \"safety-catch\": \"^1.0.1\",\n \"signal-promise\": \"^1.0.3\",\n \"sodium-universal\": \"^5.0.1\",\n \"streamx\": \"^2.16.1\",\n \"unslab\": \"^1.3.0\",\n \"xache\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"bare-node-child-process\": \"^1.0.1\",\n \"brittle\": \"^3.0.0\",\n \"graceful-goodbye\": \"^1.3.0\",\n \"newline-decoder\": \"^1.0.2\",\n \"standard\": \"^17.1.0\"\n },\n \"scripts\": {\n \"test\": \"standard && node test/all.js\",\n \"test:bare\": \"bare test/all.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\",\n \"lint\": \"standard\",\n \"integration\": \"brittle test/integration/*.js\",\n \"end-to-end\": \"brittle test/end-to-end/*.js\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"directories\": {\n \"lib\": \"lib\",\n \"test\": \"test\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hyperdht.git\"\n },\n \"keywords\": [],\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperdht/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperdht#readme\",\n \"standard\": {\n \"ignore\": [\n \"**/*.mjs\"\n ]\n },\n \"react-native\": {\n \"index.js\": \"browser.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst { EventEmitter } = require('events')\nconst DHT = require('hyperdht')\nconst spq = require('shuffled-priority-queue')\nconst b4a = require('b4a')\nconst unslab = require('unslab')\n\nconst PeerInfo = require('./lib/peer-info')\nconst RetryTimer = require('./lib/retry-timer')\nconst ConnectionSet = require('./lib/connection-set')\nconst PeerDiscovery = require('./lib/peer-discovery')\n\nconst MAX_PEERS = 64\nconst MAX_PARALLEL = 3\nconst MAX_CLIENT_CONNECTIONS = Infinity // TODO: Change\nconst MAX_SERVER_CONNECTIONS = Infinity\n\nconst ERR_MISSING_TOPIC = 'Topic is required and must be a 32-byte buffer'\nconst ERR_DESTROYED = 'Swarm has been destroyed'\nconst ERR_DUPLICATE = 'Duplicate connection'\n\nmodule.exports = class Hyperswarm extends EventEmitter {\n constructor (opts = {}) {\n super()\n const {\n seed,\n relayThrough,\n keyPair = DHT.keyPair(seed),\n maxPeers = MAX_PEERS,\n maxClientConnections = MAX_CLIENT_CONNECTIONS,\n maxServerConnections = MAX_SERVER_CONNECTIONS,\n maxParallel = MAX_PARALLEL,\n firewall = allowAll\n } = opts\n this.keyPair = keyPair\n\n this.dht = opts.dht || new DHT({\n bootstrap: opts.bootstrap,\n nodes: opts.nodes,\n port: opts.port\n })\n this.server = this.dht.createServer({\n firewall: this._handleFirewall.bind(this),\n relayThrough: this._maybeRelayConnection.bind(this)\n }, this._handleServerConnection.bind(this))\n\n this.destroyed = false\n this.suspended = false\n this.maxPeers = maxPeers\n this.maxClientConnections = maxClientConnections\n this.maxServerConnections = maxServerConnections\n this.maxParallel = maxParallel\n this.relayThrough = relayThrough || null\n\n this.connecting = 0\n this.connections = new Set()\n this.peers = new Map()\n this.explicitPeers = new Set()\n this.listening = null\n this.stats = {\n updates: 0,\n connects: {\n client: {\n opened: 0,\n closed: 0,\n attempted: 0\n },\n server: {\n // Note: there is no notion of 'attempts' for server connections\n opened: 0,\n closed: 0\n }\n }\n }\n\n this._discovery = new Map()\n this._timer = new RetryTimer(this._requeue.bind(this), {\n backoffs: opts.backoffs,\n jitter: opts.jitter\n })\n this._queue = spq()\n\n this._allConnections = new ConnectionSet()\n this._pendingFlushes = []\n this._flushTick = 0\n\n this._drainingQueue = false\n this._clientConnections = 0\n this._serverConnections = 0\n this._firewall = firewall\n\n this.dht.on('network-change', this._handleNetworkChange.bind(this))\n this.on('update', this._handleUpdate)\n }\n\n _maybeRelayConnection (force) {\n if (!this.relayThrough) return null\n return this.relayThrough(force)\n }\n\n _enqueue (peerInfo) {\n if (peerInfo.queued) return\n peerInfo.queued = true\n peerInfo._flushTick = this._flushTick\n this._queue.add(peerInfo)\n\n this._attemptClientConnections()\n }\n\n _requeue (batch) {\n if (this.suspended) return\n for (const peerInfo of batch) {\n peerInfo.waiting = false\n\n if ((peerInfo._updatePriority() === false) || this._allConnections.has(peerInfo.publicKey) || peerInfo.queued) continue\n peerInfo.queued = true\n peerInfo._flushTick = this._flushTick\n this._queue.add(peerInfo)\n }\n\n this._attemptClientConnections()\n }\n\n _flushMaybe (peerInfo) {\n for (let i = 0; i < this._pendingFlushes.length; i++) {\n const flush = this._pendingFlushes[i]\n if (peerInfo._flushTick > flush.tick) continue\n if (--flush.missing > 0) continue\n flush.onflush(true)\n this._pendingFlushes.splice(i--, 1)\n }\n }\n\n _flushAllMaybe () {\n if (this.connecting > 0 || (this._allConnections.size < this.maxPeers && this._clientConnections < this.maxClientConnections)) {\n return false\n }\n\n while (this._pendingFlushes.length) {\n const flush = this._pendingFlushes.pop()\n flush.onflush(true)\n }\n\n return true\n }\n\n _shouldConnectExplicit () {\n return !this.destroyed &&\n !this.suspended &&\n this.connecting < this.maxParallel\n }\n\n _shouldConnect () {\n return !this.destroyed &&\n !this.suspended &&\n this.connecting < this.maxParallel &&\n this._allConnections.size < this.maxPeers &&\n this._clientConnections < this.maxClientConnections\n }\n\n _shouldRequeue (peerInfo) {\n if (this.suspended) return false\n if (peerInfo.explicit) return true\n for (const topic of peerInfo.topics) {\n if (this._discovery.has(b4a.toString(topic, 'hex')) && !this.destroyed) {\n return true\n }\n }\n return false\n }\n\n _connect (peerInfo, queued) {\n if (peerInfo.banned || this._allConnections.has(peerInfo.publicKey)) {\n if (queued) this._flushMaybe(peerInfo)\n return\n }\n\n // TODO: Support async firewalling at some point.\n if (this._handleFirewall(peerInfo.publicKey, null)) {\n peerInfo.ban(true)\n if (queued) this._flushMaybe(peerInfo)\n return\n }\n\n const relayThrough = this._maybeRelayConnection(peerInfo.forceRelaying)\n const conn = this.dht.connect(peerInfo.publicKey, {\n relayAddresses: peerInfo.relayAddresses,\n keyPair: this.keyPair,\n relayThrough\n })\n this._allConnections.add(conn)\n\n this.stats.connects.client.attempted++\n\n this.connecting++\n this._clientConnections++\n let opened = false\n\n const onerror = (err) => {\n if (this.relayThrough && shouldForceRelaying(err.code)) {\n peerInfo.forceRelaying = true\n // Reset the attempts in order to fast connect to relay\n peerInfo.attempts = 0\n }\n }\n\n // Removed once a connection is opened\n conn.on('error', onerror)\n\n conn.on('open', () => {\n opened = true\n this.stats.connects.client.opened++\n\n this._connectDone()\n this.connections.add(conn)\n conn.removeListener('error', onerror)\n peerInfo._connected()\n peerInfo.client = true\n this.emit('connection', conn, peerInfo)\n if (queued) this._flushMaybe(peerInfo)\n\n this.emit('update')\n })\n conn.on('close', () => {\n if (!opened) this._connectDone()\n this.stats.connects.client.closed++\n\n this.connections.delete(conn)\n this._allConnections.delete(conn)\n this._clientConnections--\n peerInfo._disconnected()\n\n peerInfo.waiting = this._shouldRequeue(peerInfo) && this._timer.add(peerInfo)\n this._maybeDeletePeer(peerInfo)\n\n if (!opened && queued) this._flushMaybe(peerInfo)\n\n this._attemptClientConnections()\n\n this.emit('update')\n })\n\n this.emit('update')\n }\n\n _connectDone () {\n this.connecting--\n\n if (this.connecting < this.maxParallel) this._attemptClientConnections()\n if (this.connecting === 0) this._flushAllMaybe()\n }\n\n // Called when the PeerQueue indicates a connection should be attempted.\n _attemptClientConnections () {\n // Guard against re-entries - unsure if it still needed but doesn't hurt\n if (this._drainingQueue) return\n this._drainingQueue = true\n\n for (const peerInfo of this.explicitPeers) {\n if (!this._shouldConnectExplicit()) break\n if (peerInfo.attempts >= 5 || (Date.now() - peerInfo.disconnectedTime) < peerInfo.attempts * 1000) continue\n this._connect(peerInfo, false)\n }\n\n while (this._queue.length && this._shouldConnect()) {\n const peerInfo = this._queue.shift()\n peerInfo.queued = false\n this._connect(peerInfo, true)\n }\n this._drainingQueue = false\n if (this.connecting === 0) this._flushAllMaybe()\n }\n\n _handleFirewall (remotePublicKey, payload) {\n if (this.suspended) return true\n if (b4a.equals(remotePublicKey, this.keyPair.publicKey)) return true\n\n const peerInfo = this.peers.get(b4a.toString(remotePublicKey, 'hex'))\n if (peerInfo && peerInfo.banned) return true\n\n return this._firewall(remotePublicKey, payload)\n }\n\n _handleServerConnectionSwap (existing, conn) {\n let closed = false\n\n existing.on('close', () => {\n if (closed) return\n\n conn.removeListener('error', noop)\n conn.removeListener('close', onclose)\n\n this._handleServerConnection(conn)\n })\n\n conn.on('error', noop)\n conn.on('close', onclose)\n\n function onclose () {\n closed = true\n }\n }\n\n // Called when the DHT receives a new server connection.\n _handleServerConnection (conn) {\n if (this.destroyed || this.suspended) {\n // TODO: Investigate why a final server connection can be received after close\n conn.on('error', noop)\n return conn.destroy(ERR_DESTROYED)\n }\n\n const existing = this._allConnections.get(conn.remotePublicKey)\n\n if (existing) {\n // If both connections are from the same peer,\n // - pick the new one if the existing stream is already established (has sent and received bytes),\n // because the other client must have lost that connection and be reconnecting\n // - otherwise, pick the one thats expected to initiate in a tie break\n const existingIsOutdated = existing.rawBytesRead > 0 && existing.rawBytesWritten > 0\n const expectedInitiator = b4a.compare(conn.publicKey, conn.remotePublicKey) > 0\n const keepNew = existingIsOutdated || (expectedInitiator === conn.isInitiator)\n\n if (keepNew === false) {\n existing.sendKeepAlive()\n conn.on('error', noop)\n conn.destroy(new Error(ERR_DUPLICATE))\n return\n }\n\n existing.on('error', noop)\n existing.destroy(new Error(ERR_DUPLICATE))\n this._handleServerConnectionSwap(existing, conn)\n return\n }\n\n // When reaching here, the connection will always be 'opened' next tick\n this.stats.connects.server.opened++\n\n const peerInfo = this._upsertPeer(conn.remotePublicKey, null)\n\n this.connections.add(conn)\n this._allConnections.add(conn)\n this._serverConnections++\n\n conn.on('close', () => {\n this.connections.delete(conn)\n this._allConnections.delete(conn)\n this._serverConnections--\n this.stats.connects.server.closed++\n\n this._maybeDeletePeer(peerInfo)\n\n this._attemptClientConnections()\n\n this.emit('update')\n })\n peerInfo.client = false\n this.emit('connection', conn, peerInfo)\n\n this.emit('update')\n }\n\n _upsertPeer (publicKey, relayAddresses) {\n if (b4a.equals(publicKey, this.keyPair.publicKey)) return null\n const keyString = b4a.toString(publicKey, 'hex')\n let peerInfo = this.peers.get(keyString)\n\n if (peerInfo) {\n peerInfo.relayAddresses = relayAddresses // new is always better\n return peerInfo\n }\n\n peerInfo = new PeerInfo({\n publicKey,\n relayAddresses\n })\n\n this.peers.set(keyString, peerInfo)\n return peerInfo\n }\n\n _handleUpdate () {\n this.stats.updates++\n }\n\n _maybeDeletePeer (peerInfo) {\n if (!peerInfo.shouldGC()) return\n\n const hasActiveConn = this._allConnections.has(peerInfo.publicKey)\n if (hasActiveConn) return\n\n const keyString = b4a.toString(peerInfo.publicKey, 'hex')\n this.peers.delete(keyString)\n }\n\n /*\n * Called when a peer is actively discovered during a lookup.\n *\n * Three conditions:\n * 1. Not a known peer -- insert into queue\n * 2. A known peer with normal priority -- do nothing\n * 3. A known peer with low priority -- bump priority, because it's been rediscovered\n */\n _handlePeer (peer, topic) {\n const peerInfo = this._upsertPeer(peer.publicKey, peer.relayAddresses)\n if (peerInfo) peerInfo._topic(topic)\n if (!peerInfo || this._allConnections.has(peer.publicKey)) return\n if (!peerInfo.prioritized || peerInfo.server) peerInfo._reset()\n if (peerInfo._updatePriority()) {\n this._enqueue(peerInfo)\n }\n }\n\n async _handleNetworkChange () {\n // prioritize figuring out if existing connections are dead\n for (const conn of this._allConnections) {\n conn.sendKeepAlive()\n }\n\n const refreshes = []\n\n for (const discovery of this._discovery.values()) {\n refreshes.push(discovery.refresh())\n }\n\n await Promise.allSettled(refreshes)\n }\n\n status (key) {\n return this._discovery.get(b4a.toString(key, 'hex')) || null\n }\n\n listen () {\n if (!this.listening) this.listening = this.server.listen(this.keyPair)\n return this.listening\n }\n\n // Object that exposes a cancellation method (destroy)\n // TODO: When you rejoin, it should reannounce + bump lookup priority\n join (topic, opts = {}) {\n if (!topic) throw new Error(ERR_MISSING_TOPIC)\n topic = unslab(topic)\n\n const topicString = b4a.toString(topic, 'hex')\n\n let discovery = this._discovery.get(topicString)\n\n if (discovery && !discovery.destroyed) {\n return discovery.session(opts)\n }\n\n discovery = new PeerDiscovery(this, topic, {\n limit: opts.limit,\n wait: discovery ? discovery.destroy() : null,\n suspended: this.suspended,\n onpeer: peer => this._handlePeer(peer, topic)\n })\n this._discovery.set(topicString, discovery)\n return discovery.session(opts)\n }\n\n // Returns a promise\n async leave (topic) {\n if (!topic) throw new Error(ERR_MISSING_TOPIC)\n const topicString = b4a.toString(topic, 'hex')\n if (!this._discovery.has(topicString)) return Promise.resolve()\n\n const discovery = this._discovery.get(topicString)\n\n try {\n await discovery.destroy()\n } catch {\n // ignore, prop network\n }\n\n if (this._discovery.get(topicString) === discovery) {\n this._discovery.delete(topicString)\n }\n }\n\n joinPeer (publicKey) {\n const peerInfo = this._upsertPeer(publicKey, null)\n if (!peerInfo) return\n if (!this.explicitPeers.has(peerInfo)) {\n peerInfo.explicit = true\n this.explicitPeers.add(peerInfo)\n }\n if (this._allConnections.has(publicKey)) return\n if (peerInfo._updatePriority()) {\n this._enqueue(peerInfo)\n }\n }\n\n leavePeer (publicKey) {\n const keyString = b4a.toString(publicKey, 'hex')\n if (!this.peers.has(keyString)) return\n\n const peerInfo = this.peers.get(keyString)\n peerInfo.explicit = false\n this.explicitPeers.delete(peerInfo)\n this._maybeDeletePeer(peerInfo)\n }\n\n // Returns a promise\n async flush () {\n const allFlushed = [...this._discovery.values()].map(v => v.flushed())\n await Promise.all(allFlushed)\n if (this._flushAllMaybe()) return true\n const pendingSize = this._allConnections.size - this.connections.size\n if (!this._queue.length && !pendingSize) return true\n return new Promise((resolve) => {\n this._pendingFlushes.push({\n onflush: resolve,\n missing: this._queue.length + pendingSize,\n tick: this._flushTick++\n })\n })\n }\n\n async clear () {\n const cleared = Promise.allSettled([...this._discovery.values()].map(d => d.destroy()))\n this._discovery.clear()\n return cleared\n }\n\n async destroy ({ force } = {}) {\n if (this.destroyed && !force) return\n this.destroyed = true\n\n this._timer.destroy()\n\n if (!force) await this.clear()\n\n await this.server.close()\n\n while (this._pendingFlushes.length) {\n const flush = this._pendingFlushes.pop()\n flush.onflush(false)\n }\n\n await this.dht.destroy({ force })\n }\n\n async suspend ({ log = noop } = {}) {\n if (this.suspended) return\n\n const promises = []\n\n promises.push(this.server.suspend({ log }))\n\n for (const discovery of this._discovery.values()) {\n promises.push(discovery.suspend({ log }))\n }\n\n for (const connection of this._allConnections) {\n connection.destroy()\n }\n\n this.suspended = true\n\n log('Suspending server and discovery... (' + promises.length + ')')\n await Promise.allSettled(promises)\n log('Done, suspending the dht...')\n await this.dht.suspend({ log })\n log('Done, swarm fully suspended')\n }\n\n async resume ({ log = noop } = {}) {\n if (!this.suspended) return\n\n log('Resuming the dht')\n await this.dht.resume()\n log('Done, resuming the server')\n await this.server.resume()\n log('Done, all discovery')\n\n for (const discovery of this._discovery.values()) {\n discovery.resume()\n }\n\n this._attemptClientConnections()\n this.suspended = false\n }\n\n topics () {\n return this._discovery.values()\n }\n}\n\nfunction noop () { }\n\nfunction allowAll () {\n return false\n}\n\nfunction shouldForceRelaying (code) {\n return (code === 'HOLEPUNCH_ABORTED') ||\n (code === 'HOLEPUNCH_DOUBLE_RANDOMIZED_NATS') ||\n (code === 'REMOTE_NOT_HOLEPUNCHABLE')\n}\nmodule.exports = class BulkTimer {\n constructor (time, fn) {\n this._time = time\n this._fn = fn\n this._interval = null\n this._next = []\n this._pending = []\n this._destroyed = false\n }\n\n destroy () {\n if (this._destroyed) return\n this._destroyed = true\n clearInterval(this._interval)\n this._interval = null\n }\n\n _ontick () {\n if (!this._next.length && !this._pending.length) return\n if (this._next.length) this._fn(this._next)\n this._next = this._pending\n this._pending = []\n }\n\n add (info) {\n if (this._destroyed) return\n if (!this._interval) {\n this._interval = setInterval(this._ontick.bind(this), Math.floor(this._time * 0.66))\n }\n\n this._pending.push(info)\n }\n}\nconst b4a = require('b4a')\n\nmodule.exports = class ConnectionSet {\n constructor () {\n this._byPublicKey = new Map()\n }\n\n [Symbol.iterator] () {\n return this._byPublicKey.values()\n }\n\n get size () {\n return this._byPublicKey.size\n }\n\n has (publicKey) {\n return this._byPublicKey.has(b4a.toString(publicKey, 'hex'))\n }\n\n get (publicKey) {\n return this._byPublicKey.get(b4a.toString(publicKey, 'hex'))\n }\n\n add (connection) {\n this._byPublicKey.set(b4a.toString(connection.remotePublicKey, 'hex'), connection)\n }\n\n delete (connection) {\n const keyString = b4a.toString(connection.remotePublicKey, 'hex')\n const existing = this._byPublicKey.get(keyString)\n if (existing !== connection) return\n this._byPublicKey.delete(keyString)\n }\n}\nconst safetyCatch = require('safety-catch')\nconst b4a = require('b4a')\n\nconst REFRESH_INTERVAL = 1000 * 60 * 10 // 10 min\nconst RANDOM_JITTER = 1000 * 60 * 2 // 2 min\nconst DELAY_GRACE_PERIOD = 1000 * 30 // 30s\n\nmodule.exports = class PeerDiscovery {\n constructor (swarm, topic, { limit = Infinity, wait = null, suspended = false, onpeer = noop, onerror = safetyCatch }) {\n this.limit = limit\n this.swarm = swarm\n this.topic = topic\n this.isClient = false\n this.isServer = false\n this.destroyed = false\n this.destroying = null\n this.suspended = suspended\n\n this._sessions = []\n this._clientSessions = 0\n this._serverSessions = 0\n\n this._onpeer = onpeer\n this._onerror = onerror\n\n this._activeQuery = null\n this._timer = null\n this._currentRefresh = null\n this._closestNodes = null\n this._firstAnnounce = true\n this._needsUnannounce = false\n this._refreshes = 0\n this._wait = wait\n }\n\n session ({ server = true, client = true, limit = Infinity, onerror = safetyCatch }) {\n if (this.destroyed) throw new Error('PeerDiscovery is destroyed')\n const session = new PeerDiscoverySession(this)\n session.refresh({ server, client, limit }).catch(onerror)\n this._sessions.push(session)\n return session\n }\n\n _refreshLater (eager) {\n const jitter = Math.round(Math.random() * RANDOM_JITTER)\n const delay = !eager\n ? REFRESH_INTERVAL + jitter\n : jitter\n\n if (this._timer) clearTimeout(this._timer)\n\n const startTime = Date.now()\n this._timer = setTimeout(() => {\n // If your laptop went to sleep, and is coming back online...\n const overdue = Date.now() - startTime > delay + DELAY_GRACE_PERIOD\n if (overdue) this._refreshLater(true)\n else this.refresh().catch(this._onerror)\n }, delay)\n }\n\n _isActive () {\n return !this.destroyed && !this.suspended\n }\n\n // TODO: Allow announce to be an argument to this\n // TODO: Maybe announce should be a setter?\n async _refresh () {\n if (this.suspended) return\n const clock = ++this._refreshes\n\n if (this._wait) {\n await this._wait\n this._wait = null\n if (clock !== this._refreshes || !this._isActive()) return\n }\n\n const clear = this.isServer && this._firstAnnounce\n if (clear) this._firstAnnounce = false\n\n const opts = {\n clear,\n closestNodes: this._closestNodes\n }\n\n if (this.isServer) {\n await this.swarm.listen()\n // if a parallel refresh is happening, yield to the new one\n if (clock !== this._refreshes || !this._isActive()) return\n this._needsUnannounce = true\n }\n\n const announcing = this.isServer\n const query = this._activeQuery = announcing\n ? this.swarm.dht.announce(this.topic, this.swarm.keyPair, this.swarm.server.relayAddresses, opts)\n : this._needsUnannounce\n ? this.swarm.dht.lookupAndUnannounce(this.topic, this.swarm.keyPair, opts)\n : this.swarm.dht.lookup(this.topic, opts)\n\n try {\n for await (const data of this._activeQuery) {\n if (!this.isClient || !this._isActive()) continue\n for (const peer of data.peers) {\n if (this.limit === 0) return\n this.limit--\n this._onpeer(peer, data)\n }\n }\n } catch (err) {\n if (this._isActive()) throw err\n } finally {\n if (this._activeQuery === query) {\n this._activeQuery = null\n if (!this.destroyed && !this.suspended) this._refreshLater(false)\n }\n }\n\n // This is set at the very end, when the query completes successfully.\n this._closestNodes = query.closestNodes\n\n if (clock !== this._refreshes) return\n\n // In this is the latest query, unannounce has been fulfilled as well\n if (!announcing) this._needsUnannounce = false\n }\n\n async refresh () {\n if (this.destroyed) throw new Error('PeerDiscovery is destroyed')\n\n const server = this._serverSessions > 0\n const client = this._clientSessions > 0\n\n if (this.suspended) return\n\n if (server === this.isServer && client === this.isClient) {\n if (this._currentRefresh) return this._currentRefresh\n this._currentRefresh = this._refresh()\n } else {\n if (this._activeQuery) this._activeQuery.destroy()\n this.isServer = server\n this.isClient = client\n this._currentRefresh = this._refresh()\n }\n\n const refresh = this._currentRefresh\n try {\n await refresh\n } catch {\n return false\n } finally {\n if (refresh === this._currentRefresh) {\n this._currentRefresh = null\n }\n }\n\n return true\n }\n\n async flushed () {\n if (this.swarm.listening) await this.swarm.listening\n\n try {\n await this._currentRefresh\n return true\n } catch {\n return false\n }\n }\n\n async _destroyMaybe () {\n if (this.destroyed) return\n\n try {\n if (this._sessions.length === 0) await this.swarm.leave(this.topic)\n else if (this._serverSessions === 0 && this._needsUnannounce) await this.refresh()\n } catch (err) { // ignore network failures here, as we are tearing down\n safetyCatch(err)\n }\n }\n\n destroy () {\n if (this.destroying) return this.destroying\n this.destroying = this._destroy()\n return this.destroying\n }\n\n async _abort (log) {\n const id = log === noop ? '' : b4a.toString(this.topic, 'hex')\n\n log('Aborting discovery', id)\n if (this._wait) await this._wait\n log('Aborting discovery (post wait)', id)\n\n if (this._activeQuery) {\n this._activeQuery.destroy()\n this._activeQuery = null\n }\n if (this._timer) {\n clearTimeout(this._timer)\n this._timer = null\n }\n\n let nodes = this._closestNodes\n\n if (this._currentRefresh) {\n try {\n await this._currentRefresh\n } catch {\n // If the destroy causes the refresh to fail, suppress it.\n }\n }\n\n log('Aborting discovery (post refresh)', id)\n if (this._isActive()) return\n\n if (!nodes) nodes = this._closestNodes\n else if (this._closestNodes !== nodes) {\n const len = nodes.length\n for (const newer of this._closestNodes) {\n if (newer.id && !hasNode(nodes, len, newer)) nodes.push(newer)\n }\n }\n\n if (this._needsUnannounce) {\n log('Unannouncing discovery', id)\n if (nodes && nodes.length) await this.swarm.dht.unannounce(this.topic, this.swarm.keyPair, { closestNodes: nodes, onlyClosestNodes: true, force: true })\n this._needsUnannounce = false\n log('Unannouncing discovery (done)', id)\n }\n }\n\n _destroy () {\n if (this.destroyed) return\n this.destroyed = true\n return this._abort(noop)\n }\n\n async suspend ({ log = noop } = {}) {\n if (this.suspended) return\n this.suspended = true\n try {\n await this._abort(log)\n } catch {\n // ignore\n }\n }\n\n resume () {\n if (!this.suspended) return\n this.suspended = false\n this.refresh().catch(noop)\n }\n}\n\nclass PeerDiscoverySession {\n constructor (discovery) {\n this.discovery = discovery\n this.isClient = false\n this.isServer = false\n this.destroyed = false\n }\n\n get swarm () {\n return this.discovery.swarm\n }\n\n get topic () {\n return this.discovery.topic\n }\n\n async refresh ({ client = this.isClient, server = this.isServer, limit = Infinity } = {}) {\n if (this.destroyed) throw new Error('PeerDiscovery is destroyed')\n if (!client && !server) throw new Error('Cannot refresh with neither client nor server option')\n\n if (client !== this.isClient) {\n this.isClient = client\n this.discovery._clientSessions += client ? 1 : -1\n }\n\n if (server !== this.isServer) {\n this.isServer = server\n this.discovery._serverSessions += server ? 1 : -1\n }\n\n this.discovery.limit = limit\n\n return this.discovery.refresh()\n }\n\n async flushed () {\n return this.discovery.flushed()\n }\n\n async destroy () {\n if (this.destroyed) return\n this.destroyed = true\n\n if (this.isClient) this.discovery._clientSessions--\n if (this.isServer) this.discovery._serverSessions--\n\n const index = this.discovery._sessions.indexOf(this)\n const head = this.discovery._sessions.pop()\n\n if (head !== this) this.discovery._sessions[index] = head\n\n return this.discovery._destroyMaybe()\n }\n}\n\nfunction hasNode (nodes, len, node) {\n for (let i = 0; i < len; i++) {\n const existing = nodes[i]\n if (existing.id && b4a.equals(existing.id, node.id)) return true\n }\n\n return false\n}\n\nfunction noop () {}\nconst { EventEmitter } = require('events')\nconst b4a = require('b4a')\nconst unslab = require('unslab')\n\nconst MIN_CONNECTION_TIME = 15000\n\nconst VERY_LOW_PRIORITY = 0\nconst LOW_PRIORITY = 1\nconst NORMAL_PRIORITY = 2\nconst HIGH_PRIORITY = 3\nconst VERY_HIGH_PRIORITY = 4\n\nmodule.exports = class PeerInfo extends EventEmitter {\n constructor ({ publicKey, relayAddresses }) {\n super()\n\n this.publicKey = unslab(publicKey)\n this.relayAddresses = relayAddresses\n\n this.reconnecting = true\n this.proven = false\n this.connectedTime = -1\n this.disconnectedTime = 0\n this.banned = false\n this.tried = false\n this.explicit = false\n this.waiting = false\n this.forceRelaying = false\n\n // Set by the Swarm\n this.queued = false\n this.client = false\n this.topics = [] // TODO: remove on next major (check with mafintosh for context)\n\n this.attempts = 0\n this.priority = NORMAL_PRIORITY\n\n // Used by shuffled-priority-queue\n this._index = 0\n\n // Used for flush management\n this._flushTick = 0\n\n // Used for topic multiplexing\n this._seenTopics = new Set()\n }\n\n get server () {\n return !this.client\n }\n\n get prioritized () {\n return this.priority >= NORMAL_PRIORITY\n }\n\n _getPriority () {\n const peerIsStale = this.tried && !this.proven\n if (peerIsStale || this.attempts > 3) return VERY_LOW_PRIORITY\n if (this.attempts === 3) return LOW_PRIORITY\n if (this.attempts === 2) return HIGH_PRIORITY\n if (this.attempts === 1) return VERY_HIGH_PRIORITY\n return NORMAL_PRIORITY\n }\n\n _connected () {\n this.proven = true\n this.connectedTime = Date.now()\n }\n\n _disconnected () {\n this.disconnectedTime = Date.now()\n if (this.connectedTime > -1) {\n if ((this.disconnectedTime - this.connectedTime) >= MIN_CONNECTION_TIME) this.attempts = 0 // fast retry\n this.connectedTime = -1\n }\n this.attempts++\n }\n\n _deprioritize () {\n this.attempts = 3\n }\n\n _reset () {\n this.client = false\n this.proven = false\n this.tried = false\n this.attempts = 0\n }\n\n _updatePriority () {\n if (this.explicit && this.attempts > 3) this._deprioritize()\n if (this.banned || this.queued || this.attempts > 3) return false\n this.priority = this._getPriority()\n return true\n }\n\n _topic (topic) {\n const topicString = b4a.toString(topic, 'hex')\n if (this._seenTopics.has(topicString)) return\n this._seenTopics.add(topicString)\n this.topics.push(topic)\n this.emit('topic', topic)\n }\n\n reconnect (val) {\n this.reconnecting = !!val\n }\n\n ban (val) {\n this.banned = !!val\n }\n\n shouldGC () {\n return !(this.banned || this.queued || this.explicit || this.waiting)\n }\n}\nconst BulkTimer = require('./bulk-timer')\n\nconst BACKOFF_JITTER = 500\nconst BACKOFF_S = 1000 + Math.round(BACKOFF_JITTER * Math.random())\nconst BACKOFF_M = 5000 + Math.round(2 * BACKOFF_JITTER * Math.random())\nconst BACKOFF_L = 15000 + Math.round(4 * BACKOFF_JITTER * Math.random())\nconst BACKOFF_X = 1000 * 60 * 10 + Math.round(240 * BACKOFF_JITTER * Math.random())\n\nmodule.exports = class RetryTimer {\n constructor (push, { backoffs = [BACKOFF_S, BACKOFF_M, BACKOFF_L, BACKOFF_X], jitter = BACKOFF_JITTER } = {}) {\n this.jitter = jitter\n this.backoffs = backoffs\n\n this._sTimer = new BulkTimer(backoffs[0] + Math.round(jitter * Math.random()), push)\n this._mTimer = new BulkTimer(backoffs[1] + Math.round(jitter * Math.random()), push)\n this._lTimer = new BulkTimer(backoffs[2] + Math.round(jitter * Math.random()), push)\n this._xTimer = new BulkTimer(backoffs[3] + Math.round(jitter * Math.random()), push)\n }\n\n _selectRetryTimer (peerInfo) {\n if (peerInfo.banned || !peerInfo.reconnecting) return null\n\n if (peerInfo.attempts > 3) {\n return peerInfo.explicit ? this._xTimer : null\n }\n\n if (peerInfo.attempts === 0) return this._sTimer\n if (peerInfo.proven) {\n switch (peerInfo.attempts) {\n case 1: return this._sTimer\n case 2: return this._mTimer\n case 3: return this._lTimer\n }\n } else {\n switch (peerInfo.attempts) {\n case 1: return this._mTimer\n case 2: return this._lTimer\n case 3: return this._lTimer\n }\n }\n\n return null\n }\n\n add (peerInfo) {\n const timer = this._selectRetryTimer(peerInfo)\n if (!timer) return false\n\n timer.add(peerInfo)\n return true\n }\n\n destroy () {\n this._sTimer.destroy()\n this._mTimer.destroy()\n this._lTimer.destroy()\n this._xTimer.destroy()\n }\n}\n{\n \"name\": \"hyperswarm\",\n \"version\": \"4.11.7\",\n \"description\": \"A distributed networking stack for connecting peers\",\n \"files\": [\n \"index.js\",\n \"lib/**.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"dependencies\": {\n \"b4a\": \"^1.3.1\",\n \"bare-events\": \"^2.2.0\",\n \"hyperdht\": \"^6.11.0\",\n \"safety-catch\": \"^1.0.2\",\n \"shuffled-priority-queue\": \"^2.1.0\",\n \"unslab\": \"^1.3.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.2\",\n \"hypercore-crypto\": \"^3.4.0\",\n \"standard\": \"^17.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard && node test/all.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/hyperswarm.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"contributors\": [\n \"David Mark Clements (@davidmarkclem)\",\n \"Andrew Osheroff (@andrewosh)\"\n ],\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperswarm/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperswarm\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */\nexports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var nBits = -7\n var i = isLE ? (nBytes - 1) : 0\n var d = isLE ? -1 : 1\n var s = buffer[offset + i]\n\n i += d\n\n e = s & ((1 << (-nBits)) - 1)\n s >>= (-nBits)\n nBits += eLen\n for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n m = e & ((1 << (-nBits)) - 1)\n e >>= (-nBits)\n nBits += mLen\n for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n if (e === 0) {\n e = 1 - eBias\n } else if (e === eMax) {\n return m ? NaN : ((s ? -1 : 1) * Infinity)\n } else {\n m = m + Math.pow(2, mLen)\n e = e - eBias\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n var i = isLE ? 0 : (nBytes - 1)\n var d = isLE ? 1 : -1\n var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n value = Math.abs(value)\n\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0\n e = eMax\n } else {\n e = Math.floor(Math.log(value) / Math.LN2)\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--\n c *= 2\n }\n if (e + eBias >= 1) {\n value += rt / c\n } else {\n value += rt * Math.pow(2, 1 - eBias)\n }\n if (value * c >= 2) {\n e++\n c /= 2\n }\n\n if (e + eBias >= eMax) {\n m = 0\n e = eMax\n } else if (e + eBias >= 1) {\n m = ((value * c) - 1) * Math.pow(2, mLen)\n e = e + eBias\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n e = 0\n }\n }\n\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n e = (e << mLen) | m\n eLen += mLen\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n buffer[offset + i - d] |= s * 128\n}\n{\n \"name\": \"ieee754\",\n \"description\": \"Read/write IEEE754 floating point numbers from/to a Buffer or array-like object\",\n \"version\": \"1.2.1\",\n \"author\": {\n \"name\": \"Feross Aboukhadijeh\",\n \"email\": \"feross@feross.org\",\n \"url\": \"https://feross.org\"\n },\n \"contributors\": [\n \"Romain Beauxis \"\n ],\n \"devDependencies\": {\n \"airtap\": \"^3.0.0\",\n \"standard\": \"*\",\n \"tape\": \"^5.0.1\"\n },\n \"keywords\": [\n \"IEEE 754\",\n \"buffer\",\n \"convert\",\n \"floating point\",\n \"ieee754\"\n ],\n \"license\": \"BSD-3-Clause\",\n \"main\": \"index.js\",\n \"types\": \"index.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/feross/ieee754.git\"\n },\n \"scripts\": {\n \"test\": \"standard && npm run test-node && npm run test-browser\",\n \"test-browser\": \"airtap -- test/*.js\",\n \"test-browser-local\": \"airtap --local -- test/*.js\",\n \"test-node\": \"tape test/*.js\"\n },\n \"funding\": [\n {\n \"type\": \"github\",\n \"url\": \"https://github.com/sponsors/feross\"\n },\n {\n \"type\": \"patreon\",\n \"url\": \"https://www.patreon.com/feross\"\n },\n {\n \"type\": \"consulting\",\n \"url\": \"https://feross.org/support\"\n }\n ],\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst { EventEmitter } = require('events')\n\nmodule.exports = class RoutingTable extends EventEmitter {\n constructor (id, opts) {\n if (!opts) opts = {}\n\n super()\n\n this.id = id\n this.k = opts.k || 20\n this.size = 0\n this.rows = new Array(id.length * 8)\n }\n\n add (node) {\n const i = this._diff(node.id)\n\n let row = this.rows[i]\n\n if (!row) {\n row = this.rows[i] = new Row(this, i)\n this.emit('row', row)\n }\n\n const len = row.nodes.length\n if (!row.add(node, this.k)) return false\n\n this.size += row.nodes.length - len\n return true\n }\n\n remove (id) {\n const i = this._diff(id)\n const row = this.rows[i]\n if (!row) return false\n if (!row.remove(id)) return false\n this.size--\n return true\n }\n\n get (id) {\n const i = this._diff(id)\n const row = this.rows[i]\n if (!row) return null\n return row.get(id)\n }\n\n has (id) {\n return this.get(id) !== null\n }\n\n random () {\n let n = (Math.random() * this.size) | 0\n\n for (let i = 0; i < this.rows.length; i++) {\n const r = this.rows[i]\n if (!r) continue\n if (n < r.nodes.length) return r.nodes[n]\n n -= r.nodes.length\n }\n\n return null\n }\n\n closest (id, k) {\n if (!k) k = this.k\n\n const result = []\n const d = this._diff(id)\n\n // push close nodes\n for (let i = d; i >= 0 && result.length < k; i--) this._pushNodes(i, k, result)\n\n // if we don't have enough close nodes, populate from other rows, re the paper\n for (let i = d + 1; i < this.rows.length && result.length < k; i++) this._pushNodes(i, k, result)\n\n return result\n }\n\n _pushNodes (i, k, result) {\n const row = this.rows[i]\n if (!row) return\n\n const missing = Math.min(k - result.length, row.nodes.length)\n for (let j = 0; j < missing; j++) result.push(row.nodes[j])\n }\n\n toArray () {\n return this.closest(this.id, Infinity)\n }\n\n _diff (id) {\n for (let i = 0; i < id.length; i++) {\n const a = id[i]\n const b = this.id[i]\n\n if (a !== b) return i * 8 + Math.clz32(a ^ b) - 24\n }\n\n return this.rows.length - 1\n }\n}\n\nclass Row extends EventEmitter {\n constructor (table, index) {\n super()\n\n this.data = null // can be used be upstream for whatevs\n this.byteOffset = index >> 3\n this.index = index\n this.table = table\n this.nodes = []\n }\n\n add (node) {\n const id = node.id\n\n let l = 0\n let r = this.nodes.length - 1\n\n while (l <= r) {\n const m = (l + r) >> 1\n const c = this.compare(id, this.nodes[m].id)\n\n if (c === 0) {\n this.nodes[m] = node\n return true\n }\n\n if (c < 0) r = m - 1\n else l = m + 1\n }\n\n if (this.nodes.length >= this.table.k) {\n this.emit('full', node)\n return false\n }\n\n this.insert(l, node)\n return true\n }\n\n remove (id) {\n let l = 0\n let r = this.nodes.length - 1\n\n while (l <= r) {\n const m = (l + r) >> 1\n const c = this.compare(id, this.nodes[m].id)\n\n if (c === 0) {\n this.splice(m)\n return true\n }\n\n if (c < 0) r = m - 1\n else l = m + 1\n }\n\n return false\n }\n\n get (id) {\n let l = 0\n let r = this.nodes.length - 1\n\n while (l <= r) {\n const m = (l + r) >> 1\n const node = this.nodes[m]\n const c = this.compare(id, node.id)\n\n if (c === 0) return node\n if (c < 0) r = m - 1\n else l = m + 1\n }\n\n return null\n }\n\n insert (i, node) {\n this.nodes.push(node) // push node or null or whatevs, just trying to not be polymorphic\n for (let j = this.nodes.length - 1; j > i; j--) this.nodes[j] = this.nodes[j - 1]\n this.nodes[i] = node\n this.emit('add', node)\n }\n\n splice (i) {\n for (; i < this.nodes.length - 1; i++) this.nodes[i] = this.nodes[i + 1]\n this.emit('remove', this.nodes.pop())\n }\n\n // very likely they diverge after a couple of bytes so a simple impl, like this is prop fastest vs Buffer.compare\n compare (a, b) {\n for (let i = this.byteOffset; i < a.length; i++) {\n const ai = a[i]\n const bi = b[i]\n if (ai === bi) continue\n return ai < bi ? -1 : 1\n }\n return 0\n }\n}\n{\n \"name\": \"kademlia-routing-table\",\n \"version\": \"1.0.6\",\n \"description\": \"XOR based routing table used for P2P networks such as a Kademlia DHT.\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"dependencies\": {\n \"bare-events\": \"^2.2.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"standard\": \"^17.1.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"keywords\": [\n \"kademlia\",\n \"p2p\",\n \"k-bucket\",\n \"k-buckets\",\n \"xor\",\n \"routing\",\n \"distributed\",\n \"systems\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/kademlia-routing-table.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/kademlia-routing-table/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/kademlia-routing-table\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = assert\n\nclass AssertionError extends Error {}\nAssertionError.prototype.name = 'AssertionError'\n\n/**\n * Minimal assert function\n * @param {any} t Value to check if falsy\n * @param {string=} m Optional assertion error message\n * @throws {AssertionError}\n */\nfunction assert (t, m) {\n if (!t) {\n var err = new AssertionError(m)\n if (Error.captureStackTrace) Error.captureStackTrace(err, assert)\n throw err\n }\n}\n{\n \"name\": \"nanoassert\",\n \"version\": \"2.0.0\",\n \"description\": \"Nanoscale assertion module\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"tape\": \"^4.9.1\"\n },\n \"scripts\": {\n \"test\": \"tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/emilbayes/nanoassert.git\"\n },\n \"keywords\": [\n \"assert\",\n \"unassert\",\n \"power-assert\",\n \"tiny\",\n \"nano\",\n \"pico\"\n ],\n \"author\": \"Emil Bay \",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/emilbayes/nanoassert/issues\"\n },\n \"homepage\": \"https://github.com/emilbayes/nanoassert#readme\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = class NatSampler {\n constructor () {\n this.host = null\n this.port = 0\n this.size = 0\n\n this._a = null\n this._b = null\n this._threshold = 0\n this._top = 0\n this._samples = []\n }\n\n add (host, port) {\n const a = this._bump(host, port, 2)\n const b = this._bump(host, 0, 1)\n\n if (this._samples.length < 32) {\n this.size++\n this._threshold = this.size - (this.size < 4 ? 0 : this.size < 8 ? 1 : this.size < 12 ? 2 : 3)\n this._samples.push(a, b)\n this._top += 2\n } else {\n if (this._top === 32) this._top = 0\n\n const oa = this._samples[this._top]\n this._samples[this._top++] = a\n oa.hits--\n\n const ob = this._samples[this._top]\n this._samples[this._top++] = b\n ob.hits--\n }\n\n if (this._a === null || this._a.hits < a.hits) this._a = a\n if (this._b === null || this._b.hits < b.hits) this._b = b\n\n if (this._a.hits >= this._threshold) {\n this.host = this._a.host\n this.port = this._a.port\n } else if (this._b.hits >= this._threshold) {\n this.host = this._b.host\n this.port = 0\n } else {\n this.host = null\n this.port = 0\n }\n\n return a.hits\n }\n\n _bump (host, port, inc) {\n for (let i = 0; i < 4; i++) {\n const j = (this._top - inc - (2 * i)) & 31\n if (j >= this._samples.length) return { host, port, hits: 1 }\n const s = this._samples[j]\n if (s.port === port && s.host === host) {\n s.hits++\n return s\n }\n }\n return { host, port, hits: 1 }\n }\n}\n{\n \"name\": \"nat-sampler\",\n \"version\": \"1.0.1\",\n \"description\": \"Sample addresses to figure out if a host + port is consistent\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^16.0.3\",\n \"tape\": \"^5.2.2\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/nat-sampler.git\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/nat-sampler/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/nat-sampler\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n/* eslint-disable camelcase */\nconst sodium = require('sodium-universal')\nconst assert = require('nanoassert')\nconst b4a = require('b4a')\n\nconst DHLEN = sodium.crypto_scalarmult_ed25519_BYTES\nconst PKLEN = sodium.crypto_scalarmult_ed25519_BYTES\nconst SCALARLEN = sodium.crypto_scalarmult_ed25519_BYTES\nconst SKLEN = sodium.crypto_sign_SECRETKEYBYTES\nconst ALG = 'Ed25519'\n\nmodule.exports = {\n DHLEN,\n PKLEN,\n SCALARLEN,\n SKLEN,\n ALG,\n name: ALG,\n generateKeyPair,\n dh\n}\n\nfunction generateKeyPair (privKey) {\n if (privKey) return generateSeedKeyPair(privKey.subarray(0, 32))\n\n const keyPair = {}\n keyPair.secretKey = b4a.alloc(SKLEN)\n keyPair.publicKey = b4a.alloc(PKLEN)\n\n sodium.crypto_sign_keypair(keyPair.publicKey, keyPair.secretKey)\n return keyPair\n}\n\nfunction generateSeedKeyPair (seed) {\n const keyPair = {}\n keyPair.secretKey = b4a.alloc(SKLEN)\n keyPair.publicKey = b4a.alloc(PKLEN)\n\n sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed)\n return keyPair\n}\n\nfunction dh (publicKey, { scalar, secretKey }) {\n // tweaked keys expose scalar directly\n if (!scalar) {\n assert(secretKey.byteLength === SKLEN)\n\n // libsodium stores seed not actual scalar\n const sk = b4a.alloc(64)\n sodium.crypto_hash_sha512(sk, secretKey.subarray(0, 32))\n sk[0] &= 248\n sk[31] &= 127\n sk[31] |= 64\n\n scalar = sk.subarray(0, 32)\n }\n\n assert(scalar.byteLength === SCALARLEN)\n assert(publicKey.byteLength === PKLEN)\n\n const output = b4a.alloc(DHLEN)\n\n // we clamp if necessary above\n sodium.crypto_scalarmult_ed25519_noclamp(\n output,\n scalar,\n publicKey\n )\n\n return output\n}\n{\n \"name\": \"noise-curve-ed\",\n \"version\": \"2.1.0\",\n \"description\": \"Ed25519 elliptic curve operations for [`noise-handshake`](https://github.com/chm-diederichs/noise-handshake)\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"npx standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/chm-diederichs/noise-curve-ed.git\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/chm-diederichs/noise-curve-ed/issues\"\n },\n \"homepage\": \"https://github.com/chm-diederichs/noise-curve-ed#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.1.0\",\n \"nanoassert\": \"^2.0.0\",\n \"sodium-universal\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"noise-handshake\": \"^3.0.0\",\n \"standard\": \"^16.0.3\",\n \"tape\": \"^5.2.2\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\n\nmodule.exports = class CipherState {\n constructor (key) {\n this.key = key || null\n this.nonce = 0\n this.CIPHER_ALG = 'ChaChaPoly'\n }\n\n initialiseKey (key) {\n this.key = key\n this.nonce = 0\n }\n\n setNonce (nonce) {\n this.nonce = nonce\n }\n\n encrypt (plaintext, ad) {\n if (!this.hasKey) return plaintext\n if (!ad) ad = b4a.alloc(0)\n\n const ciphertext = encryptWithAD(this.key, this.nonce, ad, plaintext)\n if (ciphertext.length > 65535) throw new Error(`ciphertext length of ${ciphertext.length} exceeds maximum Noise message length of 65535`)\n this.nonce++\n\n return ciphertext\n }\n\n decrypt (ciphertext, ad) {\n if (!this.hasKey) return ciphertext\n if (!ad) ad = b4a.alloc(0)\n if (ciphertext.length > 65535) throw new Error(`ciphertext length of ${ciphertext.length} exceeds maximum Noise message length of 65535`)\n\n const plaintext = decryptWithAD(this.key, this.nonce, ad, ciphertext)\n this.nonce++\n\n return plaintext\n }\n\n get hasKey () {\n return this.key !== null\n }\n\n _clear () {\n sodium.sodium_memzero(this.key)\n this.key = null\n this.nonce = null\n }\n\n static get MACBYTES () {\n return 16\n }\n\n static get NONCEBYTES () {\n return 8\n }\n\n static get KEYBYTES () {\n return 32\n }\n}\n\nfunction encryptWithAD (key, counter, additionalData, plaintext) {\n // for our purposes, additionalData will always be a pubkey so we encode from hex\n if (!b4a.isBuffer(additionalData)) additionalData = b4a.from(additionalData, 'hex')\n if (!b4a.isBuffer(plaintext)) plaintext = b4a.from(plaintext, 'hex')\n\n const nonce = b4a.alloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n const view = new DataView(nonce.buffer, nonce.byteOffset, nonce.byteLength)\n view.setUint32(4, counter, true)\n\n const ciphertext = b4a.alloc(plaintext.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES)\n\n sodium.crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext, plaintext, additionalData, null, nonce, key)\n return ciphertext\n}\n\nfunction decryptWithAD (key, counter, additionalData, ciphertext) {\n // for our purposes, additionalData will always be a pubkey so we encode from hex\n if (!b4a.isBuffer(additionalData)) additionalData = b4a.from(additionalData, 'hex')\n if (!b4a.isBuffer(ciphertext)) ciphertext = b4a.from(ciphertext, 'hex')\n\n const nonce = b4a.alloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n const view = new DataView(nonce.buffer, nonce.byteOffset, nonce.byteLength)\n view.setUint32(4, counter, true)\n\n const plaintext = b4a.alloc(ciphertext.byteLength - sodium.crypto_aead_chacha20poly1305_ietf_ABYTES)\n\n sodium.crypto_aead_chacha20poly1305_ietf_decrypt(plaintext, null, ciphertext, additionalData, nonce, key)\n return plaintext\n}\n/* eslint-disable camelcase */\nconst {\n crypto_kx_SEEDBYTES,\n crypto_kx_keypair,\n crypto_kx_seed_keypair,\n crypto_scalarmult_BYTES,\n crypto_scalarmult_SCALARBYTES,\n crypto_scalarmult,\n crypto_scalarmult_base\n} = require('sodium-universal')\n\nconst assert = require('nanoassert')\nconst b4a = require('b4a')\n\nconst DHLEN = crypto_scalarmult_BYTES\nconst PKLEN = crypto_scalarmult_BYTES\nconst SKLEN = crypto_scalarmult_SCALARBYTES\nconst SEEDLEN = crypto_kx_SEEDBYTES\nconst ALG = '25519'\n\nmodule.exports = {\n DHLEN,\n PKLEN,\n SKLEN,\n SEEDLEN,\n ALG,\n generateKeyPair,\n generateSeedKeyPair,\n dh\n}\n\nfunction generateKeyPair (privKey) {\n const keyPair = {}\n\n keyPair.secretKey = privKey || b4a.alloc(SKLEN)\n keyPair.publicKey = b4a.alloc(PKLEN)\n\n if (privKey) {\n crypto_scalarmult_base(keyPair.publicKey, keyPair.secretKey)\n } else {\n crypto_kx_keypair(keyPair.publicKey, keyPair.secretKey)\n }\n\n return keyPair\n}\n\nfunction generateSeedKeyPair (seed) {\n assert(seed.byteLength === SKLEN)\n\n const keyPair = {}\n keyPair.secretKey = b4a.alloc(SKLEN)\n keyPair.publicKey = b4a.alloc(PKLEN)\n\n crypto_kx_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed)\n return keyPair\n}\n\nfunction dh (publicKey, { secretKey }) {\n assert(secretKey.byteLength === SKLEN)\n assert(publicKey.byteLength === PKLEN)\n\n const output = b4a.alloc(DHLEN)\n\n crypto_scalarmult(\n output,\n secretKey,\n publicKey\n )\n\n return output\n}\nconst hmacBlake2b = require('./hmac')\nconst b4a = require('b4a')\n\nconst HASHLEN = 64\n\nmodule.exports = {\n hkdf,\n HASHLEN\n}\n\n// HMAC-based Extract-and-Expand KDF\n// https://www.ietf.org/rfc/rfc5869.txt\n\nfunction hkdf (salt, inputKeyMaterial, info = '', length = 2 * HASHLEN) {\n const pseudoRandomKey = hkdfExtract(salt, inputKeyMaterial)\n return hkdfExpand(pseudoRandomKey, info, length)\n}\n\nfunction hkdfExtract (salt, inputKeyMaterial) {\n const hmac = b4a.alloc(HASHLEN)\n return hmacDigest(hmac, salt, inputKeyMaterial)\n}\n\nfunction hkdfExpand (key, info, length) {\n // Put in dedicated slab to avoid keeping shared slab from being gc'ed\n const buffer = b4a.allocUnsafeSlow(length)\n\n const infoBuf = b4a.from(info)\n let prev = infoBuf\n\n const result = []\n for (let i = 0; i < length; i += HASHLEN) {\n const pos = b4a.from([(i / HASHLEN) + 1])\n\n const out = buffer.subarray(i, i + HASHLEN)\n result.push(out)\n\n prev = hmacDigest(out, key, [prev, infoBuf, pos])\n }\n\n return result\n}\n\nfunction hmacDigest (out, key, input) {\n hmacBlake2b(out, input, key)\n return out\n}\n/* eslint-disable camelcase */\nconst b4a = require('b4a')\nconst { sodium_memzero, crypto_generichash, crypto_generichash_batch } = require('sodium-universal')\n\nconst HASHLEN = 64\nconst BLOCKLEN = 128\nconst scratch = b4a.alloc(BLOCKLEN * 3)\nconst HMACKey = scratch.subarray(BLOCKLEN * 0, BLOCKLEN * 1)\nconst OuterKeyPad = scratch.subarray(BLOCKLEN * 1, BLOCKLEN * 2)\nconst InnerKeyPad = scratch.subarray(BLOCKLEN * 2, BLOCKLEN * 3)\n\n// Post-fill is done in the cases where someone caught an exception that\n// happened before we were able to clear data at the end\n\nmodule.exports = function hmac (out, batch, key) {\n if (key.byteLength > BLOCKLEN) {\n crypto_generichash(HMACKey.subarray(0, HASHLEN), key)\n sodium_memzero(HMACKey.subarray(HASHLEN))\n } else {\n // Covers key <= BLOCKLEN\n HMACKey.set(key)\n sodium_memzero(HMACKey.subarray(key.byteLength))\n }\n\n for (let i = 0; i < HMACKey.byteLength; i++) {\n OuterKeyPad[i] = 0x5c ^ HMACKey[i]\n InnerKeyPad[i] = 0x36 ^ HMACKey[i]\n }\n sodium_memzero(HMACKey)\n\n crypto_generichash_batch(out, [InnerKeyPad].concat(batch))\n sodium_memzero(InnerKeyPad)\n crypto_generichash_batch(out, [OuterKeyPad, out])\n sodium_memzero(OuterKeyPad)\n}\n\nmodule.exports.BYTES = HASHLEN\nmodule.exports.KEYBYTES = BLOCKLEN\nconst assert = require('nanoassert')\nconst b4a = require('b4a')\n\nconst SymmetricState = require('./symmetric-state')\nconst { HASHLEN } = require('./hkdf')\n\nconst PRESHARE_IS = Symbol('initiator static key preshared')\nconst PRESHARE_RS = Symbol('responder static key preshared')\n\nconst TOK_PSK = Symbol('psk')\n\nconst TOK_S = Symbol('s')\nconst TOK_E = Symbol('e')\n\nconst TOK_ES = Symbol('es')\nconst TOK_SE = Symbol('se')\nconst TOK_EE = Symbol('ee')\nconst TOK_SS = Symbol('ss')\n\nconst HANDSHAKES = Object.freeze({\n NN: [\n [TOK_E],\n [TOK_E, TOK_EE]\n ],\n NNpsk0: [\n [TOK_PSK, TOK_E],\n [TOK_E, TOK_EE]\n ],\n XX: [\n [TOK_E],\n [TOK_E, TOK_EE, TOK_S, TOK_ES],\n [TOK_S, TOK_SE]\n ],\n XXpsk0: [\n [TOK_PSK, TOK_E],\n [TOK_E, TOK_EE, TOK_S, TOK_ES],\n [TOK_S, TOK_SE]\n ],\n IK: [\n PRESHARE_RS,\n [TOK_E, TOK_ES, TOK_S, TOK_SS],\n [TOK_E, TOK_EE, TOK_SE]\n ]\n})\n\nclass Writer {\n constructor () {\n this.size = 0\n this.buffers = []\n }\n\n push (b) {\n this.size += b.byteLength\n this.buffers.push(b)\n }\n\n end () {\n const all = b4a.alloc(this.size)\n let offset = 0\n for (const b of this.buffers) {\n all.set(b, offset)\n offset += b.byteLength\n }\n return all\n }\n}\n\nclass Reader {\n constructor (buf) {\n this.offset = 0\n this.buffer = buf\n }\n\n shift (n) {\n const start = this.offset\n const end = this.offset += n\n if (end > this.buffer.byteLength) throw new Error('Insufficient bytes')\n return this.buffer.subarray(start, end)\n }\n\n end () {\n return this.shift(this.buffer.byteLength - this.offset)\n }\n}\n\nmodule.exports = class NoiseState extends SymmetricState {\n constructor (pattern, initiator, staticKeypair, opts = {}) {\n super(opts)\n\n this.s = staticKeypair || this.curve.generateKeyPair()\n this.e = null\n\n this.psk = null\n if (opts && opts.psk) this.psk = opts.psk\n\n this.re = null\n this.rs = null\n\n this.pattern = pattern\n this.handshake = HANDSHAKES[this.pattern].slice()\n\n this.isPskHandshake = !!this.psk && hasPskToken(this.handshake)\n\n this.protocol = b4a.from([\n 'Noise',\n this.pattern,\n this.DH_ALG,\n this.CIPHER_ALG,\n 'BLAKE2b'\n ].join('_'))\n\n this.initiator = initiator\n this.complete = false\n\n this.rx = null\n this.tx = null\n this.hash = null\n }\n\n initialise (prologue, remoteStatic) {\n if (this.protocol.byteLength <= HASHLEN) this.digest.set(this.protocol)\n else this.mixHash(this.protocol)\n\n this.chainingKey = b4a.from(this.digest)\n\n this.mixHash(prologue)\n\n while (!Array.isArray(this.handshake[0])) {\n const message = this.handshake.shift()\n\n // handshake steps should be as arrays, only\n // preshare tokens are provided otherwise\n assert(message === PRESHARE_RS || message === PRESHARE_IS,\n 'Unexpected pattern')\n\n const takeRemoteKey = this.initiator\n ? message === PRESHARE_RS\n : message === PRESHARE_IS\n\n if (takeRemoteKey) this.rs = remoteStatic\n\n const key = takeRemoteKey ? this.rs : this.s.publicKey\n assert(key != null, 'Remote pubkey required')\n\n this.mixHash(key)\n }\n }\n\n final () {\n const [k1, k2] = this.split()\n\n this.tx = this.initiator ? k1 : k2\n this.rx = this.initiator ? k2 : k1\n\n this.complete = true\n this.hash = this.getHandshakeHash()\n\n this._clear()\n }\n\n recv (buf) {\n const r = new Reader(buf)\n\n for (const pattern of this.handshake.shift()) {\n switch (pattern) {\n case TOK_PSK :\n this.mixKeyAndHash(this.psk)\n break\n\n case TOK_E :\n this.re = r.shift(this.curve.PKLEN)\n this.mixHash(this.re)\n if (this.isPskHandshake) this.mixKeyNormal(this.re)\n break\n\n case TOK_S : {\n const klen = this.hasKey ? this.curve.PKLEN + 16 : this.curve.PKLEN\n this.rs = this.decryptAndHash(r.shift(klen))\n break\n }\n\n case TOK_EE :\n case TOK_ES :\n case TOK_SE :\n case TOK_SS : {\n const useStatic = keyPattern(pattern, this.initiator)\n\n const localKey = useStatic.local ? this.s : this.e\n const remoteKey = useStatic.remote ? this.rs : this.re\n\n this.mixKey(remoteKey, localKey)\n break\n }\n\n default :\n throw new Error('Unexpected message')\n }\n }\n\n const payload = this.decryptAndHash(r.end())\n\n if (!this.handshake.length) this.final()\n return payload\n }\n\n send (payload = b4a.alloc(0)) {\n const w = new Writer()\n\n for (const pattern of this.handshake.shift()) {\n switch (pattern) {\n case TOK_PSK :\n this.mixKeyAndHash(this.psk)\n break\n\n case TOK_E :\n if (this.e === null) this.e = this.curve.generateKeyPair()\n this.mixHash(this.e.publicKey)\n if (this.isPskHandshake) this.mixKeyNormal(this.e.publicKey)\n w.push(this.e.publicKey)\n break\n\n case TOK_S :\n w.push(this.encryptAndHash(this.s.publicKey))\n break\n\n case TOK_ES :\n case TOK_SE :\n case TOK_EE :\n case TOK_SS : {\n const useStatic = keyPattern(pattern, this.initiator)\n\n const localKey = useStatic.local ? this.s : this.e\n const remoteKey = useStatic.remote ? this.rs : this.re\n\n this.mixKey(remoteKey, localKey)\n break\n }\n\n default :\n throw new Error('Unexpected message')\n }\n }\n\n w.push(this.encryptAndHash(payload))\n const response = w.end()\n\n if (!this.handshake.length) this.final()\n return response\n }\n\n _clear () {\n super._clear()\n\n this.e.secretKey.fill(0)\n this.e.publicKey.fill(0)\n\n this.re.fill(0)\n\n this.e = null\n this.re = null\n }\n}\n\nfunction keyPattern (pattern, initiator) {\n const ret = {\n local: false,\n remote: false\n }\n\n switch (pattern) {\n case TOK_EE:\n return ret\n\n case TOK_ES:\n ret.local ^= !initiator\n ret.remote ^= initiator\n return ret\n\n case TOK_SE:\n ret.local ^= initiator\n ret.remote ^= !initiator\n return ret\n\n case TOK_SS:\n ret.local ^= 1\n ret.remote ^= 1\n return ret\n }\n}\n\nfunction hasPskToken (handshake) {\n return handshake.some(x => {\n return Array.isArray(x) && x.indexOf(TOK_PSK) !== -1\n })\n}\n{\n \"name\": \"noise-handshake\",\n \"version\": \"4.1.0\",\n \"description\": \"Noise protocol handshake\",\n \"main\": \"noise.js\",\n \"files\": [\n \"*.js\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test/*.js\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/noise-handshake.git\"\n },\n \"dependencies\": {\n \"b4a\": \"^1.1.0\",\n \"nanoassert\": \"^2.0.0\",\n \"sodium-universal\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"noise-protocol\": \"chm-diederichs/noise-protocol.git#xx-ephemeral-key\",\n \"standard\": \"^16.0.3\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst sodium = require('sodium-universal')\nconst assert = require('nanoassert')\nconst b4a = require('b4a')\nconst CipherState = require('./cipher')\nconst curve = require('./dh')\nconst { HASHLEN, hkdf } = require('./hkdf')\n\nmodule.exports = class SymmetricState extends CipherState {\n constructor (opts = {}) {\n super()\n\n this.curve = opts.curve || curve\n this.digest = b4a.alloc(HASHLEN)\n this.chainingKey = null\n this.offset = 0\n\n this.DH_ALG = this.curve.ALG\n }\n\n mixHash (data) {\n accumulateDigest(this.digest, data)\n }\n\n mixKeyAndHash (key) {\n const [ck, tempH, tempK] = hkdf(this.chainingKey, key, '', 3 * HASHLEN)\n this.chainingKey = ck\n this.mixHash(tempH)\n this.initialiseKey(tempK.subarray(0, 32))\n }\n\n mixKeyNormal (key) {\n const [ck, tempK] = hkdf(this.chainingKey, key)\n this.chainingKey = ck\n this.initialiseKey(tempK.subarray(0, 32))\n }\n\n mixKey (remoteKey, localKey) {\n const dh = this.curve.dh(remoteKey, localKey)\n const hkdfResult = hkdf(this.chainingKey, dh)\n this.chainingKey = hkdfResult[0]\n this.initialiseKey(hkdfResult[1].subarray(0, 32))\n }\n\n encryptAndHash (plaintext) {\n const ciphertext = this.encrypt(plaintext, this.digest)\n accumulateDigest(this.digest, ciphertext)\n return ciphertext\n }\n\n decryptAndHash (ciphertext) {\n const plaintext = this.decrypt(ciphertext, this.digest)\n accumulateDigest(this.digest, ciphertext)\n return plaintext\n }\n\n getHandshakeHash (out) {\n if (!out) return this.getHandshakeHash(b4a.alloc(HASHLEN))\n assert(out.byteLength === HASHLEN, `output must be ${HASHLEN} bytes`)\n\n out.set(this.digest)\n return out\n }\n\n split () {\n const res = hkdf(this.chainingKey, b4a.alloc(0))\n return res.map(k => k.subarray(0, 32))\n }\n\n _clear () {\n super._clear()\n\n sodium.sodium_memzero(this.digest)\n sodium.sodium_memzero(this.chainingKey)\n\n this.digest = null\n this.chainingKey = null\n this.offset = null\n\n this.curve = null\n }\n\n static get alg () {\n return CipherState.alg + '_BLAKE2b'\n }\n}\n\nfunction accumulateDigest (digest, input) {\n const toHash = b4a.concat([digest, input])\n sodium.crypto_generichash(digest, toHash)\n}\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\nconst queueTick = require('queue-tick')\nconst safetyCatch = require('safety-catch')\nconst unslab = require('unslab')\n\nconst MAX_BUFFERED = 32768\nconst MAX_BACKLOG = Infinity // TODO: impl \"open\" backpressure\nconst MAX_BATCH = 8 * 1024 * 1024\n\nclass Channel {\n constructor (mux, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain) {\n this.userData = userData\n this.protocol = protocol\n this.aliases = aliases\n this.id = id\n this.handshake = null\n this.messages = []\n\n this.opened = false\n this.closed = false\n this.destroyed = false\n\n this.onopen = onopen\n this.onclose = onclose\n this.ondestroy = ondestroy\n this.ondrain = ondrain\n\n this._handshake = handshake\n this._mux = mux\n this._info = info\n this._localId = 0\n this._remoteId = 0\n this._active = 0\n this._extensions = null\n\n this._decBound = this._dec.bind(this)\n this._decAndDestroyBound = this._decAndDestroy.bind(this)\n\n this._openedPromise = null\n this._openedResolve = null\n\n this._destroyedPromise = null\n this._destroyedResolve = null\n\n for (const m of messages) this.addMessage(m)\n }\n\n get drained () {\n return this._mux.drained\n }\n\n fullyOpened () {\n if (this.opened) return Promise.resolve(true)\n if (this.closed) return Promise.resolve(false)\n if (this._openedPromise) return this._openedPromise\n\n this._openedPromise = new Promise((resolve) => { this._openedResolve = resolve })\n return this._openedPromise\n }\n\n fullyClosed () {\n if (this.destroyed) return Promise.resolve()\n if (this._destroyedPromise) return this._destroyedPromise\n\n this._destroyedPromise = new Promise((resolve) => { this._destroyedResolve = resolve })\n return this._destroyedPromise\n }\n\n open (handshake) {\n const id = this._mux._free.length > 0\n ? this._mux._free.pop()\n : this._mux._local.push(null) - 1\n\n this._info.opened++\n this._info.lastChannel = this\n this._localId = id + 1\n this._mux._local[id] = this\n\n if (this._remoteId === 0) {\n this._info.outgoing.push(this._localId)\n }\n\n const state = { buffer: null, start: 2, end: 2 }\n\n c.uint.preencode(state, this._localId)\n c.string.preencode(state, this.protocol)\n c.buffer.preencode(state, this.id)\n if (this._handshake) this._handshake.preencode(state, handshake)\n\n state.buffer = this._mux._alloc(state.end)\n\n state.buffer[0] = 0\n state.buffer[1] = 1\n c.uint.encode(state, this._localId)\n c.string.encode(state, this.protocol)\n c.buffer.encode(state, this.id)\n if (this._handshake) this._handshake.encode(state, handshake)\n\n this._mux._write0(state.buffer)\n }\n\n _dec () {\n if (--this._active === 0 && this.closed === true) this._destroy()\n }\n\n _decAndDestroy (err) {\n this._dec()\n this._mux._safeDestroy(err)\n }\n\n _fullyOpenSoon () {\n this._mux._remote[this._remoteId - 1].session = this\n queueTick(this._fullyOpen.bind(this))\n }\n\n _fullyOpen () {\n if (this.opened === true || this.closed === true) return\n\n const remote = this._mux._remote[this._remoteId - 1]\n\n this.opened = true\n this.handshake = this._handshake ? this._handshake.decode(remote.state) : null\n this._track(this.onopen(this.handshake, this))\n\n remote.session = this\n remote.state = null\n if (remote.pending !== null) this._drain(remote)\n\n this._resolveOpen(true)\n }\n\n _resolveOpen (opened) {\n if (this._openedResolve !== null) {\n this._openedResolve(opened)\n this._openedResolve = this._openedPromise = null\n }\n }\n\n _resolveDestroyed () {\n if (this._destroyedResolve !== null) {\n this._destroyedResolve()\n this._destroyedResolve = this._destroyedPromise = null\n }\n }\n\n _drain (remote) {\n for (let i = 0; i < remote.pending.length; i++) {\n const p = remote.pending[i]\n this._mux._buffered -= byteSize(p.state)\n this._recv(p.type, p.state)\n }\n\n remote.pending = null\n this._mux._resumeMaybe()\n }\n\n _track (p) {\n if (isPromise(p) === true) {\n this._active++\n return p.then(this._decBound, this._decAndDestroyBound)\n }\n\n return null\n }\n\n _close (isRemote) {\n if (this.closed === true) return\n this.closed = true\n\n this._info.opened--\n if (this._info.lastChannel === this) this._info.lastChannel = null\n\n if (this._remoteId > 0) {\n this._mux._remote[this._remoteId - 1] = null\n this._remoteId = 0\n // If remote has acked, we can reuse the local id now\n // otherwise, we need to wait for the \"ack\" to arrive\n this._mux._free.push(this._localId - 1)\n }\n\n this._mux._local[this._localId - 1] = null\n this._localId = 0\n\n this._mux._gc(this._info)\n this._track(this.onclose(isRemote, this))\n\n if (this._active === 0) this._destroy()\n\n this._resolveOpen(false)\n }\n\n _destroy () {\n if (this.destroyed === true) return\n this.destroyed = true\n this._track(this.ondestroy(this))\n this._resolveDestroyed()\n }\n\n _recv (type, state) {\n if (type < this.messages.length) {\n const m = this.messages[type]\n const p = m.recv(state, this)\n if (m.autoBatch === true) return p\n }\n return null\n }\n\n cork () {\n this._mux.cork()\n }\n\n uncork () {\n this._mux.uncork()\n }\n\n close () {\n if (this.closed === true) return\n\n const state = { buffer: null, start: 2, end: 2 }\n\n c.uint.preencode(state, this._localId)\n\n state.buffer = this._mux._alloc(state.end)\n\n state.buffer[0] = 0\n state.buffer[1] = 3\n c.uint.encode(state, this._localId)\n\n this._close(false)\n this._mux._write0(state.buffer)\n }\n\n addMessage (opts) {\n if (!opts) return this._skipMessage()\n\n const type = this.messages.length\n const autoBatch = opts.autoBatch !== false\n const encoding = opts.encoding || c.raw\n const onmessage = opts.onmessage || noop\n\n const s = this\n const typeLen = encodingLength(c.uint, type)\n\n const m = {\n type,\n autoBatch,\n encoding,\n onmessage,\n recv (state, session) {\n return session._track(m.onmessage(encoding.decode(state), session))\n },\n send (m, session = s) {\n if (session.closed === true) return false\n\n const mux = session._mux\n const state = { buffer: null, start: 0, end: typeLen }\n\n if (mux._batch !== null) {\n encoding.preencode(state, m)\n state.buffer = mux._alloc(state.end)\n\n c.uint.encode(state, type)\n encoding.encode(state, m)\n\n mux._pushBatch(session._localId, state.buffer)\n return true\n }\n\n c.uint.preencode(state, session._localId)\n encoding.preencode(state, m)\n\n state.buffer = mux._alloc(state.end)\n\n c.uint.encode(state, session._localId)\n c.uint.encode(state, type)\n encoding.encode(state, m)\n\n mux.drained = mux.stream.write(state.buffer)\n\n return mux.drained\n }\n }\n\n this.messages.push(m)\n\n return m\n }\n\n _skipMessage () {\n const type = this.messages.length\n const m = {\n type,\n encoding: c.raw,\n onmessage: noop,\n recv (state, session) {},\n send (m, session) {}\n }\n\n this.messages.push(m)\n return m\n }\n}\n\nmodule.exports = class Protomux {\n constructor (stream, { alloc } = {}) {\n if (stream.userData === null) stream.userData = this\n\n this.isProtomux = true\n this.stream = stream\n this.corked = 0\n this.drained = true\n\n this._alloc = alloc || (typeof stream.alloc === 'function' ? stream.alloc.bind(stream) : b4a.allocUnsafe)\n this._safeDestroyBound = this._safeDestroy.bind(this)\n this._uncorkBound = this.uncork.bind(this)\n\n this._remoteBacklog = 0\n this._buffered = 0\n this._paused = false\n this._remote = []\n this._local = []\n this._free = []\n this._batch = null\n this._batchState = null\n\n this._infos = new Map()\n this._notify = new Map()\n\n this.stream.on('data', this._ondata.bind(this))\n this.stream.on('drain', this._ondrain.bind(this))\n this.stream.on('end', this._onend.bind(this))\n this.stream.on('error', noop) // we handle this in \"close\"\n this.stream.on('close', this._shutdown.bind(this))\n }\n\n static from (stream, opts) {\n if (stream.userData && stream.userData.isProtomux) return stream.userData\n if (stream.isProtomux) return stream\n return new this(stream, opts)\n }\n\n static isProtomux (mux) {\n return typeof mux === 'object' && mux.isProtomux === true\n }\n\n * [Symbol.iterator] () {\n for (const session of this._local) {\n if (session !== null) yield session\n }\n }\n\n isIdle () {\n return this._local.length === this._free.length\n }\n\n cork () {\n if (++this.corked === 1) {\n this._batch = []\n this._batchState = { buffer: null, start: 0, end: 1 }\n }\n }\n\n uncork () {\n if (--this.corked === 0) {\n this._sendBatch(this._batch, this._batchState)\n this._batch = null\n this._batchState = null\n }\n }\n\n getLastChannel ({ protocol, id = null }) {\n const key = toKey(protocol, id)\n const info = this._infos.get(key)\n if (info) return info.lastChannel\n return null\n }\n\n pair ({ protocol, id = null }, notify) {\n this._notify.set(toKey(protocol, id), notify)\n }\n\n unpair ({ protocol, id = null }) {\n this._notify.delete(toKey(protocol, id))\n }\n\n opened ({ protocol, id = null }) {\n const key = toKey(protocol, id)\n const info = this._infos.get(key)\n return info ? info.opened > 0 : false\n }\n\n createChannel ({ userData = null, protocol, aliases = [], id = null, unique = true, handshake = null, messages = [], onopen = noop, onclose = noop, ondestroy = noop, ondrain = noop }) {\n if (this.stream.destroyed) return null\n\n const info = this._get(protocol, id, aliases)\n if (unique && info.opened > 0) return null\n\n if (info.incoming.length === 0) {\n return new Channel(this, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain)\n }\n\n this._remoteBacklog--\n\n const remoteId = info.incoming.shift()\n const r = this._remote[remoteId - 1]\n if (r === null) return null\n\n const session = new Channel(this, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain)\n\n session._remoteId = remoteId\n session._fullyOpenSoon()\n\n return session\n }\n\n _pushBatch (localId, buffer) {\n if (this._batchState.end >= MAX_BATCH) {\n this._sendBatch(this._batch, this._batchState)\n this._batch = []\n this._batchState = { buffer: null, start: 0, end: 1 }\n }\n\n if (this._batch.length === 0 || this._batch[this._batch.length - 1].localId !== localId) {\n this._batchState.end++\n c.uint.preencode(this._batchState, localId)\n }\n c.buffer.preencode(this._batchState, buffer)\n this._batch.push({ localId, buffer })\n }\n\n _sendBatch (batch, state) {\n if (batch.length === 0) return\n\n let prev = batch[0].localId\n\n state.buffer = this._alloc(state.end)\n state.buffer[state.start++] = 0\n state.buffer[state.start++] = 0\n\n c.uint.encode(state, prev)\n\n for (let i = 0; i < batch.length; i++) {\n const b = batch[i]\n if (prev !== b.localId) {\n state.buffer[state.start++] = 0\n c.uint.encode(state, (prev = b.localId))\n }\n c.buffer.encode(state, b.buffer)\n }\n\n this.drained = this.stream.write(state.buffer)\n }\n\n _get (protocol, id, aliases = []) {\n const key = toKey(protocol, id)\n\n let info = this._infos.get(key)\n if (info) return info\n\n info = { key, protocol, aliases: [], id, pairing: 0, opened: 0, incoming: [], outgoing: [], lastChannel: null }\n this._infos.set(key, info)\n\n for (const alias of aliases) {\n const key = toKey(alias, id)\n info.aliases.push(key)\n\n this._infos.set(key, info)\n }\n\n return info\n }\n\n _gc (info) {\n if (info.opened === 0 && info.outgoing.length === 0 && info.incoming.length === 0) {\n this._infos.delete(info.key)\n\n for (const alias of info.aliases) this._infos.delete(alias)\n }\n }\n\n _ondata (buffer) {\n if (buffer.byteLength === 0) return // ignore empty frames...\n try {\n const state = { buffer, start: 0, end: buffer.byteLength }\n this._decode(c.uint.decode(state), state)\n } catch (err) {\n this._safeDestroy(err)\n }\n }\n\n _ondrain () {\n this.drained = true\n\n for (const s of this._local) {\n if (s !== null) s._track(s.ondrain(s))\n }\n }\n\n _onend () { // TODO: support half open mode for the users who wants that here\n this.stream.end()\n }\n\n _decode (remoteId, state) {\n const type = c.uint.decode(state)\n\n if (remoteId === 0) {\n return this._oncontrolsession(type, state)\n }\n\n const r = remoteId <= this._remote.length ? this._remote[remoteId - 1] : null\n\n // if the channel is closed ignore - could just be a pipeline message...\n if (r === null) return null\n\n if (r.pending !== null) {\n this._bufferMessage(r, type, state)\n return null\n }\n\n return r.session._recv(type, state)\n }\n\n _oncontrolsession (type, state) {\n switch (type) {\n case 0:\n this._onbatch(state)\n break\n\n case 1:\n // return the promise back up as this has sideeffects so we can batch reply\n return this._onopensession(state)\n\n case 2:\n this._onrejectsession(state)\n break\n\n case 3:\n this._onclosesession(state)\n break\n }\n\n return null\n }\n\n _bufferMessage (r, type, { buffer, start, end }) {\n const state = { buffer, start, end } // copy\n r.pending.push({ type, state })\n this._buffered += byteSize(state)\n this._pauseMaybe()\n }\n\n _pauseMaybe () {\n if (this._paused === true || this._buffered <= MAX_BUFFERED) return\n this._paused = true\n this.stream.pause()\n }\n\n _resumeMaybe () {\n if (this._paused === false || this._buffered > MAX_BUFFERED) return\n this._paused = false\n this.stream.resume()\n }\n\n _onbatch (state) {\n const end = state.end\n let remoteId = c.uint.decode(state)\n\n let waiting = null\n\n while (state.end > state.start) {\n const len = c.uint.decode(state)\n if (len === 0) {\n remoteId = c.uint.decode(state)\n continue\n }\n state.end = state.start + len\n // if batch contains more than one message, cork it so we reply back with a batch\n if (end !== state.end && waiting === null) {\n waiting = []\n this.cork()\n }\n const p = this._decode(remoteId, state)\n if (waiting !== null && p !== null) waiting.push(p)\n state.start = state.end\n state.end = end\n }\n\n if (waiting !== null) {\n // the waiting promises are not allowed to throw but we destroy the stream in case we are wrong\n Promise.all(waiting).then(this._uncorkBound, this._safeDestroyBound)\n }\n }\n\n _onopensession (state) {\n const remoteId = c.uint.decode(state)\n const protocol = c.string.decode(state)\n const id = unslab(c.buffer.decode(state))\n\n // remote tried to open the control session - auto reject for now\n // as we can use as an explicit control protocol declaration if we need to\n if (remoteId === 0) {\n this._rejectSession(0)\n return null\n }\n\n const rid = remoteId - 1\n const info = this._get(protocol, id)\n\n // allow the remote to grow the ids by one\n if (this._remote.length === rid) {\n this._remote.push(null)\n }\n\n if (rid >= this._remote.length || this._remote[rid] !== null) {\n throw new Error('Invalid open message')\n }\n\n if (info.outgoing.length > 0) {\n const localId = info.outgoing.shift()\n const session = this._local[localId - 1]\n\n if (session === null) { // we already closed the channel - ignore\n this._free.push(localId - 1)\n return null\n }\n\n this._remote[rid] = { state, pending: null, session: null }\n\n session._remoteId = remoteId\n session._fullyOpen()\n return null\n }\n\n const copyState = { buffer: state.buffer, start: state.start, end: state.end }\n this._remote[rid] = { state: copyState, pending: [], session: null }\n\n if (++this._remoteBacklog > MAX_BACKLOG) {\n throw new Error('Remote exceeded backlog')\n }\n\n info.pairing++\n info.incoming.push(remoteId)\n\n return this._requestSession(protocol, id, info).catch(this._safeDestroyBound)\n }\n\n _onrejectsession (state) {\n const localId = c.uint.decode(state)\n\n // TODO: can be done smarter...\n for (const info of this._infos.values()) {\n const i = info.outgoing.indexOf(localId)\n if (i === -1) continue\n\n info.outgoing.splice(i, 1)\n\n const session = this._local[localId - 1]\n\n this._free.push(localId - 1)\n if (session !== null) session._close(true)\n\n this._gc(info)\n return\n }\n\n throw new Error('Invalid reject message')\n }\n\n _onclosesession (state) {\n const remoteId = c.uint.decode(state)\n\n if (remoteId === 0) return // ignore\n\n const rid = remoteId - 1\n const r = rid < this._remote.length ? this._remote[rid] : null\n\n if (r === null) return\n\n if (r.session !== null) r.session._close(true)\n }\n\n async _requestSession (protocol, id, info) {\n const notify = this._notify.get(toKey(protocol, id)) || this._notify.get(toKey(protocol, null))\n\n if (notify) await notify(id)\n\n if (--info.pairing > 0) return\n\n while (info.incoming.length > 0) {\n this._rejectSession(info, info.incoming.shift())\n }\n\n this._gc(info)\n }\n\n _rejectSession (info, remoteId) {\n if (remoteId > 0) {\n const r = this._remote[remoteId - 1]\n\n if (r.pending !== null) {\n for (let i = 0; i < r.pending.length; i++) {\n this._buffered -= byteSize(r.pending[i].state)\n }\n }\n\n this._remote[remoteId - 1] = null\n this._resumeMaybe()\n }\n\n const state = { buffer: null, start: 2, end: 2 }\n\n c.uint.preencode(state, remoteId)\n\n state.buffer = this._alloc(state.end)\n\n state.buffer[0] = 0\n state.buffer[1] = 2\n c.uint.encode(state, remoteId)\n\n this._write0(state.buffer)\n }\n\n _write0 (buffer) {\n if (this._batch !== null) {\n this._pushBatch(0, buffer.subarray(1))\n return\n }\n\n this.drained = this.stream.write(buffer)\n }\n\n destroy (err) {\n this.stream.destroy(err)\n }\n\n _safeDestroy (err) {\n safetyCatch(err)\n this.stream.destroy(err)\n }\n\n _shutdown () {\n for (const s of this._local) {\n if (s !== null) s._close(true)\n }\n }\n}\n\nfunction noop () {}\n\nfunction toKey (protocol, id) {\n return protocol + '##' + (id ? b4a.toString(id, 'hex') : '')\n}\n\nfunction byteSize (state) {\n return 512 + (state.end - state.start)\n}\n\nfunction isPromise (p) {\n return !!(p && typeof p.then === 'function')\n}\n\nfunction encodingLength (enc, val) {\n const state = { buffer: null, start: 0, end: 0 }\n enc.preencode(state, val)\n return state.end\n}\n{\n \"name\": \"protomux\",\n \"version\": \"3.10.1\",\n \"description\": \"Multiplex multiple message oriented protocols over a stream\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.3.1\",\n \"compact-encoding\": \"^2.5.1\",\n \"queue-tick\": \"^1.0.0\",\n \"safety-catch\": \"^1.0.1\",\n \"unslab\": \"^1.3.0\"\n },\n \"devDependencies\": {\n \"@hyperswarm/secret-stream\": \"^6.0.0\",\n \"brittle\": \"^3.0.0\",\n \"standard\": \"^16.0.4\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/protomux.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/protomux/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/protomux\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n{\n \"name\": \"queue-tick\",\n \"version\": \"1.0.1\",\n \"description\": \"Next tick shim that prefers process.nextTick over queueMicrotask for compat\",\n \"main\": \"./process-next-tick.js\",\n \"browser\": {\n \"./process-next-tick.js\": \"./queue-microtask.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^16.0.3\",\n \"tape\": \"^5.3.1\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/queue-tick.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/queue-tick/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/queue-tick\",\n \"react-native\": {\n \"./process-next-tick.js\": \"./queue-microtask.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = (typeof process !== 'undefined' && typeof process.nextTick === 'function')\n ? process.nextTick.bind(process)\n : require('./queue-microtask')\nmodule.exports = typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn)\nconst b4a = require('b4a')\n\nvar EMPTY = []\n\nmodule.exports = RecordCache\n\nfunction RecordSet () {\n this.list = []\n this.map = new Map()\n}\n\nRecordSet.prototype.add = function (record, value) {\n var k = toString(record)\n var r = this.map.get(k)\n if (r) return false\n\n r = {index: this.list.length, record: value || record}\n this.list.push(r)\n this.map.set(k, r)\n return true\n}\n\nRecordSet.prototype.remove = function (record) {\n var k = toString(record)\n var r = this.map.get(k)\n if (!r) return false\n\n swap(this.list, r.index, this.list.length - 1)\n this.list.pop()\n this.map.delete(k)\n return true\n}\n\nfunction RecordStore () {\n this.records = new Map()\n this.size = 0\n}\n\nRecordStore.prototype.add = function (name, record, value) {\n var r = this.records.get(name)\n\n if (!r) {\n r = new RecordSet()\n this.records.set(name, r)\n }\n\n if (r.add(record, value)) {\n this.size++\n return true\n }\n\n return false\n}\n\nRecordStore.prototype.remove = function (name, record, value) {\n var r = this.records.get(name)\n if (!r) return false\n\n if (r.remove(record, value)) {\n this.size--\n if (!r.map.size) this.records.delete(name)\n return true\n }\n\n return false\n}\n\nRecordStore.prototype.get = function (name) {\n var r = this.records.get(name)\n return r ? r.list : EMPTY\n}\n\nfunction RecordCache (opts) {\n if (!(this instanceof RecordCache)) return new RecordCache(opts)\n if (!opts) opts = {}\n\n this.maxSize = opts.maxSize || Infinity\n this.maxAge = opts.maxAge || 0\n\n this._onstale = opts.onStale || opts.onstale || null\n this._fresh = new RecordStore()\n this._stale = new RecordStore()\n this._interval = null\n this._gced = false\n\n if (this.maxAge && this.maxAge < Infinity) {\n // 2/3 gives us a span of 0.66-1.33 maxAge or avg maxAge\n var tick = Math.ceil(2 / 3 * this.maxAge)\n this._interval = setInterval(this._gcAuto.bind(this), tick)\n if (this._interval.unref) this._interval.unref()\n }\n}\n\nObject.defineProperty(RecordCache.prototype, 'size', {\n get: function () {\n return this._fresh.size + this._stale.size\n }\n})\n\nRecordCache.prototype.add = function (name, record, value) {\n this._stale.remove(name, record, value)\n if (this._fresh.add(name, record, value) && this._fresh.size > this.maxSize) {\n this._gc()\n }\n}\n\nRecordCache.prototype.remove = function (name, record, value) {\n this._fresh.remove(name, record, value)\n this._stale.remove(name, record, value)\n}\n\nRecordCache.prototype.get = function (name, n) {\n var a = this._fresh.get(name)\n var b = this._stale.get(name)\n var aLen = a.length\n var bLen = b.length\n var len = aLen + bLen\n\n if (n > len || !n) n = len\n var result = new Array(n)\n\n for (var i = 0; i < n; i++) {\n var j = Math.floor(Math.random() * (aLen + bLen))\n if (j < aLen) {\n result[i] = a[j].record\n swap(a, j, --aLen)\n } else {\n j -= aLen\n result[i] = b[j].record\n swap(b, j, --bLen)\n }\n }\n\n return result\n}\n\nRecordCache.prototype._gcAuto = function () {\n if (!this._gced) this._gc()\n this._gced = false\n}\n\nRecordCache.prototype._gc = function () {\n if (this._onstale && this._stale.size > 0) this._onstale(this._stale)\n this._stale = this._fresh\n this._fresh = new RecordStore()\n this._gced = true\n}\n\nRecordCache.prototype.clear = function () {\n this._gc()\n this._gc()\n}\n\nRecordCache.prototype.destroy = function () {\n this.clear()\n clearInterval(this._interval)\n this._interval = null\n}\n\nfunction toString (record) {\n return b4a.isBuffer(record) ? b4a.toString(record, 'hex') : record\n}\n\nfunction swap (list, a, b) {\n var tmp = list[a]\n tmp.index = b\n list[b].index = a\n list[a] = list[b]\n list[b] = tmp\n}\n{\n \"name\": \"record-cache\",\n \"version\": \"1.2.0\",\n \"description\": \"Cache optimised for record like things\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.3.1\"\n },\n \"devDependencies\": {\n \"standard\": \"^10.0.3\",\n \"tape\": \"^4.8.0\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/record-cache.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/record-cache/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/record-cache\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst runtime = require('./lib/runtime')\n\nif (runtime === 'bare') {\n module.exports = require('./lib/runtime/bare')\n} else if (runtime === 'node') {\n module.exports = require('./lib/runtime/node')\n} else {\n module.exports = require('./lib/runtime/default')\n}\nmodule.exports =\n typeof Bare !== 'undefined'\n ? 'bare'\n : typeof process !== 'undefined'\n ? 'node'\n : 'unknown'\nmodule.exports = require.addon.bind(require)\nif (typeof require.addon === 'function') {\n module.exports = require.addon.bind(require)\n} else {\n module.exports = function addon(specifier, parentURL) {\n throw new Error(\n `Cannot find addon '${specifier}' imported from '${parentURL}'`\n )\n }\n}\nif (typeof require.addon === 'function') {\n module.exports = require.addon.bind(require)\n} else {\n const url = require('url')\n const resolve = require('bare-addon-resolve')\n\n const host = process.platform + '-' + process.arch\n const conditions = ['node', process.platform, process.arch]\n const extensions = ['.node']\n\n module.exports = function addon(specifier, parentURL) {\n if (typeof parentURL === 'string') parentURL = url.pathToFileURL(parentURL)\n\n for (const resolution of resolve(\n specifier,\n parentURL,\n { host, conditions, extensions },\n readPackage\n )) {\n switch (resolution.protocol) {\n case 'file:':\n try {\n return require(url.fileURLToPath(resolution))\n } catch {\n continue\n }\n }\n }\n\n throw new Error(\n `Cannot find addon '${specifier}' imported from '${parentURL.href}'`\n )\n\n function readPackage(packageURL) {\n try {\n return require(url.fileURLToPath(packageURL))\n } catch (err) {\n return null\n }\n }\n }\n}\n{\n \"name\": \"require-addon\",\n \"version\": \"1.1.0\",\n \"description\": \"Import native addons across JavaScript runtimes\",\n \"exports\": {\n \".\": \"./index.js\",\n \"./package\": \"./package.json\"\n },\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n },\n \"url\": {\n \"bare\": \"bare-url\",\n \"default\": \"url\"\n }\n },\n \"files\": [\n \"index.js\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:bare\": \"bare test.js\",\n \"test:node\": \"node test.js\",\n \"lint\": \"prettier . --check\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/require-addon.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/require-addon/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/require-addon#readme\",\n \"engines\": {\n \"bare\": \">=1.10.0\"\n },\n \"dependencies\": {\n \"bare-addon-resolve\": \"^1.3.0\",\n \"bare-url\": \"^2.1.0\"\n },\n \"devDependencies\": {\n \"bare-bundle\": \"^1.8.1\",\n \"bare-bundle-evaluate\": \"^1.1.0\",\n \"bare-fs\": \"^4.0.0\",\n \"bare-path\": \"^3.0.0\",\n \"brittle\": \"^3.7.0\",\n \"prettier\": \"^3.4.1\",\n \"prettier-config-standard\": \"^7.0.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = safetyCatch\n\nfunction isActuallyUncaught (err) {\n if (!err) return false\n return err instanceof TypeError ||\n err instanceof SyntaxError ||\n err instanceof ReferenceError ||\n err instanceof EvalError ||\n err instanceof RangeError ||\n err instanceof URIError ||\n err.code === 'ERR_ASSERTION'\n}\n\nfunction throwErrorNT (err) {\n queueMicrotask(() => { throw err })\n}\n\nfunction safetyCatch (err) {\n if (isActuallyUncaught(err)) {\n throwErrorNT(err)\n throw err\n }\n}\n{\n \"name\": \"safety-catch\",\n \"version\": \"1.0.2\",\n \"description\": \"Small module that makes sure your catch, caught an actual error and not a programming mistake or assertion\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {},\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/safety-catch.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/safety-catch/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/safety-catch\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst set = require('unordered-set')\n\nmodule.exports = opts => new ShuffledPriorityQueue(opts)\n\nclass ShuffledPriorityQueue {\n constructor (opts) {\n this.priorities = []\n this.equals = (opts && opts.equals) || null\n }\n\n get length () {\n return this.priorities.reduce(add, 0)\n }\n\n [Symbol.iterator] () {\n return new Iterator(this)\n }\n\n head () {\n for (let i = this.priorities.length - 1; i >= 0; i--) {\n const q = this.priorities[i]\n if (q.length) return shuffle(q, 0)\n }\n return null\n }\n\n tail () {\n for (let i = 0; i < this.priorities.length; i++) {\n const q = this.priorities[i]\n if (q.length) return shuffle(q, 0)\n }\n return null\n }\n\n prev (prev) {\n if (!prev) return this.tail()\n return next(this.priorities, prev, 1)\n }\n\n next (prev) {\n if (!prev) return this.head()\n return next(this.priorities, prev, -1)\n }\n\n shift () {\n return this.remove(this.head())\n }\n\n pop () {\n return this.remove(this.tail())\n }\n\n add (val) {\n const prio = val.priority || 0\n while (prio >= this.priorities.length) this.priorities.push([])\n set.add(this.priorities[prio], val)\n return val\n }\n\n remove (val) {\n if (!val) return null\n\n if (val._index === undefined) {\n val = this.find(val)\n if (!val) return null\n }\n\n return set.remove(this.priorities[val.priority || 0], val)\n }\n\n has (val) {\n if (val._index === undefined) return this.find(val)\n const priority = val.priority || 0\n if (priority >= this.priorities.length) return false\n return set.has(this.priorities[priority], val)\n }\n\n find (val) {\n if (val._index !== undefined) return val\n\n const prio = val.priority || 0\n const qs = this.priorities\n if (prio >= qs.length) return null\n\n const q = qs[prio]\n\n for (let i = 0; i < q.length; i++) {\n if (this.equals(q[i], val)) return q[i]\n }\n\n return null\n }\n}\n\nclass Iterator {\n constructor (queue) {\n this.prev = null\n this.queue = queue\n }\n\n next () {\n const next = this.queue.next(this.prev)\n this.prev = next\n return { done: !next, value: next }\n }\n}\n\nfunction shuffle (q, i) {\n const ran = i + Math.floor(Math.random() * (q.length - i))\n set.swap(q, q[ran], q[i])\n return q[i]\n}\n\nfunction next (queues, prev, inc) {\n let i = prev.priority || 0\n let j = (prev._index || 0) + 1\n\n while (true) {\n if (i < 0 || i >= queues.length) return null\n const q = queues[i]\n\n if (j >= q.length) {\n i += inc\n j = 0\n continue\n }\n\n return shuffle(q, j)\n }\n}\n\nfunction add (len, b) {\n return len + b.length\n}\n{\n \"name\": \"shuffled-priority-queue\",\n \"version\": \"2.1.0\",\n \"description\": \"A priority queue that shuffles elements with the same priority.\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"dependencies\": {\n \"unordered-set\": \"^2.0.1\"\n },\n \"devDependencies\": {\n \"standard\": \"^12.0.1\",\n \"tape\": \"^4.9.1\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/shuffled-priority-queue.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/shuffled-priority-queue/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/shuffled-priority-queue\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = class Signal {\n constructor () {\n this._resolve = null\n this._reject = null\n this._promise = null\n this._bind = bind.bind(this)\n this._onerror = clear.bind(this)\n this._onsuccess = clear.bind(this, null)\n this._timers = new Set()\n }\n\n wait (max) {\n if (!this._promise) {\n this._promise = new Promise(this._bind)\n this._promise.then(this._onsuccess).catch(this._onerror)\n }\n if (max) return this._sleep(max)\n return this._promise\n }\n\n _sleep (max) {\n const s = new Promise((resolve, reject) => {\n const done = () => {\n this._timers.delete(state)\n resolve(true)\n }\n const id = setTimeout(done, max)\n const state = { id, resolve, reject }\n this._timers.add(state)\n })\n\n return s\n }\n\n notify (err) {\n if (!this._promise) return\n const resolve = this._resolve\n const reject = this._reject\n this._promise = null\n if (err) reject(err)\n else resolve(true)\n }\n}\n\nfunction clear (err) {\n for (const { id, resolve, reject } of this._timers) {\n clearTimeout(id)\n if (err) reject(err)\n else resolve(true)\n }\n this._timers.clear()\n}\n\nfunction bind (resolve, reject) {\n this._resolve = resolve\n this._reject = reject\n}\n{\n \"name\": \"signal-promise\",\n \"version\": \"1.0.3\",\n \"description\": \"Simple wait/notify promise with optional max wait time\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {},\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/signal-promise.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/signal-promise/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/signal-promise\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nrequire.addon = require('require-addon')\nmodule.exports = require.addon('.', __filename)\nconst binding = require('./binding')\nconst { isNode } = require('which-runtime')\n\nconst OPTIONAL = Buffer.from(new ArrayBuffer(0))\n\nmodule.exports = exports = { ...binding }\n\n// memory\n\nexports.sodium_memzero = function (buf) {\n binding.sodium_memzero(buf)\n}\n\nexports.sodium_mlock = function (buf) {\n const res = binding.sodium_mlock(buf)\n if (res !== 0) throw new Error('memory lock failed')\n}\n\nexports.sodium_munlock = function (buf) {\n const res = binding.sodium_munlock(buf)\n if (res !== 0) throw new Error('memory unlock failed')\n}\n\nexports.sodium_malloc = function (size) {\n if (size < 0) throw new Error('invalid size')\n const buf = Buffer.from(binding.sodium_malloc(size))\n buf.secure = true\n\n return buf\n}\n\nexports.sodium_free = function (buf) {\n if (!buf?.secure) return\n\n binding.sodium_free(buf.buffer)\n}\n\nexports.sodium_mprotect_noaccess = function (buf) {\n const res = binding.sodium_mprotect_noaccess(buf.buffer)\n\n if (res !== 0) throw new Error('failed to lock buffer')\n}\n\nexports.sodium_mprotect_readonly = function (buf) {\n const res = binding.sodium_mprotect_readonly(buf.buffer)\n\n if (res !== 0) throw new Error('failed to unlock buffer')\n}\n\nexports.sodium_mprotect_readwrite = function (buf) {\n const res = binding.sodium_mprotect_readwrite(buf.buffer)\n\n if (res !== 0) throw new Error('failed to unlock buffer')\n}\n\n// crypto_randombytes\n\nexports.randombytes_buf = function (buffer) {\n binding.randombytes_buf(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength\n )\n}\n\nexports.randombytes_buf_deterministic = function (buffer, seed) {\n binding.randombytes_buf_deterministic(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength,\n\n seed.buffer,\n seed.byteOffset,\n seed.byteLength\n )\n}\n\n// sodium_helpers\n\nexports.sodium_memcmp = function (a, b) {\n if (a?.byteLength !== b?.byteLength) throw new Error('buffers must be of same length\"')\n return binding.sodium_memcmp(a, b)\n}\n\nexports.sodium_add = function (a, b) {\n if (a?.byteLength !== b?.byteLength) throw new Error('buffers must be of same length\"')\n binding.sodium_add(a, b)\n}\n\nexports.sodium_sub = function (a, b) {\n if (a?.byteLength !== b?.byteLength) throw new Error('buffers must be of same length\"')\n binding.sodium_sub(a, b)\n}\n\n/** @returns {number} */\nexports.sodium_compare = function (a, b) {\n if (a?.byteLength !== b?.byteLength) throw new Error('buffers must be of same length\"')\n return binding.sodium_compare(a, b)\n}\n\n/** @returns {boolean} */\nexports.sodium_is_zero = function (buffer, length) {\n if (!buffer) throw new Error('invalid buffer')\n length ??= buffer.byteLength\n if (length > buffer.byteLength || length < 0) throw new Error('invalid length')\n\n return binding.sodium_is_zero(buffer, length)\n}\n\n/** @returns {number} padded buffer length */\nexports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) {\n if (unpaddedBuflen > buffer.byteLength) throw new Error('unpadded length cannot exceed buffer length')\n if (blockSize > buffer.byteLength) throw new Error('block size cannot exceed buffer length')\n if (blockSize < 1) throw new Error('block sizemust be at least 1 byte')\n if (buffer?.byteLength < unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize))) throw new Error('buf not long enough')\n\n return binding.sodium_pad(buffer, unpaddedBuflen, blockSize)\n}\n\n/** @returns {number} unpadded buffer length */\nexports.sodium_unpad = function (buffer, paddedBuflen, blockSize) {\n if (paddedBuflen > buffer.byteLength) throw new Error('unpadded length cannot exceed buffer length')\n if (blockSize > buffer.byteLength) throw new Error('block size cannot exceed buffer length')\n if (blockSize < 1) throw new Error('block size must be at least 1 byte')\n\n return binding.sodium_unpad(buffer, paddedBuflen, blockSize)\n}\n\n// crypto_sign\n\nexports.crypto_sign_keypair = function (pk, sk) {\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n const res = binding.crypto_sign_keypair(pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_sign_seed_keypair = function (pk, sk, seed) {\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n const res = binding.crypto_sign_seed_keypair(pk, sk, seed)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_sign = function (sm, m, sk) {\n if (sm?.byteLength !== binding.crypto_sign_BYTES + m.byteLength) throw new Error('sm must be \"m.byteLength + crypto_sign_BYTES\" bytes')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_sign(sm, m, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_sign_open = function (m, sm, pk) {\n if (sm?.byteLength < binding.crypto_sign_BYTES) throw new Error('sm')\n if (m?.byteLength !== sm.byteLength - binding.crypto_sign_BYTES) throw new Error('m must be \"sm.byteLength - crypto_sign_BYTES\" bytes')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n const res = binding.crypto_sign_open(m, sm, pk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_sign_open = function (m, sm, pk) {\n if (sm?.byteLength < binding.crypto_sign_BYTES) throw new Error('sm')\n if (m?.byteLength !== sm.byteLength - binding.crypto_sign_BYTES) throw new Error('m must be \"sm.byteLength - crypto_sign_BYTES\" bytes')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n return binding.crypto_sign_open(m, sm, pk)\n}\n\nexports.crypto_sign_detached = function (sig, m, sk) {\n if (sig?.byteLength !== binding.crypto_sign_BYTES) throw new Error('sig')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_sign_detached(sig, m, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_sign_verify_detached = function (sig, m, pk) {\n return binding.crypto_sign_verify_detached(\n sig.buffer,\n sig.byteOffset,\n sig.byteLength,\n\n m.buffer,\n m.byteOffset,\n m.byteLength,\n\n pk.buffer,\n pk.byteOffset,\n pk.byteLength\n )\n}\n\nexports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) {\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) {\n if (x25519pk?.byteLength !== binding.crypto_box_PUBLICKEYBYTES) throw new Error('x25519_pk')\n if (ed25519pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('ed25519_pk')\n\n const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) {\n if (x25519sk?.byteLength !== binding.crypto_box_SECRETKEYBYTES) throw new Error('x25519_sk')\n\n const edLen = ed25519sk.byteLength\n\n if (edLen !== binding.crypto_sign_SECRETKEYBYTES && edLen !== binding.crypto_box_SECRETKEYBYTES) {\n throw new Error('ed25519_sk should either be \\'crypto_sign_SECRETKEYBYTES\\' bytes or \\'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES\\' bytes')\n }\n\n const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_box\n\nexports.crypto_box_keypair = function (pk, sk) {\n if (pk?.byteLength !== binding.crypto_box_PUBLICKEYBYTES) throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_box_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_box_keypair(pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_box_seed_keypair = function (pk, sk, seed) {\n if (pk?.byteLength !== binding.crypto_box_PUBLICKEYBYTES) throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_box_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_box_seed_keypair(pk, sk, seed)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_box_easy = function (c, m, n, pk, sk) {\n const res = binding.crypto_box_easy(c, m, n, pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_box_detached = function (c, mac, m, n, pk, sk) {\n const res = binding.crypto_box_detached(c, mac, m, n, pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_box_seal = function (c, m, pk) {\n const res = binding.crypto_box_seal(c, m, pk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_box_seal_open = function (m, c, pk, sk) {\n return binding.crypto_box_seal_open(\n m.buffer,\n m.byteOffset,\n m.byteLength,\n\n c.buffer,\n c.byteOffset,\n c.byteLength,\n\n pk.buffer,\n pk.byteOffset,\n pk.byteLength,\n\n sk.buffer,\n sk.byteOffset,\n sk.byteLength\n )\n}\n\n// crypto_secretbox\n\nexports.crypto_secretbox_easy = function (c, m, n, k) {\n if (c?.byteLength !== m.byteLength + binding.crypto_secretbox_MACBYTES) throw new Error('c must be \"m.byteLength + crypto_secretbox_MACBYTES\" bytes')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_secretbox_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_secretbox_easy(c, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_secretbox_open_easy = function (m, c, n, k) {\n if (m?.byteLength !== c.byteLength - binding.crypto_secretbox_MACBYTES) throw new Error('m must be \"c - crypto_secretbox_MACBYTES\" bytes')\n if (c?.byteLength < binding.crypto_secretbox_MACBYTES) throw new Error('c')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_secretbox_KEYBYTES) throw new Error('k')\n\n return binding.crypto_secretbox_open_easy(m, c, n, k)\n}\n\nexports.crypto_secretbox_detached = function (c, mac, m, n, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('c must \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_secretbox_MACBYTES) throw new Error('mac')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_secretbox_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_secretbox_detached(c, mac, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_secretbox_open_detached = function (m, c, mac, n, k) {\n if (m?.byteLength !== c.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_secretbox_MACBYTES) throw new Error('mac')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_secretbox_KEYBYTES) throw new Error('k')\n\n return binding.crypto_secretbox_open_detached(m, c, mac, n, k)\n}\n\n// crypto_generichash\n\nexports.crypto_generichash = function (output, input, key = OPTIONAL) {\n const res = binding.crypto_generichash(\n output.buffer,\n output.byteOffset,\n output.byteLength,\n\n input.buffer,\n input.byteOffset,\n input.byteLength,\n\n key.buffer,\n key.byteOffset,\n key.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_generichash_batch = function (output, batch, key) {\n if (isNode || batch.length < 4) {\n // iterate batch from native\n const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL)\n if (res !== 0) throw new Error('status: ' + res)\n } else {\n // iterate batch through fastcalls\n const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES)\n\n exports.crypto_generichash_init(state, key, output.byteLength)\n\n for (const buf of batch) {\n exports.crypto_generichash_update(state, buf)\n }\n\n exports.crypto_generichash_final(state, output)\n }\n}\n\nexports.crypto_generichash_keygen = function (key) {\n const res = binding.crypto_generichash_keygen(\n key.buffer, key.byteOffset, key.byteLength\n )\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_generichash_init = function (state, key, outputLength) {\n key ||= OPTIONAL\n\n const res = binding.crypto_generichash_init(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n key.buffer,\n key.byteOffset,\n key.byteLength,\n\n outputLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_generichash_update = function (state, input) {\n const res = binding.crypto_generichash_update(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n input.buffer,\n input.byteOffset,\n input.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_generichash_final = function (state, output) {\n const res = binding.crypto_generichash_final(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n output.buffer,\n output.byteOffset,\n output.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// secretstream\n\nexports.crypto_secretstream_xchacha20poly1305_keygen = function (k) {\n binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength)\n}\n\nexports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) {\n const res = binding.crypto_secretstream_xchacha20poly1305_init_push(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n header.buffer,\n header.byteOffset,\n header.byteLength,\n\n k.buffer,\n k.byteOffset,\n k.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) {\n const res = binding.crypto_secretstream_xchacha20poly1305_init_pull(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n header.buffer,\n header.byteOffset,\n header.byteLength,\n\n k.buffer,\n k.byteOffset,\n k.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {number} */\nexports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) {\n ad ||= OPTIONAL\n\n const res = binding.crypto_secretstream_xchacha20poly1305_push(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n c.buffer,\n c.byteOffset,\n c.byteLength,\n\n m.buffer,\n m.byteOffset,\n m.byteLength,\n\n ad.buffer,\n ad.byteOffset,\n ad.byteLength,\n\n tag\n )\n\n if (res < 0) throw new Error('push failed')\n\n return res\n}\n\n/** @returns {number} */\nexports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) {\n ad ||= OPTIONAL\n\n if (c?.byteLength < binding.crypto_secretstream_xchacha20poly1305_ABYTES) throw new Error('invalid cipher length')\n if (m?.byteLength !== c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES) throw new Error('invalid message length')\n\n const res = binding.crypto_secretstream_xchacha20poly1305_pull(\n state.buffer,\n state.byteOffset,\n state.byteLength,\n\n m.buffer,\n m.byteOffset,\n m.byteLength,\n\n tag.buffer,\n tag.byteOffset,\n tag.byteLength,\n\n c.buffer,\n c.byteOffset,\n c.byteLength,\n\n ad.buffer,\n ad.byteOffset,\n ad.byteLength\n )\n\n if (res < 0) throw new Error('pull failed')\n\n return res\n}\n\nexports.crypto_secretstream_xchacha20poly1305_rekey = function (state) {\n binding.crypto_secretstream_xchacha20poly1305_rekey(state.buffer, state.byteOffset, state.byteLength)\n}\n\n// crypto_stream\n\nexports.crypto_stream = function (c, n, k) {\n if (n?.byteLength !== binding.crypto_stream_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream(c, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_xor = function (c, m, n, k) {\n const res = binding.crypto_stream_xor(\n c.buffer,\n c.byteOffset,\n c.byteLength,\n\n m.buffer,\n m.byteOffset,\n m.byteLength,\n\n n.buffer,\n n.byteOffset,\n n.byteLength,\n\n k.buffer,\n k.byteOffset,\n k.byteLength\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20 = function (c, n, k) {\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20(c, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20_xor = function (c, m, n, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20_xor(c, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20_ietf = function (c, n, k) {\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20_ietf(c, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_xchacha20 = function (c, n, k) {\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_xchacha20(c, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_xchacha20_xor = function (c, m, n, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_xchacha20_xor(c, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_salsa20 = function (c, n, k) {\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_salsa20(c, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_salsa20_xor = function (c, m, n, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_salsa20_xor(c, m, n, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) {\n if (c?.byteLength !== m.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_auth\n\nexports.crypto_auth = function (out, input, k) {\n if (out?.byteLength !== binding.crypto_auth_BYTES) throw new Error('out')\n if (k?.byteLength !== binding.crypto_auth_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_auth(out, input, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_auth_verify = function (h, input, k) {\n if (h?.byteLength !== binding.crypto_auth_BYTES) throw new Error('h')\n if (k?.byteLength !== binding.crypto_auth_KEYBYTES) throw new Error('k')\n\n return binding.crypto_auth_verify(h, input, k)\n}\n\n// crypto_onetimeauth\n\nexports.crypto_onetimeauth = function (out, input, k) {\n if (out?.byteLength !== binding.crypto_onetimeauth_BYTES) throw new Error('out')\n if (k?.byteLength !== binding.crypto_onetimeauth_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_onetimeauth(out, input, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_onetimeauth_init = function (state, k) {\n if (state?.byteLength !== binding.crypto_onetimeauth_STATEBYTES) throw new Error(\"state must be 'crypto_onetimeauth_STATEBYTES' bytes\")\n if (k?.byteLength !== binding.crypto_onetimeauth_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_onetimeauth_init(state, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_onetimeauth_update = function (state, input) {\n if (state?.byteLength !== binding.crypto_onetimeauth_STATEBYTES) throw new Error(\"state must be 'crypto_onetimeauth_STATEBYTES' bytes\")\n\n const res = binding.crypto_onetimeauth_update(state, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_onetimeauth_final = function (state, out) {\n if (state?.byteLength !== binding.crypto_onetimeauth_STATEBYTES) throw new Error(\"state must be 'crypto_onetimeauth_STATEBYTES' bytes\")\n if (out?.byteLength !== binding.crypto_onetimeauth_BYTES) throw new Error('out')\n\n const res = binding.crypto_onetimeauth_final(state, out)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_onetimeauth_verify = function (h, input, k) {\n if (h?.byteLength !== binding.crypto_onetimeauth_BYTES) throw new Error('h')\n if (k?.byteLength !== binding.crypto_onetimeauth_KEYBYTES) throw new Error('k')\n\n return binding.crypto_onetimeauth_verify(h, input, k)\n}\n\n// crypto_pwhash\n\nexports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) {\n if (out?.byteLength < binding.crypto_pwhash_BYTES_MIN) throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_BYTES_MAX) throw new Error('out')\n if (salt?.byteLength !== binding.crypto_pwhash_SALTBYTES) throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n if (alg < 1 || alg > 2) throw new Error('alg must be either Argon2i 1.3 or Argon2id 1.3')\n\n const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_async = function (out, passwd, salt, opslimit, memlimit, alg, callback = undefined) {\n if (out?.byteLength < binding.crypto_pwhash_BYTES_MIN) throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_BYTES_MAX) throw new Error('out')\n if (salt?.byteLength !== binding.crypto_pwhash_SALTBYTES) throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n if (alg < 1 || alg > 2) throw new Error('alg must be either Argon2i 1.3 or Argon2id 1.3')\n\n const [done, promise] = checkStatus(callback)\n\n binding.crypto_pwhash_async(\n out.buffer,\n out.byteOffset,\n out.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n salt.buffer,\n salt.byteOffset,\n salt.byteLength,\n\n opslimit,\n memlimit,\n alg,\n\n done\n )\n\n return promise\n}\n\nexports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) {\n if (out?.byteLength !== binding.crypto_pwhash_STRBYTES) throw new Error('out')\n if (typeof opslimit !== 'number') throw new Error('opslimit')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (typeof memlimit !== 'number') throw new Error('memlimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) {\n if (out?.byteLength !== binding.crypto_pwhash_STRBYTES) throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (typeof opslimit !== 'number') throw new Error('opslimit')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (typeof memlimit !== 'number') throw new Error('memlimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const [done, promise] = checkStatus(callback)\n\n binding.crypto_pwhash_str_async(\n out.buffer,\n out.byteOffset,\n out.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n opslimit,\n memlimit,\n\n done\n )\n\n return promise\n}\n\n/** @returns {boolean} */\nexports.crypto_pwhash_str_verify = function (str, passwd) {\n if (str?.byteLength !== binding.crypto_pwhash_STRBYTES) throw new Error('str')\n\n return binding.crypto_pwhash_str_verify(str, passwd)\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) {\n if (str?.byteLength !== binding.crypto_pwhash_STRBYTES) throw new Error('str')\n if (!passwd?.byteLength) throw new Error('passwd')\n\n const [done, promise] = checkStatus(callback, true)\n\n binding.crypto_pwhash_str_verify_async(\n str.buffer,\n str.byteOffset,\n str.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n done\n )\n\n return promise\n}\n\n/** @returns {boolean} */\nexports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) {\n if (str?.byteLength !== binding.crypto_pwhash_STRBYTES) throw new Error('str')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n\n return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit)\n}\n\nexports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) {\n if (out?.byteLength < binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN) throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX) throw new Error('out')\n if (salt?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES) throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_scryptsalsa208sha256_async = function (out, passwd, salt, opslimit, memlimit, callback = undefined) {\n if (out?.byteLength < binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN) throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX) throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (salt?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES) throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const [done, promise] = checkStatus(callback)\n\n binding.crypto_pwhash_scryptsalsa208sha256_async(\n out.buffer,\n out.byteOffset,\n out.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n salt.buffer,\n salt.byteOffset,\n salt.byteLength,\n\n opslimit,\n memlimit,\n\n done\n )\n\n return promise\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_scryptsalsa208sha256_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) {\n if (out?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES) throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const [done, promise] = checkStatus(callback)\n\n binding.crypto_pwhash_scryptsalsa208sha256_str_async(\n out.buffer,\n out.byteOffset,\n out.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n opslimit,\n memlimit,\n\n done\n )\n\n return promise\n}\n\nexports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) {\n if (out?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES) throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX) throw new Error('memlimit')\n\n const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {Promise|undefined} */\nexports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function (str, passwd, callback = undefined) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES) throw new Error('str')\n if (!passwd?.byteLength) throw new Error('passwd')\n\n const [done, promise] = checkStatus(callback, true)\n\n binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async(\n str.buffer,\n str.byteOffset,\n str.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n done\n )\n\n return promise\n}\n\n/** @returns {boolean} */\nexports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES) throw new Error('str')\n if (!passwd?.byteLength) throw new Error('passwd')\n\n return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd)\n}\n\n/** @returns {boolean} */\nexports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES) throw new Error('str')\n if (opslimit < binding.crypto_pwhash_OPSLIMIT_MIN) throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_OPSLIMIT_MAX) throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_MEMLIMIT_MIN) throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_MEMLIMIT_MAX) throw new Error('memlimit')\n\n return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit)\n}\n\n// crypto_kx\n\nexports.crypto_kx_keypair = function (pk, sk) {\n if (pk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES) throw new Error('sk')\n\n const res = binding.crypto_kx_keypair(pk, sk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_kx_seed_keypair = function (pk, sk, seed) {\n if (pk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES) throw new Error('sk')\n if (seed?.byteLength !== binding.crypto_kx_SEEDBYTES) throw new Error('seed')\n\n const res = binding.crypto_kx_seed_keypair(pk, sk, seed)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) {\n // match `std::optional` by coercing null to undefined\n rx ??= undefined\n tx ??= undefined\n\n if (!rx && !tx) throw new Error('at least one session key must be specified')\n\n if (rx) {\n if (rx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES) throw new Error('receiving key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null')\n } else {\n if (tx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES) throw new Error('transmitting key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null')\n }\n\n if (clientPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('client_pk')\n if (clientSk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES) throw new Error('client_sk')\n if (serverPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('server_pk')\n\n const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) {\n // match `std::optional` by coercing null to undefined\n rx ??= undefined\n tx ??= undefined\n\n if (!rx && !tx) throw new Error('at least one session key must be specified')\n\n if (rx) {\n if (rx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES) throw new Error('receiving key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null')\n } else {\n if (tx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES) throw new Error('transmitting key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null')\n }\n\n if (serverPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('server_pk')\n if (serverSk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES) throw new Error('server_sk')\n if (clientPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES) throw new Error('client_pk')\n\n const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_scalarmult\n\nexports.crypto_scalarmult_base = function (q, n) {\n if (q?.byteLength !== binding.crypto_scalarmult_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_SCALARBYTES) throw new Error('n')\n\n const res = binding.crypto_scalarmult_base(q, n)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_scalarmult = function (q, n, p) {\n if (q?.byteLength !== binding.crypto_scalarmult_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_SCALARBYTES) throw new Error('n')\n if (p?.byteLength !== binding.crypto_scalarmult_BYTES) throw new Error('p')\n\n const res = binding.crypto_scalarmult(q, n, p)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_scalarmult_ed25519_base = function (q, n) {\n if (q?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES) throw new Error('n')\n\n const res = binding.crypto_scalarmult_ed25519_base(q, n)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_scalarmult_ed25519 = function (q, n, p) {\n if (q?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES) throw new Error('n')\n if (p?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('p')\n\n const res = binding.crypto_scalarmult_ed25519(q, n, p)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n/** @returns {boolean} */\nexports.crypto_core_ed25519_is_valid_point = function (p) {\n if (p?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('p')\n\n return binding.crypto_core_ed25519_is_valid_point(p)\n}\n\nexports.crypto_core_ed25519_from_uniform = function (p, r) {\n if (p?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('p')\n if (r?.byteLength !== binding.crypto_core_ed25519_UNIFORMBYTES) throw new Error('r')\n\n const res = binding.crypto_core_ed25519_from_uniform(p, r)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) {\n if (q?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES) throw new Error('n')\n\n const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) {\n if (q?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES) throw new Error('n')\n if (p?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES) throw new Error('p')\n\n const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_core\n\nexports.crypto_core_ed25519_add = function (r, p, q) {\n if (r?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('r')\n if (p?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('p')\n if (q?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('q')\n\n const res = binding.crypto_core_ed25519_add(r, p, q)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_core_ed25519_sub = function (r, p, q) {\n if (r?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('r')\n if (p?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('p')\n if (q?.byteLength !== binding.crypto_core_ed25519_BYTES) throw new Error('q')\n\n const res = binding.crypto_core_ed25519_sub(r, p, q)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_core_ed25519_scalar_random = function (r) {\n if (r?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('r')\n\n binding.crypto_core_ed25519_scalar_random(r)\n}\n\nexports.crypto_core_ed25519_scalar_reduce = function (r, s) {\n if (r?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('r')\n if (s?.byteLength !== binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES) throw new Error('s')\n\n binding.crypto_core_ed25519_scalar_reduce(r, s)\n}\n\nexports.crypto_core_ed25519_scalar_invert = function (recip, s) {\n if (recip?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('recip')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('s')\n\n binding.crypto_core_ed25519_scalar_invert(recip, s)\n}\n\nexports.crypto_core_ed25519_scalar_negate = function (neg, s) {\n if (neg?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('neg')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('s')\n\n binding.crypto_core_ed25519_scalar_negate(neg, s)\n}\n\nexports.crypto_core_ed25519_scalar_complement = function (comp, s) {\n if (comp?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('comp')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('s')\n\n binding.crypto_core_ed25519_scalar_complement(comp, s)\n}\n\nexports.crypto_core_ed25519_scalar_add = function (z, x, y) {\n if (z?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('z')\n if (x?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('x')\n if (y?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('y')\n\n binding.crypto_core_ed25519_scalar_add(z, x, y)\n}\n\nexports.crypto_core_ed25519_scalar_sub = function (z, x, y) {\n if (z?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('z')\n if (x?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('x')\n if (y?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES) throw new Error('y')\n\n binding.crypto_core_ed25519_scalar_sub(z, x, y)\n}\n\n// crypto_shorthash\n\nexports.crypto_shorthash = function (out, input, k) {\n if (out?.byteLength !== binding.crypto_shorthash_BYTES) throw new Error('out')\n if (k?.byteLength !== binding.crypto_shorthash_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_shorthash(out, input, k)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_kdf\n\nexports.crypto_kdf_keygen = function (key) {\n if (key?.byteLength !== binding.crypto_kdf_KEYBYTES) throw new Error('key')\n\n binding.crypto_kdf_keygen(key)\n}\n\nexports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) {\n if (subkey?.byteLength < binding.crypto_kdf_BYTES_MIN) throw new Error('subkey')\n if (subkey?.byteLength > binding.crypto_kdf_BYTES_MAX) throw new Error('subkey')\n if (ctx?.byteLength !== binding.crypto_kdf_CONTEXTBYTES) throw new Error('ctx')\n if (key?.byteLength !== binding.crypto_kdf_KEYBYTES) throw new Error('key')\n\n const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_hash\n\nexports.crypto_hash = function (out, input) {\n if (out?.byteLength !== binding.crypto_hash_BYTES) throw new Error('out')\n\n const res = binding.crypto_hash(out, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha256 = function (out, input) {\n if (out?.byteLength !== binding.crypto_hash_sha256_BYTES) throw new Error('out')\n\n const res = binding.crypto_hash_sha256(out, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha256_init = function (state) {\n if (state?.byteLength !== binding.crypto_hash_sha256_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha256_STATEBYTES' bytes\")\n }\n\n const res = binding.crypto_hash_sha256_init(state)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha256_update = function (state, input) {\n if (state?.byteLength !== binding.crypto_hash_sha256_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha256_STATEBYTES' bytes\")\n }\n\n const res = binding.crypto_hash_sha256_update(state, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha256_final = function (state, out) {\n if (state?.byteLength !== binding.crypto_hash_sha256_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha256_STATEBYTES' bytes\")\n }\n if (out?.byteLength !== binding.crypto_hash_sha256_BYTES) throw new Error('state')\n\n const res = binding.crypto_hash_sha256_final(state, out)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha512 = function (out, input) {\n if (out?.byteLength !== binding.crypto_hash_sha512_BYTES) throw new Error('out')\n\n const res = binding.crypto_hash_sha512(out, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha512_init = function (state) {\n if (state?.byteLength !== binding.crypto_hash_sha512_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha512_STATEBYTES' bytes\")\n }\n\n const res = binding.crypto_hash_sha512_init(state)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha512_update = function (state, input) {\n if (state?.byteLength !== binding.crypto_hash_sha512_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha512_STATEBYTES' bytes\")\n }\n\n const res = binding.crypto_hash_sha512_update(state, input)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_hash_sha512_final = function (state, out) {\n if (state?.byteLength !== binding.crypto_hash_sha512_STATEBYTES) {\n throw new Error(\"state must be 'crypto_hash_sha512_STATEBYTES' bytes\")\n }\n if (out?.byteLength !== binding.crypto_hash_sha512_BYTES) throw new Error('out')\n\n const res = binding.crypto_hash_sha512_final(state, out)\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\n// crypto_aead\n\nexports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) {\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n binding.crypto_aead_xchacha20poly1305_ietf_keygen(k)\n}\n\n/** @returns {number} */\nexports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec = null, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES) throw new Error('c must \"m.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES\" bytes')\n if (c?.byteLength > 0xffffffff) throw new Error('c.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k)\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\n/** @returns {number} */\nexports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec = null, c, ad, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES) throw new Error('m must \"c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES\" bytes')\n if (m?.byteLength > 0xffffffff) throw new Error('m.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k)\n if (res < 0) throw new Error('could not verify data')\n\n return res\n}\n\n/** @returns {number} */\nexports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function (c, mac, m, ad, nsec = null, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_ABYTES) throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k)\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\nexports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function (m, nsec = null, c, mac, ad, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_ABYTES) throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k)\n if (res !== 0) throw new Error('could not verify data')\n}\n\nexports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) {\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n binding.crypto_aead_chacha20poly1305_ietf_keygen(k)\n}\n\n/** @returns {number} */\nexports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec = null, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES) throw new Error('c must \"m.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES\" bytes')\n if (c?.byteLength > 0xffffffff) throw new Error('c.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k)\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\n/** @returns {number} */\nexports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec = null, c, ad, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES) throw new Error('m must \"c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES\" bytes')\n if (m?.byteLength > 0xffffffff) throw new Error('m.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k)\n if (res < 0) throw new Error('could not verify data')\n\n return res\n}\n\n/** @returns {number} */\nexports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function (c, mac, m, ad, nsec = null, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_ABYTES) throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k)\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\nexports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function (m, nsec = null, c, mac, ad, npub, k) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength) throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_ABYTES) throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES) throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k)\n if (res !== 0) throw new Error('could not verify data')\n}\n\n// crypto_stream\n\nexports.crypto_stream_xor_wrap_init = function (state, n, k) {\n if (state?.byteLength !== binding.sn_crypto_stream_xor_STATEBYTES) {\n throw new Error(\"state must be 'sn_crypto_stream_xor_STATEBYTES' bytes\")\n }\n if (n?.byteLength !== binding.crypto_stream_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_KEYBYTES) throw new Error('k')\n\n binding.crypto_stream_xor_wrap_init(state, n, k)\n}\n\nexports.crypto_stream_xor_wrap_update = function (state, c, m) {\n if (state?.byteLength !== binding.sn_crypto_stream_xor_STATEBYTES) {\n throw new Error(\"state must be 'sn_crypto_stream_xor_STATEBYTES' bytes\")\n }\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n\n binding.crypto_stream_xor_wrap_update(state, c, m)\n}\n\nexports.crypto_stream_xor_wrap_final = function (state) {\n if (state?.byteLength !== binding.sn_crypto_stream_xor_STATEBYTES) {\n throw new Error(\"state must be 'sn_crypto_stream_xor_STATEBYTES' bytes\")\n }\n\n binding.crypto_stream_xor_wrap_final(state)\n}\n\nexports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\")\n }\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES) throw new Error('k')\n\n binding.crypto_stream_chacha20_xor_wrap_init(state, n, k)\n}\n\nexports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\")\n }\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n\n binding.crypto_stream_chacha20_xor_wrap_update(state, c, m)\n}\n\nexports.crypto_stream_chacha20_xor_wrap_final = function (state) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\")\n }\n\n binding.crypto_stream_chacha20_xor_wrap_final(state)\n}\n\nexports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\")\n }\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES) throw new Error('k')\n\n binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k)\n}\n\nexports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\")\n }\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n\n binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m)\n}\n\nexports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) {\n if (state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\")\n }\n\n binding.crypto_stream_chacha20_ietf_xor_wrap_final(state)\n}\n\nexports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) {\n if (state?.byteLength !== binding.crypto_stream_xchacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\")\n }\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES) throw new Error('k')\n\n binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k)\n}\n\nexports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) {\n if (state?.byteLength !== binding.crypto_stream_xchacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\")\n }\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n\n binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m)\n}\n\nexports.crypto_stream_xchacha20_xor_wrap_final = function (state) {\n if (state?.byteLength !== binding.crypto_stream_xchacha20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\")\n }\n\n binding.crypto_stream_xchacha20_xor_wrap_final(state)\n}\n\nexports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) {\n if (state?.byteLength !== binding.crypto_stream_salsa20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\")\n }\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES) throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES) throw new Error('k')\n\n binding.crypto_stream_salsa20_xor_wrap_init(state, n, k)\n}\n\nexports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) {\n if (state?.byteLength !== binding.crypto_stream_salsa20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\")\n }\n if (c?.byteLength !== m.byteLength) throw new Error('c must be \"m.byteLength\" bytes')\n\n binding.crypto_stream_salsa20_xor_wrap_update(state, c, m)\n}\n\nexports.crypto_stream_salsa20_xor_wrap_final = function (state) {\n if (state?.byteLength !== binding.crypto_stream_salsa20_xor_STATEBYTES) {\n throw new Error(\"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\")\n }\n\n binding.crypto_stream_salsa20_xor_wrap_final(state)\n}\n\n// experimental\n\nexports.extension_tweak_ed25519_base = function (n, p, ns) {\n if (n?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('n')\n if (p?.byteLength !== binding.extension_tweak_ed25519_BYTES) throw new Error('p')\n\n binding.extension_tweak_ed25519_base(n, p, ns)\n}\n\nexports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) {\n if (sig?.byteLength !== binding.crypto_sign_BYTES) throw new Error('sig')\n if (scalar?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar')\n if (pk && pk.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk)\n if (res !== 0) throw new Error('failed to compute signature')\n}\n\nexports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) {\n if (n?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('n')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES) throw new Error('sk')\n\n binding.extension_tweak_ed25519_sk_to_scalar(n, sk)\n}\n\nexports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) {\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_out')\n if (scalar?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar')\n\n binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns)\n}\n\nexports.extension_tweak_ed25519_pk = function (tpk, pk, ns) {\n if (tpk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('tpk')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n\n const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns)\n if (res !== 0) throw new Error('failed to tweak public key')\n}\n\nexports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) {\n if (pk?.byteLength !== binding.extension_tweak_ed25519_BYTES) throw new Error('pk')\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_out')\n if (scalarIn?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_in')\n\n binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns)\n}\n\nexports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) {\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_out')\n if (scalar?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar')\n if (n?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('n')\n\n binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n)\n}\n\nexports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) {\n if (tpk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('tpk')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('pk')\n if (p?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES) throw new Error('p')\n\n const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p)\n if (res !== 0) throw new Error('failed to add tweak to public key')\n}\n\nexports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) {\n if (pk?.byteLength !== binding.extension_tweak_ed25519_BYTES) throw new Error('pk')\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_out')\n if (scalarIn?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('scalar_in')\n if (tweak?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES) throw new Error('tweak')\n\n const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak)\n if (res !== 0) throw new Error('failed to add tweak to keypair')\n}\n\n/** @returns {Promise|undefined} */\nexports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback = undefined) {\n if (iter < binding.extension_pbkdf2_sha512_ITERATIONS_MIN) throw new Error('iterations')\n if (outlen > binding.extension_pbkdf2_sha512_BYTES_MAX) throw new Error('outlen')\n if (out?.byteLength < outlen) throw new Error('out')\n if (!out?.byteLength) throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (!salt?.byteLength) throw new Error('salt')\n\n const [done, promise] = checkStatus(callback)\n\n binding.extension_pbkdf2_sha512_async(\n out.buffer,\n out.byteOffset,\n out.byteLength,\n\n passwd.buffer,\n passwd.byteOffset,\n passwd.byteLength,\n\n salt.buffer,\n salt.byteOffset,\n salt.byteLength,\n\n iter,\n outlen,\n\n done\n )\n\n return promise\n}\n\nexports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) {\n if (iter < binding.extension_pbkdf2_sha512_ITERATIONS_MIN) throw new Error('iterations')\n if (outlen > binding.extension_pbkdf2_sha512_BYTES_MAX) throw new Error('outlen')\n if (out?.byteLength < outlen) throw new Error('out')\n\n const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen)\n\n if (res !== 0) throw new Error('failed to add tweak to public key')\n}\n\n// mimic 4.x public api\nfunction checkStatus (callback, booleanResult = false) {\n let done, promise\n\n if (typeof callback === 'function') {\n done = function (status) {\n if (booleanResult) callback(null, status === 0)\n else if (status === 0) callback(null)\n else callback(new Error('status: ' + status))\n }\n } else {\n promise = new Promise(function (resolve, reject) {\n done = function (status) {\n if (booleanResult) resolve(status === 0)\n else if (status === 0) resolve()\n else reject(new Error('status: ' + status))\n }\n })\n }\n\n return [done, promise]\n}\n{\n \"name\": \"sodium-native\",\n \"version\": \"5.0.6\",\n \"description\": \"Low level bindings for libsodium\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"binding.cc\",\n \"binding.js\",\n \"extensions\",\n \"prebuilds\",\n \"CMakeLists.txt\"\n ],\n \"addon\": true,\n \"dependencies\": {\n \"require-addon\": \"^1.1.0\",\n \"which-runtime\": \"^1.2.1\"\n },\n \"devDependencies\": {\n \"bare-compat-napi\": \"^1.3.5\",\n \"brittle\": \"^3.16.2\",\n \"cmake-bare\": \"^1.6.1\",\n \"cmake-fetch\": \"^1.4.3\",\n \"cmake-napi\": \"^1.2.1\",\n \"standard\": \"^17.1.2\"\n },\n \"scripts\": {\n \"test\": \"standard && npm run test:node && npm run test:bare\",\n \"test:node\": \"node test/all.js\",\n \"test:bare\": \"bare test/all.js\"\n },\n \"standard\": {\n \"ignore\": [\n \"/test/fixtures/*.js\"\n ]\n },\n \"engines\": {\n \"bare\": \">=1.16.0\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/sodium-native.git\"\n },\n \"contributors\": [\n \"Emil Bay (http://bayes.dk)\",\n \"Mathias Buus (https://mafinto.sh)\",\n \"Christophe Diederichs \"\n ],\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/sodium-native/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/sodium-native\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\n\nconst ABYTES = sodium.crypto_secretstream_xchacha20poly1305_ABYTES\nconst TAG_MESSAGE = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE\nconst TAG_FINAL = sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL\nconst STATEBYTES = sodium.crypto_secretstream_xchacha20poly1305_STATEBYTES\nconst HEADERBYTES = sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES\nconst KEYBYTES = sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES\nconst TAG_FINAL_BYTE = b4a.isBuffer(TAG_FINAL) ? TAG_FINAL[0] : TAG_FINAL\n\nconst EMPTY = b4a.alloc(0)\nconst TAG = b4a.alloc(1)\n\nclass Push {\n constructor (key, state = b4a.allocUnsafeSlow(STATEBYTES), header = b4a.allocUnsafeSlow(HEADERBYTES)) {\n if (!TAG_FINAL) throw new Error('JavaScript sodium version needs to support crypto_secretstream_xchacha20poly')\n\n this.key = key\n this.state = state\n this.header = header\n\n sodium.crypto_secretstream_xchacha20poly1305_init_push(this.state, this.header, this.key)\n }\n\n next (message, cipher = b4a.allocUnsafe(message.byteLength + ABYTES)) {\n sodium.crypto_secretstream_xchacha20poly1305_push(this.state, cipher, message, null, TAG_MESSAGE)\n return cipher\n }\n\n final (message = EMPTY, cipher = b4a.allocUnsafe(ABYTES)) {\n sodium.crypto_secretstream_xchacha20poly1305_push(this.state, cipher, message, null, TAG_FINAL)\n return cipher\n }\n}\n\nclass Pull {\n constructor (key, state = b4a.allocUnsafeSlow(STATEBYTES)) {\n if (!TAG_FINAL) throw new Error('JavaScript sodium version needs to support crypto_secretstream_xchacha20poly')\n\n this.key = key\n this.state = state\n this.final = false\n }\n\n init (header) {\n sodium.crypto_secretstream_xchacha20poly1305_init_pull(this.state, header, this.key)\n }\n\n next (cipher, message = b4a.allocUnsafe(cipher.byteLength - ABYTES)) {\n sodium.crypto_secretstream_xchacha20poly1305_pull(this.state, message, TAG, cipher, null)\n this.final = TAG[0] === TAG_FINAL_BYTE\n return message\n }\n}\n\nfunction keygen (buf = b4a.alloc(KEYBYTES)) {\n sodium.crypto_secretstream_xchacha20poly1305_keygen(buf)\n return buf\n}\n\nmodule.exports = {\n keygen,\n KEYBYTES,\n ABYTES,\n STATEBYTES,\n HEADERBYTES,\n Push,\n Pull\n}\n{\n \"name\": \"sodium-secretstream\",\n \"version\": \"1.2.0\",\n \"description\": \"Wraps libsodiums secretstream in a higher level abstraction\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.1.1\",\n \"sodium-universal\": \"^5.0.0\"\n },\n \"devDependencies\": {\n \"standard\": \"^17.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/sodium-secretstream.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/sodium-secretstream/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/sodium-secretstream\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = require('sodium-native')\n{\n \"name\": \"sodium-universal\",\n \"version\": \"5.0.1\",\n \"description\": \"Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"sodium-native\": \"^5.0.1\"\n },\n \"peerDependencies\": {\n \"sodium-javascript\": \"~0.8.0\"\n },\n \"peerDependenciesMeta\": {\n \"sodium-javascript\": {\n \"optional\": true\n }\n },\n \"scripts\": {\n \"prepublish\": \"./build-scripts/generate.js\"\n },\n \"browser\": {\n \"sodium-native\": \"sodium-javascript\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/sodium-universal.git\"\n },\n \"keywords\": [\n \"libsodium\",\n \"sodium\",\n \"sodium-native\",\n \"sodium-javascript\",\n \"browserify\"\n ],\n \"contributors\": [\n \"Emil Bay (http://bayes.dk)\",\n \"Mathias Buus (https://mafinto.sh)\",\n \"Christophe Diederichs \"\n ],\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/sodium-universal/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/sodium-universal#readme\",\n \"react-native\": {\n \"sodium-native\": \"sodium-javascript\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst { EventEmitter } = require('events')\nconst STREAM_DESTROYED = new Error('Stream was destroyed')\nconst PREMATURE_CLOSE = new Error('Premature close')\n\nconst FIFO = require('fast-fifo')\nconst TextDecoder = require('text-decoder')\n\n// if we do a future major, expect queue microtask to be there always, for now a bit defensive\nconst qmt = typeof queueMicrotask === 'undefined' ? fn => global.process.nextTick(fn) : queueMicrotask\n\n/* eslint-disable no-multi-spaces */\n\n// 29 bits used total (4 from shared, 14 from read, and 11 from write)\nconst MAX = ((1 << 29) - 1)\n\n// Shared state\nconst OPENING = 0b0001\nconst PREDESTROYING = 0b0010\nconst DESTROYING = 0b0100\nconst DESTROYED = 0b1000\n\nconst NOT_OPENING = MAX ^ OPENING\nconst NOT_PREDESTROYING = MAX ^ PREDESTROYING\n\n// Read state (4 bit offset from shared state)\nconst READ_ACTIVE = 0b00000000000001 << 4\nconst READ_UPDATING = 0b00000000000010 << 4\nconst READ_PRIMARY = 0b00000000000100 << 4\nconst READ_QUEUED = 0b00000000001000 << 4\nconst READ_RESUMED = 0b00000000010000 << 4\nconst READ_PIPE_DRAINED = 0b00000000100000 << 4\nconst READ_ENDING = 0b00000001000000 << 4\nconst READ_EMIT_DATA = 0b00000010000000 << 4\nconst READ_EMIT_READABLE = 0b00000100000000 << 4\nconst READ_EMITTED_READABLE = 0b00001000000000 << 4\nconst READ_DONE = 0b00010000000000 << 4\nconst READ_NEXT_TICK = 0b00100000000000 << 4\nconst READ_NEEDS_PUSH = 0b01000000000000 << 4\nconst READ_READ_AHEAD = 0b10000000000000 << 4\n\n// Combined read state\nconst READ_FLOWING = READ_RESUMED | READ_PIPE_DRAINED\nconst READ_ACTIVE_AND_NEEDS_PUSH = READ_ACTIVE | READ_NEEDS_PUSH\nconst READ_PRIMARY_AND_ACTIVE = READ_PRIMARY | READ_ACTIVE\nconst READ_EMIT_READABLE_AND_QUEUED = READ_EMIT_READABLE | READ_QUEUED\nconst READ_RESUMED_READ_AHEAD = READ_RESUMED | READ_READ_AHEAD\n\nconst READ_NOT_ACTIVE = MAX ^ READ_ACTIVE\nconst READ_NON_PRIMARY = MAX ^ READ_PRIMARY\nconst READ_NON_PRIMARY_AND_PUSHED = MAX ^ (READ_PRIMARY | READ_NEEDS_PUSH)\nconst READ_PUSHED = MAX ^ READ_NEEDS_PUSH\nconst READ_PAUSED = MAX ^ READ_RESUMED\nconst READ_NOT_QUEUED = MAX ^ (READ_QUEUED | READ_EMITTED_READABLE)\nconst READ_NOT_ENDING = MAX ^ READ_ENDING\nconst READ_PIPE_NOT_DRAINED = MAX ^ READ_FLOWING\nconst READ_NOT_NEXT_TICK = MAX ^ READ_NEXT_TICK\nconst READ_NOT_UPDATING = MAX ^ READ_UPDATING\nconst READ_NO_READ_AHEAD = MAX ^ READ_READ_AHEAD\nconst READ_PAUSED_NO_READ_AHEAD = MAX ^ READ_RESUMED_READ_AHEAD\n\n// Write state (18 bit offset, 4 bit offset from shared state and 14 from read state)\nconst WRITE_ACTIVE = 0b00000000001 << 18\nconst WRITE_UPDATING = 0b00000000010 << 18\nconst WRITE_PRIMARY = 0b00000000100 << 18\nconst WRITE_QUEUED = 0b00000001000 << 18\nconst WRITE_UNDRAINED = 0b00000010000 << 18\nconst WRITE_DONE = 0b00000100000 << 18\nconst WRITE_EMIT_DRAIN = 0b00001000000 << 18\nconst WRITE_NEXT_TICK = 0b00010000000 << 18\nconst WRITE_WRITING = 0b00100000000 << 18\nconst WRITE_FINISHING = 0b01000000000 << 18\nconst WRITE_CORKED = 0b10000000000 << 18\n\nconst WRITE_NOT_ACTIVE = MAX ^ (WRITE_ACTIVE | WRITE_WRITING)\nconst WRITE_NON_PRIMARY = MAX ^ WRITE_PRIMARY\nconst WRITE_NOT_FINISHING = MAX ^ (WRITE_ACTIVE | WRITE_FINISHING)\nconst WRITE_DRAINED = MAX ^ WRITE_UNDRAINED\nconst WRITE_NOT_QUEUED = MAX ^ WRITE_QUEUED\nconst WRITE_NOT_NEXT_TICK = MAX ^ WRITE_NEXT_TICK\nconst WRITE_NOT_UPDATING = MAX ^ WRITE_UPDATING\nconst WRITE_NOT_CORKED = MAX ^ WRITE_CORKED\n\n// Combined shared state\nconst ACTIVE = READ_ACTIVE | WRITE_ACTIVE\nconst NOT_ACTIVE = MAX ^ ACTIVE\nconst DONE = READ_DONE | WRITE_DONE\nconst DESTROY_STATUS = DESTROYING | DESTROYED | PREDESTROYING\nconst OPEN_STATUS = DESTROY_STATUS | OPENING\nconst AUTO_DESTROY = DESTROY_STATUS | DONE\nconst NON_PRIMARY = WRITE_NON_PRIMARY & READ_NON_PRIMARY\nconst ACTIVE_OR_TICKING = WRITE_NEXT_TICK | READ_NEXT_TICK\nconst TICKING = ACTIVE_OR_TICKING & NOT_ACTIVE\nconst IS_OPENING = OPEN_STATUS | TICKING\n\n// Combined shared state and read state\nconst READ_PRIMARY_STATUS = OPEN_STATUS | READ_ENDING | READ_DONE\nconst READ_STATUS = OPEN_STATUS | READ_DONE | READ_QUEUED\nconst READ_ENDING_STATUS = OPEN_STATUS | READ_ENDING | READ_QUEUED\nconst READ_READABLE_STATUS = OPEN_STATUS | READ_EMIT_READABLE | READ_QUEUED | READ_EMITTED_READABLE\nconst SHOULD_NOT_READ = OPEN_STATUS | READ_ACTIVE | READ_ENDING | READ_DONE | READ_NEEDS_PUSH | READ_READ_AHEAD\nconst READ_BACKPRESSURE_STATUS = DESTROY_STATUS | READ_ENDING | READ_DONE\nconst READ_UPDATE_SYNC_STATUS = READ_UPDATING | OPEN_STATUS | READ_NEXT_TICK | READ_PRIMARY\nconst READ_NEXT_TICK_OR_OPENING = READ_NEXT_TICK | OPENING\n\n// Combined write state\nconst WRITE_PRIMARY_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_DONE\nconst WRITE_QUEUED_AND_UNDRAINED = WRITE_QUEUED | WRITE_UNDRAINED\nconst WRITE_QUEUED_AND_ACTIVE = WRITE_QUEUED | WRITE_ACTIVE\nconst WRITE_DRAIN_STATUS = WRITE_QUEUED | WRITE_UNDRAINED | OPEN_STATUS | WRITE_ACTIVE\nconst WRITE_STATUS = OPEN_STATUS | WRITE_ACTIVE | WRITE_QUEUED | WRITE_CORKED\nconst WRITE_PRIMARY_AND_ACTIVE = WRITE_PRIMARY | WRITE_ACTIVE\nconst WRITE_ACTIVE_AND_WRITING = WRITE_ACTIVE | WRITE_WRITING\nconst WRITE_FINISHING_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_QUEUED_AND_ACTIVE | WRITE_DONE\nconst WRITE_BACKPRESSURE_STATUS = WRITE_UNDRAINED | DESTROY_STATUS | WRITE_FINISHING | WRITE_DONE\nconst WRITE_UPDATE_SYNC_STATUS = WRITE_UPDATING | OPEN_STATUS | WRITE_NEXT_TICK | WRITE_PRIMARY\nconst WRITE_DROP_DATA = WRITE_FINISHING | WRITE_DONE | DESTROY_STATUS\n\nconst asyncIterator = Symbol.asyncIterator || Symbol('asyncIterator')\n\nclass WritableState {\n constructor (stream, { highWaterMark = 16384, map = null, mapWritable, byteLength, byteLengthWritable } = {}) {\n this.stream = stream\n this.queue = new FIFO()\n this.highWaterMark = highWaterMark\n this.buffered = 0\n this.error = null\n this.pipeline = null\n this.drains = null // if we add more seldomly used helpers we might them into a subobject so its a single ptr\n this.byteLength = byteLengthWritable || byteLength || defaultByteLength\n this.map = mapWritable || map\n this.afterWrite = afterWrite.bind(this)\n this.afterUpdateNextTick = updateWriteNT.bind(this)\n }\n\n get ended () {\n return (this.stream._duplexState & WRITE_DONE) !== 0\n }\n\n push (data) {\n if ((this.stream._duplexState & WRITE_DROP_DATA) !== 0) return false\n if (this.map !== null) data = this.map(data)\n\n this.buffered += this.byteLength(data)\n this.queue.push(data)\n\n if (this.buffered < this.highWaterMark) {\n this.stream._duplexState |= WRITE_QUEUED\n return true\n }\n\n this.stream._duplexState |= WRITE_QUEUED_AND_UNDRAINED\n return false\n }\n\n shift () {\n const data = this.queue.shift()\n\n this.buffered -= this.byteLength(data)\n if (this.buffered === 0) this.stream._duplexState &= WRITE_NOT_QUEUED\n\n return data\n }\n\n end (data) {\n if (typeof data === 'function') this.stream.once('finish', data)\n else if (data !== undefined && data !== null) this.push(data)\n this.stream._duplexState = (this.stream._duplexState | WRITE_FINISHING) & WRITE_NON_PRIMARY\n }\n\n autoBatch (data, cb) {\n const buffer = []\n const stream = this.stream\n\n buffer.push(data)\n while ((stream._duplexState & WRITE_STATUS) === WRITE_QUEUED_AND_ACTIVE) {\n buffer.push(stream._writableState.shift())\n }\n\n if ((stream._duplexState & OPEN_STATUS) !== 0) return cb(null)\n stream._writev(buffer, cb)\n }\n\n update () {\n const stream = this.stream\n\n stream._duplexState |= WRITE_UPDATING\n\n do {\n while ((stream._duplexState & WRITE_STATUS) === WRITE_QUEUED) {\n const data = this.shift()\n stream._duplexState |= WRITE_ACTIVE_AND_WRITING\n stream._write(data, this.afterWrite)\n }\n\n if ((stream._duplexState & WRITE_PRIMARY_AND_ACTIVE) === 0) this.updateNonPrimary()\n } while (this.continueUpdate() === true)\n\n stream._duplexState &= WRITE_NOT_UPDATING\n }\n\n updateNonPrimary () {\n const stream = this.stream\n\n if ((stream._duplexState & WRITE_FINISHING_STATUS) === WRITE_FINISHING) {\n stream._duplexState = stream._duplexState | WRITE_ACTIVE\n stream._final(afterFinal.bind(this))\n return\n }\n\n if ((stream._duplexState & DESTROY_STATUS) === DESTROYING) {\n if ((stream._duplexState & ACTIVE_OR_TICKING) === 0) {\n stream._duplexState |= ACTIVE\n stream._destroy(afterDestroy.bind(this))\n }\n return\n }\n\n if ((stream._duplexState & IS_OPENING) === OPENING) {\n stream._duplexState = (stream._duplexState | ACTIVE) & NOT_OPENING\n stream._open(afterOpen.bind(this))\n }\n }\n\n continueUpdate () {\n if ((this.stream._duplexState & WRITE_NEXT_TICK) === 0) return false\n this.stream._duplexState &= WRITE_NOT_NEXT_TICK\n return true\n }\n\n updateCallback () {\n if ((this.stream._duplexState & WRITE_UPDATE_SYNC_STATUS) === WRITE_PRIMARY) this.update()\n else this.updateNextTick()\n }\n\n updateNextTick () {\n if ((this.stream._duplexState & WRITE_NEXT_TICK) !== 0) return\n this.stream._duplexState |= WRITE_NEXT_TICK\n if ((this.stream._duplexState & WRITE_UPDATING) === 0) qmt(this.afterUpdateNextTick)\n }\n}\n\nclass ReadableState {\n constructor (stream, { highWaterMark = 16384, map = null, mapReadable, byteLength, byteLengthReadable } = {}) {\n this.stream = stream\n this.queue = new FIFO()\n this.highWaterMark = highWaterMark === 0 ? 1 : highWaterMark\n this.buffered = 0\n this.readAhead = highWaterMark > 0\n this.error = null\n this.pipeline = null\n this.byteLength = byteLengthReadable || byteLength || defaultByteLength\n this.map = mapReadable || map\n this.pipeTo = null\n this.afterRead = afterRead.bind(this)\n this.afterUpdateNextTick = updateReadNT.bind(this)\n }\n\n get ended () {\n return (this.stream._duplexState & READ_DONE) !== 0\n }\n\n pipe (pipeTo, cb) {\n if (this.pipeTo !== null) throw new Error('Can only pipe to one destination')\n if (typeof cb !== 'function') cb = null\n\n this.stream._duplexState |= READ_PIPE_DRAINED\n this.pipeTo = pipeTo\n this.pipeline = new Pipeline(this.stream, pipeTo, cb)\n\n if (cb) this.stream.on('error', noop) // We already error handle this so supress crashes\n\n if (isStreamx(pipeTo)) {\n pipeTo._writableState.pipeline = this.pipeline\n if (cb) pipeTo.on('error', noop) // We already error handle this so supress crashes\n pipeTo.on('finish', this.pipeline.finished.bind(this.pipeline)) // TODO: just call finished from pipeTo itself\n } else {\n const onerror = this.pipeline.done.bind(this.pipeline, pipeTo)\n const onclose = this.pipeline.done.bind(this.pipeline, pipeTo, null) // onclose has a weird bool arg\n pipeTo.on('error', onerror)\n pipeTo.on('close', onclose)\n pipeTo.on('finish', this.pipeline.finished.bind(this.pipeline))\n }\n\n pipeTo.on('drain', afterDrain.bind(this))\n this.stream.emit('piping', pipeTo)\n pipeTo.emit('pipe', this.stream)\n }\n\n push (data) {\n const stream = this.stream\n\n if (data === null) {\n this.highWaterMark = 0\n stream._duplexState = (stream._duplexState | READ_ENDING) & READ_NON_PRIMARY_AND_PUSHED\n return false\n }\n\n if (this.map !== null) {\n data = this.map(data)\n if (data === null) {\n stream._duplexState &= READ_PUSHED\n return this.buffered < this.highWaterMark\n }\n }\n\n this.buffered += this.byteLength(data)\n this.queue.push(data)\n\n stream._duplexState = (stream._duplexState | READ_QUEUED) & READ_PUSHED\n\n return this.buffered < this.highWaterMark\n }\n\n shift () {\n const data = this.queue.shift()\n\n this.buffered -= this.byteLength(data)\n if (this.buffered === 0) this.stream._duplexState &= READ_NOT_QUEUED\n return data\n }\n\n unshift (data) {\n const pending = [this.map !== null ? this.map(data) : data]\n while (this.buffered > 0) pending.push(this.shift())\n\n for (let i = 0; i < pending.length - 1; i++) {\n const data = pending[i]\n this.buffered += this.byteLength(data)\n this.queue.push(data)\n }\n\n this.push(pending[pending.length - 1])\n }\n\n read () {\n const stream = this.stream\n\n if ((stream._duplexState & READ_STATUS) === READ_QUEUED) {\n const data = this.shift()\n if (this.pipeTo !== null && this.pipeTo.write(data) === false) stream._duplexState &= READ_PIPE_NOT_DRAINED\n if ((stream._duplexState & READ_EMIT_DATA) !== 0) stream.emit('data', data)\n return data\n }\n\n if (this.readAhead === false) {\n stream._duplexState |= READ_READ_AHEAD\n this.updateNextTick()\n }\n\n return null\n }\n\n drain () {\n const stream = this.stream\n\n while ((stream._duplexState & READ_STATUS) === READ_QUEUED && (stream._duplexState & READ_FLOWING) !== 0) {\n const data = this.shift()\n if (this.pipeTo !== null && this.pipeTo.write(data) === false) stream._duplexState &= READ_PIPE_NOT_DRAINED\n if ((stream._duplexState & READ_EMIT_DATA) !== 0) stream.emit('data', data)\n }\n }\n\n update () {\n const stream = this.stream\n\n stream._duplexState |= READ_UPDATING\n\n do {\n this.drain()\n\n while (this.buffered < this.highWaterMark && (stream._duplexState & SHOULD_NOT_READ) === READ_READ_AHEAD) {\n stream._duplexState |= READ_ACTIVE_AND_NEEDS_PUSH\n stream._read(this.afterRead)\n this.drain()\n }\n\n if ((stream._duplexState & READ_READABLE_STATUS) === READ_EMIT_READABLE_AND_QUEUED) {\n stream._duplexState |= READ_EMITTED_READABLE\n stream.emit('readable')\n }\n\n if ((stream._duplexState & READ_PRIMARY_AND_ACTIVE) === 0) this.updateNonPrimary()\n } while (this.continueUpdate() === true)\n\n stream._duplexState &= READ_NOT_UPDATING\n }\n\n updateNonPrimary () {\n const stream = this.stream\n\n if ((stream._duplexState & READ_ENDING_STATUS) === READ_ENDING) {\n stream._duplexState = (stream._duplexState | READ_DONE) & READ_NOT_ENDING\n stream.emit('end')\n if ((stream._duplexState & AUTO_DESTROY) === DONE) stream._duplexState |= DESTROYING\n if (this.pipeTo !== null) this.pipeTo.end()\n }\n\n if ((stream._duplexState & DESTROY_STATUS) === DESTROYING) {\n if ((stream._duplexState & ACTIVE_OR_TICKING) === 0) {\n stream._duplexState |= ACTIVE\n stream._destroy(afterDestroy.bind(this))\n }\n return\n }\n\n if ((stream._duplexState & IS_OPENING) === OPENING) {\n stream._duplexState = (stream._duplexState | ACTIVE) & NOT_OPENING\n stream._open(afterOpen.bind(this))\n }\n }\n\n continueUpdate () {\n if ((this.stream._duplexState & READ_NEXT_TICK) === 0) return false\n this.stream._duplexState &= READ_NOT_NEXT_TICK\n return true\n }\n\n updateCallback () {\n if ((this.stream._duplexState & READ_UPDATE_SYNC_STATUS) === READ_PRIMARY) this.update()\n else this.updateNextTick()\n }\n\n updateNextTickIfOpen () {\n if ((this.stream._duplexState & READ_NEXT_TICK_OR_OPENING) !== 0) return\n this.stream._duplexState |= READ_NEXT_TICK\n if ((this.stream._duplexState & READ_UPDATING) === 0) qmt(this.afterUpdateNextTick)\n }\n\n updateNextTick () {\n if ((this.stream._duplexState & READ_NEXT_TICK) !== 0) return\n this.stream._duplexState |= READ_NEXT_TICK\n if ((this.stream._duplexState & READ_UPDATING) === 0) qmt(this.afterUpdateNextTick)\n }\n}\n\nclass TransformState {\n constructor (stream) {\n this.data = null\n this.afterTransform = afterTransform.bind(stream)\n this.afterFinal = null\n }\n}\n\nclass Pipeline {\n constructor (src, dst, cb) {\n this.from = src\n this.to = dst\n this.afterPipe = cb\n this.error = null\n this.pipeToFinished = false\n }\n\n finished () {\n this.pipeToFinished = true\n }\n\n done (stream, err) {\n if (err) this.error = err\n\n if (stream === this.to) {\n this.to = null\n\n if (this.from !== null) {\n if ((this.from._duplexState & READ_DONE) === 0 || !this.pipeToFinished) {\n this.from.destroy(this.error || new Error('Writable stream closed prematurely'))\n }\n return\n }\n }\n\n if (stream === this.from) {\n this.from = null\n\n if (this.to !== null) {\n if ((stream._duplexState & READ_DONE) === 0) {\n this.to.destroy(this.error || new Error('Readable stream closed before ending'))\n }\n return\n }\n }\n\n if (this.afterPipe !== null) this.afterPipe(this.error)\n this.to = this.from = this.afterPipe = null\n }\n}\n\nfunction afterDrain () {\n this.stream._duplexState |= READ_PIPE_DRAINED\n this.updateCallback()\n}\n\nfunction afterFinal (err) {\n const stream = this.stream\n if (err) stream.destroy(err)\n if ((stream._duplexState & DESTROY_STATUS) === 0) {\n stream._duplexState |= WRITE_DONE\n stream.emit('finish')\n }\n if ((stream._duplexState & AUTO_DESTROY) === DONE) {\n stream._duplexState |= DESTROYING\n }\n\n stream._duplexState &= WRITE_NOT_FINISHING\n\n // no need to wait the extra tick here, so we short circuit that\n if ((stream._duplexState & WRITE_UPDATING) === 0) this.update()\n else this.updateNextTick()\n}\n\nfunction afterDestroy (err) {\n const stream = this.stream\n\n if (!err && this.error !== STREAM_DESTROYED) err = this.error\n if (err) stream.emit('error', err)\n stream._duplexState |= DESTROYED\n stream.emit('close')\n\n const rs = stream._readableState\n const ws = stream._writableState\n\n if (rs !== null && rs.pipeline !== null) rs.pipeline.done(stream, err)\n\n if (ws !== null) {\n while (ws.drains !== null && ws.drains.length > 0) ws.drains.shift().resolve(false)\n if (ws.pipeline !== null) ws.pipeline.done(stream, err)\n }\n}\n\nfunction afterWrite (err) {\n const stream = this.stream\n\n if (err) stream.destroy(err)\n stream._duplexState &= WRITE_NOT_ACTIVE\n\n if (this.drains !== null) tickDrains(this.drains)\n\n if ((stream._duplexState & WRITE_DRAIN_STATUS) === WRITE_UNDRAINED) {\n stream._duplexState &= WRITE_DRAINED\n if ((stream._duplexState & WRITE_EMIT_DRAIN) === WRITE_EMIT_DRAIN) {\n stream.emit('drain')\n }\n }\n\n this.updateCallback()\n}\n\nfunction afterRead (err) {\n if (err) this.stream.destroy(err)\n this.stream._duplexState &= READ_NOT_ACTIVE\n if (this.readAhead === false && (this.stream._duplexState & READ_RESUMED) === 0) this.stream._duplexState &= READ_NO_READ_AHEAD\n this.updateCallback()\n}\n\nfunction updateReadNT () {\n if ((this.stream._duplexState & READ_UPDATING) === 0) {\n this.stream._duplexState &= READ_NOT_NEXT_TICK\n this.update()\n }\n}\n\nfunction updateWriteNT () {\n if ((this.stream._duplexState & WRITE_UPDATING) === 0) {\n this.stream._duplexState &= WRITE_NOT_NEXT_TICK\n this.update()\n }\n}\n\nfunction tickDrains (drains) {\n for (let i = 0; i < drains.length; i++) {\n // drains.writes are monotonic, so if one is 0 its always the first one\n if (--drains[i].writes === 0) {\n drains.shift().resolve(true)\n i--\n }\n }\n}\n\nfunction afterOpen (err) {\n const stream = this.stream\n\n if (err) stream.destroy(err)\n\n if ((stream._duplexState & DESTROYING) === 0) {\n if ((stream._duplexState & READ_PRIMARY_STATUS) === 0) stream._duplexState |= READ_PRIMARY\n if ((stream._duplexState & WRITE_PRIMARY_STATUS) === 0) stream._duplexState |= WRITE_PRIMARY\n stream.emit('open')\n }\n\n stream._duplexState &= NOT_ACTIVE\n\n if (stream._writableState !== null) {\n stream._writableState.updateCallback()\n }\n\n if (stream._readableState !== null) {\n stream._readableState.updateCallback()\n }\n}\n\nfunction afterTransform (err, data) {\n if (data !== undefined && data !== null) this.push(data)\n this._writableState.afterWrite(err)\n}\n\nfunction newListener (name) {\n if (this._readableState !== null) {\n if (name === 'data') {\n this._duplexState |= (READ_EMIT_DATA | READ_RESUMED_READ_AHEAD)\n this._readableState.updateNextTick()\n }\n if (name === 'readable') {\n this._duplexState |= READ_EMIT_READABLE\n this._readableState.updateNextTick()\n }\n }\n\n if (this._writableState !== null) {\n if (name === 'drain') {\n this._duplexState |= WRITE_EMIT_DRAIN\n this._writableState.updateNextTick()\n }\n }\n}\n\nclass Stream extends EventEmitter {\n constructor (opts) {\n super()\n\n this._duplexState = 0\n this._readableState = null\n this._writableState = null\n\n if (opts) {\n if (opts.open) this._open = opts.open\n if (opts.destroy) this._destroy = opts.destroy\n if (opts.predestroy) this._predestroy = opts.predestroy\n if (opts.signal) {\n opts.signal.addEventListener('abort', abort.bind(this))\n }\n }\n\n this.on('newListener', newListener)\n }\n\n _open (cb) {\n cb(null)\n }\n\n _destroy (cb) {\n cb(null)\n }\n\n _predestroy () {\n // does nothing\n }\n\n get readable () {\n return this._readableState !== null ? true : undefined\n }\n\n get writable () {\n return this._writableState !== null ? true : undefined\n }\n\n get destroyed () {\n return (this._duplexState & DESTROYED) !== 0\n }\n\n get destroying () {\n return (this._duplexState & DESTROY_STATUS) !== 0\n }\n\n destroy (err) {\n if ((this._duplexState & DESTROY_STATUS) === 0) {\n if (!err) err = STREAM_DESTROYED\n this._duplexState = (this._duplexState | DESTROYING) & NON_PRIMARY\n\n if (this._readableState !== null) {\n this._readableState.highWaterMark = 0\n this._readableState.error = err\n }\n if (this._writableState !== null) {\n this._writableState.highWaterMark = 0\n this._writableState.error = err\n }\n\n this._duplexState |= PREDESTROYING\n this._predestroy()\n this._duplexState &= NOT_PREDESTROYING\n\n if (this._readableState !== null) this._readableState.updateNextTick()\n if (this._writableState !== null) this._writableState.updateNextTick()\n }\n }\n}\n\nclass Readable extends Stream {\n constructor (opts) {\n super(opts)\n\n this._duplexState |= OPENING | WRITE_DONE | READ_READ_AHEAD\n this._readableState = new ReadableState(this, opts)\n\n if (opts) {\n if (this._readableState.readAhead === false) this._duplexState &= READ_NO_READ_AHEAD\n if (opts.read) this._read = opts.read\n if (opts.eagerOpen) this._readableState.updateNextTick()\n if (opts.encoding) this.setEncoding(opts.encoding)\n }\n }\n\n setEncoding (encoding) {\n const dec = new TextDecoder(encoding)\n const map = this._readableState.map || echo\n this._readableState.map = mapOrSkip\n return this\n\n function mapOrSkip (data) {\n const next = dec.push(data)\n return next === '' && (data.byteLength !== 0 || dec.remaining > 0) ? null : map(next)\n }\n }\n\n _read (cb) {\n cb(null)\n }\n\n pipe (dest, cb) {\n this._readableState.updateNextTick()\n this._readableState.pipe(dest, cb)\n return dest\n }\n\n read () {\n this._readableState.updateNextTick()\n return this._readableState.read()\n }\n\n push (data) {\n this._readableState.updateNextTickIfOpen()\n return this._readableState.push(data)\n }\n\n unshift (data) {\n this._readableState.updateNextTickIfOpen()\n return this._readableState.unshift(data)\n }\n\n resume () {\n this._duplexState |= READ_RESUMED_READ_AHEAD\n this._readableState.updateNextTick()\n return this\n }\n\n pause () {\n this._duplexState &= (this._readableState.readAhead === false ? READ_PAUSED_NO_READ_AHEAD : READ_PAUSED)\n return this\n }\n\n static _fromAsyncIterator (ite, opts) {\n let destroy\n\n const rs = new Readable({\n ...opts,\n read (cb) {\n ite.next().then(push).then(cb.bind(null, null)).catch(cb)\n },\n predestroy () {\n destroy = ite.return()\n },\n destroy (cb) {\n if (!destroy) return cb(null)\n destroy.then(cb.bind(null, null)).catch(cb)\n }\n })\n\n return rs\n\n function push (data) {\n if (data.done) rs.push(null)\n else rs.push(data.value)\n }\n }\n\n static from (data, opts) {\n if (isReadStreamx(data)) return data\n if (data[asyncIterator]) return this._fromAsyncIterator(data[asyncIterator](), opts)\n if (!Array.isArray(data)) data = data === undefined ? [] : [data]\n\n let i = 0\n return new Readable({\n ...opts,\n read (cb) {\n this.push(i === data.length ? null : data[i++])\n cb(null)\n }\n })\n }\n\n static isBackpressured (rs) {\n return (rs._duplexState & READ_BACKPRESSURE_STATUS) !== 0 || rs._readableState.buffered >= rs._readableState.highWaterMark\n }\n\n static isPaused (rs) {\n return (rs._duplexState & READ_RESUMED) === 0\n }\n\n [asyncIterator] () {\n const stream = this\n\n let error = null\n let promiseResolve = null\n let promiseReject = null\n\n this.on('error', (err) => { error = err })\n this.on('readable', onreadable)\n this.on('close', onclose)\n\n return {\n [asyncIterator] () {\n return this\n },\n next () {\n return new Promise(function (resolve, reject) {\n promiseResolve = resolve\n promiseReject = reject\n const data = stream.read()\n if (data !== null) ondata(data)\n else if ((stream._duplexState & DESTROYED) !== 0) ondata(null)\n })\n },\n return () {\n return destroy(null)\n },\n throw (err) {\n return destroy(err)\n }\n }\n\n function onreadable () {\n if (promiseResolve !== null) ondata(stream.read())\n }\n\n function onclose () {\n if (promiseResolve !== null) ondata(null)\n }\n\n function ondata (data) {\n if (promiseReject === null) return\n if (error) promiseReject(error)\n else if (data === null && (stream._duplexState & READ_DONE) === 0) promiseReject(STREAM_DESTROYED)\n else promiseResolve({ value: data, done: data === null })\n promiseReject = promiseResolve = null\n }\n\n function destroy (err) {\n stream.destroy(err)\n return new Promise((resolve, reject) => {\n if (stream._duplexState & DESTROYED) return resolve({ value: undefined, done: true })\n stream.once('close', function () {\n if (err) reject(err)\n else resolve({ value: undefined, done: true })\n })\n })\n }\n }\n}\n\nclass Writable extends Stream {\n constructor (opts) {\n super(opts)\n\n this._duplexState |= OPENING | READ_DONE\n this._writableState = new WritableState(this, opts)\n\n if (opts) {\n if (opts.writev) this._writev = opts.writev\n if (opts.write) this._write = opts.write\n if (opts.final) this._final = opts.final\n if (opts.eagerOpen) this._writableState.updateNextTick()\n }\n }\n\n cork () {\n this._duplexState |= WRITE_CORKED\n }\n\n uncork () {\n this._duplexState &= WRITE_NOT_CORKED\n this._writableState.updateNextTick()\n }\n\n _writev (batch, cb) {\n cb(null)\n }\n\n _write (data, cb) {\n this._writableState.autoBatch(data, cb)\n }\n\n _final (cb) {\n cb(null)\n }\n\n static isBackpressured (ws) {\n return (ws._duplexState & WRITE_BACKPRESSURE_STATUS) !== 0\n }\n\n static drained (ws) {\n if (ws.destroyed) return Promise.resolve(false)\n const state = ws._writableState\n const pending = (isWritev(ws) ? Math.min(1, state.queue.length) : state.queue.length)\n const writes = pending + ((ws._duplexState & WRITE_WRITING) ? 1 : 0)\n if (writes === 0) return Promise.resolve(true)\n if (state.drains === null) state.drains = []\n return new Promise((resolve) => {\n state.drains.push({ writes, resolve })\n })\n }\n\n write (data) {\n this._writableState.updateNextTick()\n return this._writableState.push(data)\n }\n\n end (data) {\n this._writableState.updateNextTick()\n this._writableState.end(data)\n return this\n }\n}\n\nclass Duplex extends Readable { // and Writable\n constructor (opts) {\n super(opts)\n\n this._duplexState = OPENING | (this._duplexState & READ_READ_AHEAD)\n this._writableState = new WritableState(this, opts)\n\n if (opts) {\n if (opts.writev) this._writev = opts.writev\n if (opts.write) this._write = opts.write\n if (opts.final) this._final = opts.final\n }\n }\n\n cork () {\n this._duplexState |= WRITE_CORKED\n }\n\n uncork () {\n this._duplexState &= WRITE_NOT_CORKED\n this._writableState.updateNextTick()\n }\n\n _writev (batch, cb) {\n cb(null)\n }\n\n _write (data, cb) {\n this._writableState.autoBatch(data, cb)\n }\n\n _final (cb) {\n cb(null)\n }\n\n write (data) {\n this._writableState.updateNextTick()\n return this._writableState.push(data)\n }\n\n end (data) {\n this._writableState.updateNextTick()\n this._writableState.end(data)\n return this\n }\n}\n\nclass Transform extends Duplex {\n constructor (opts) {\n super(opts)\n this._transformState = new TransformState(this)\n\n if (opts) {\n if (opts.transform) this._transform = opts.transform\n if (opts.flush) this._flush = opts.flush\n }\n }\n\n _write (data, cb) {\n if (this._readableState.buffered >= this._readableState.highWaterMark) {\n this._transformState.data = data\n } else {\n this._transform(data, this._transformState.afterTransform)\n }\n }\n\n _read (cb) {\n if (this._transformState.data !== null) {\n const data = this._transformState.data\n this._transformState.data = null\n cb(null)\n this._transform(data, this._transformState.afterTransform)\n } else {\n cb(null)\n }\n }\n\n destroy (err) {\n super.destroy(err)\n if (this._transformState.data !== null) {\n this._transformState.data = null\n this._transformState.afterTransform()\n }\n }\n\n _transform (data, cb) {\n cb(null, data)\n }\n\n _flush (cb) {\n cb(null)\n }\n\n _final (cb) {\n this._transformState.afterFinal = cb\n this._flush(transformAfterFlush.bind(this))\n }\n}\n\nclass PassThrough extends Transform {}\n\nfunction transformAfterFlush (err, data) {\n const cb = this._transformState.afterFinal\n if (err) return cb(err)\n if (data !== null && data !== undefined) this.push(data)\n this.push(null)\n cb(null)\n}\n\nfunction pipelinePromise (...streams) {\n return new Promise((resolve, reject) => {\n return pipeline(...streams, (err) => {\n if (err) return reject(err)\n resolve()\n })\n })\n}\n\nfunction pipeline (stream, ...streams) {\n const all = Array.isArray(stream) ? [...stream, ...streams] : [stream, ...streams]\n const done = (all.length && typeof all[all.length - 1] === 'function') ? all.pop() : null\n\n if (all.length < 2) throw new Error('Pipeline requires at least 2 streams')\n\n let src = all[0]\n let dest = null\n let error = null\n\n for (let i = 1; i < all.length; i++) {\n dest = all[i]\n\n if (isStreamx(src)) {\n src.pipe(dest, onerror)\n } else {\n errorHandle(src, true, i > 1, onerror)\n src.pipe(dest)\n }\n\n src = dest\n }\n\n if (done) {\n let fin = false\n\n const autoDestroy = isStreamx(dest) || !!(dest._writableState && dest._writableState.autoDestroy)\n\n dest.on('error', (err) => {\n if (error === null) error = err\n })\n\n dest.on('finish', () => {\n fin = true\n if (!autoDestroy) done(error)\n })\n\n if (autoDestroy) {\n dest.on('close', () => done(error || (fin ? null : PREMATURE_CLOSE)))\n }\n }\n\n return dest\n\n function errorHandle (s, rd, wr, onerror) {\n s.on('error', onerror)\n s.on('close', onclose)\n\n function onclose () {\n if (rd && s._readableState && !s._readableState.ended) return onerror(PREMATURE_CLOSE)\n if (wr && s._writableState && !s._writableState.ended) return onerror(PREMATURE_CLOSE)\n }\n }\n\n function onerror (err) {\n if (!err || error) return\n error = err\n\n for (const s of all) {\n s.destroy(err)\n }\n }\n}\n\nfunction echo (s) {\n return s\n}\n\nfunction isStream (stream) {\n return !!stream._readableState || !!stream._writableState\n}\n\nfunction isStreamx (stream) {\n return typeof stream._duplexState === 'number' && isStream(stream)\n}\n\nfunction isEnded (stream) {\n return !!stream._readableState && stream._readableState.ended\n}\n\nfunction isFinished (stream) {\n return !!stream._writableState && stream._writableState.ended\n}\n\nfunction getStreamError (stream, opts = {}) {\n const err = (stream._readableState && stream._readableState.error) || (stream._writableState && stream._writableState.error)\n\n // avoid implicit errors by default\n return (!opts.all && err === STREAM_DESTROYED) ? null : err\n}\n\nfunction isReadStreamx (stream) {\n return isStreamx(stream) && stream.readable\n}\n\nfunction isDisturbed (stream) {\n return (stream._duplexState & OPENING) !== OPENING || (stream._duplexState & ACTIVE_OR_TICKING) !== 0\n}\n\nfunction isTypedArray (data) {\n return typeof data === 'object' && data !== null && typeof data.byteLength === 'number'\n}\n\nfunction defaultByteLength (data) {\n return isTypedArray(data) ? data.byteLength : 1024\n}\n\nfunction noop () {}\n\nfunction abort () {\n this.destroy(new Error('Stream aborted.'))\n}\n\nfunction isWritev (s) {\n return s._writev !== Writable.prototype._writev && s._writev !== Duplex.prototype._writev\n}\n\nmodule.exports = {\n pipeline,\n pipelinePromise,\n isStream,\n isStreamx,\n isEnded,\n isFinished,\n isDisturbed,\n getStreamError,\n Stream,\n Writable,\n Readable,\n Duplex,\n Transform,\n // Export PassThrough for compatibility with Node.js core's stream module\n PassThrough\n}\n{\n \"name\": \"streamx\",\n \"version\": \"2.22.1\",\n \"description\": \"An iteration of the Node.js core streams with a series of improvements\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"fast-fifo\": \"^1.3.2\",\n \"text-decoder\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"b4a\": \"^1.6.6\",\n \"brittle\": \"^3.1.1\",\n \"end-of-stream\": \"^1.4.4\",\n \"standard\": \"^17.0.0\"\n },\n \"optionalDependencies\": {\n \"bare-events\": \"^2.2.0\"\n },\n \"files\": [\n \"index.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"scripts\": {\n \"test\": \"standard && brittle test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/streamx.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/streamx/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/streamx\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst PassThroughDecoder = require('./lib/pass-through-decoder')\nconst UTF8Decoder = require('./lib/utf8-decoder')\n\nmodule.exports = class TextDecoder {\n constructor (encoding = 'utf8') {\n this.encoding = normalizeEncoding(encoding)\n\n switch (this.encoding) {\n case 'utf8':\n this.decoder = new UTF8Decoder()\n break\n case 'utf16le':\n case 'base64':\n throw new Error('Unsupported encoding: ' + this.encoding)\n default:\n this.decoder = new PassThroughDecoder(this.encoding)\n }\n }\n\n get remaining () {\n return this.decoder.remaining\n }\n\n push (data) {\n if (typeof data === 'string') return data\n return this.decoder.decode(data)\n }\n\n // For Node.js compatibility\n write (data) {\n return this.push(data)\n }\n\n end (data) {\n let result = ''\n if (data) result = this.push(data)\n result += this.decoder.flush()\n return result\n }\n}\n\nfunction normalizeEncoding (encoding) {\n encoding = encoding.toLowerCase()\n\n switch (encoding) {\n case 'utf8':\n case 'utf-8':\n return 'utf8'\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return 'utf16le'\n case 'latin1':\n case 'binary':\n return 'latin1'\n case 'base64':\n case 'ascii':\n case 'hex':\n return encoding\n default:\n throw new Error('Unknown encoding: ' + encoding)\n }\n};\nconst b4a = require('b4a')\n\nmodule.exports = class PassThroughDecoder {\n constructor (encoding) {\n this.encoding = encoding\n }\n\n get remaining () {\n return 0\n }\n\n decode (tail) {\n return b4a.toString(tail, this.encoding)\n }\n\n flush () {\n return ''\n }\n}\nconst b4a = require('b4a')\n\n/**\n * https://encoding.spec.whatwg.org/#utf-8-decoder\n */\nmodule.exports = class UTF8Decoder {\n constructor () {\n this.codePoint = 0\n this.bytesSeen = 0\n this.bytesNeeded = 0\n this.lowerBoundary = 0x80\n this.upperBoundary = 0xbf\n }\n\n get remaining () {\n return this.bytesSeen\n }\n\n decode (data) {\n // If we have a fast path, just sniff if the last part is a boundary\n if (this.bytesNeeded === 0) {\n let isBoundary = true\n\n for (let i = Math.max(0, data.byteLength - 4), n = data.byteLength; i < n && isBoundary; i++) {\n isBoundary = data[i] <= 0x7f\n }\n\n if (isBoundary) return b4a.toString(data, 'utf8')\n }\n\n let result = ''\n\n for (let i = 0, n = data.byteLength; i < n; i++) {\n const byte = data[i]\n\n if (this.bytesNeeded === 0) {\n if (byte <= 0x7f) {\n result += String.fromCharCode(byte)\n } else {\n this.bytesSeen = 1\n\n if (byte >= 0xc2 && byte <= 0xdf) {\n this.bytesNeeded = 2\n this.codePoint = byte & 0x1f\n } else if (byte >= 0xe0 && byte <= 0xef) {\n if (byte === 0xe0) this.lowerBoundary = 0xa0\n else if (byte === 0xed) this.upperBoundary = 0x9f\n this.bytesNeeded = 3\n this.codePoint = byte & 0xf\n } else if (byte >= 0xf0 && byte <= 0xf4) {\n if (byte === 0xf0) this.lowerBoundary = 0x90\n if (byte === 0xf4) this.upperBoundary = 0x8f\n this.bytesNeeded = 4\n this.codePoint = byte & 0x7\n } else {\n result += '\\ufffd'\n }\n }\n\n continue\n }\n\n if (byte < this.lowerBoundary || byte > this.upperBoundary) {\n this.codePoint = 0\n this.bytesNeeded = 0\n this.bytesSeen = 0\n this.lowerBoundary = 0x80\n this.upperBoundary = 0xbf\n\n result += '\\ufffd'\n\n continue\n }\n\n this.lowerBoundary = 0x80\n this.upperBoundary = 0xbf\n\n this.codePoint = (this.codePoint << 6) | (byte & 0x3f)\n this.bytesSeen++\n\n if (this.bytesSeen !== this.bytesNeeded) continue\n\n result += String.fromCodePoint(this.codePoint)\n\n this.codePoint = 0\n this.bytesNeeded = 0\n this.bytesSeen = 0\n }\n\n return result\n }\n\n flush () {\n const result = this.bytesNeeded > 0 ? '\\ufffd' : ''\n\n this.codePoint = 0\n this.bytesNeeded = 0\n this.bytesSeen = 0\n this.lowerBoundary = 0x80\n this.upperBoundary = 0xbf\n\n return result\n }\n}\n{\n \"name\": \"text-decoder\",\n \"version\": \"1.2.3\",\n \"description\": \"Streaming text decoder that preserves multibyte Unicode characters\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib\"\n ],\n \"browser\": {\n \"./lib/pass-through-decoder.js\": \"./lib/pass-through-decoder.js\",\n \"./lib/utf8-decoder.js\": \"./lib/utf8-decoder.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"react-native\": {\n \"./lib/pass-through-decoder.js\": \"./lib/pass-through-decoder.js\",\n \"./lib/utf8-decoder.js\": \"./lib/utf8-decoder.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/text-decoder.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/text-decoder/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/text-decoder#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.6.4\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"standard\": \"^17.0.0\"\n }\n}\nmodule.exports = class TimeOrderedSet {\n constructor () {\n this.oldest = null\n this.latest = null\n this.length = 0\n }\n\n has (node) {\n return !!(node.next || node.prev) || node === this.oldest\n }\n\n add (node) {\n if (this.has(node)) this.remove(node)\n\n if (!this.latest && !this.oldest) {\n this.latest = this.oldest = node\n node.prev = node.next = null\n } else {\n this.latest.next = node\n node.prev = this.latest\n node.next = null\n this.latest = node\n }\n\n this.length++\n\n return node\n }\n\n remove (node) {\n if (!this.has(node)) return node\n\n if (this.oldest !== node && this.latest !== node) {\n node.prev.next = node.next\n node.next.prev = node.prev\n } else {\n if (this.oldest === node) {\n this.oldest = node.next\n if (this.oldest) this.oldest.prev = null\n }\n if (this.latest === node) {\n this.latest = node.prev\n if (this.latest) this.latest.next = null\n }\n }\n\n node.next = node.prev = null\n this.length--\n\n return node\n }\n\n toArray ({ limit = Infinity, reverse = false } = {}) {\n const list = []\n\n if (reverse) {\n let node = this.latest\n while (node && limit--) {\n list.push(node)\n node = node.prev\n }\n } else {\n let node = this.oldest\n while (node && limit--) {\n list.push(node)\n node = node.next\n }\n }\n\n return list\n }\n}\n{\n \"name\": \"time-ordered-set\",\n \"version\": \"2.0.1\",\n \"description\": \"Efficiently maintain a set of nodes ordered by the time they were added to the set\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"standard\": \"^17.1.2\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/time-ordered-set.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/time-ordered-set/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/time-ordered-set\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = class TimerBrowser {\n constructor (ms, fn, ctx = null, interval = false) {\n this.ms = ms\n this.ontimeout = fn\n this.context = ctx || null\n this.interval = interval\n this.done = false\n\n this._timer = interval\n ? setInterval(callInterval, ms, this)\n : setTimeout(callTimeout, ms, this)\n }\n\n unref () {}\n\n ref () {}\n\n refresh () {\n if (this.done) return\n\n if (this.interval) {\n clearInterval(this._timer)\n this._timer = setInterval(callInterval, this.ms, this)\n } else {\n clearTimeout(this._timer)\n this._timer = setTimeout(callTimeout, this.ms, this)\n }\n }\n\n destroy () {\n this.done = true\n this.ontimeout = null\n\n if (this.interval) clearInterval(this._timer)\n else clearTimeout(this._timer)\n }\n\n static once (ms, fn, ctx) {\n return new this(ms, fn, ctx, false)\n }\n\n static on (ms, fn, ctx) {\n return new this(ms, fn, ctx, true)\n }\n}\n\nfunction callTimeout (self) {\n self.done = true\n self.ontimeout.call(self.context)\n}\n\nfunction callInterval (self) {\n self.ontimeout.call(self.context)\n}\nmodule.exports = isNode()\n ? require('./node')\n : require('./browser')\n\nfunction isNode () {\n const to = setTimeout(function () {}, 1000)\n clearTimeout(to)\n return !!to.refresh\n}\nmodule.exports = class Timer {\n constructor (ms, fn, ctx = null, interval = false) {\n this.ms = ms\n this.ontimeout = fn\n this.context = ctx\n this.interval = interval\n this.done = false\n\n this._timer = interval\n ? setInterval(callInterval, ms, this)\n : setTimeout(callTimeout, ms, this)\n }\n\n unref () {\n this._timer.unref()\n }\n\n ref () {\n this._timer.ref()\n }\n\n refresh () {\n if (this.done !== true) this._timer.refresh()\n }\n\n destroy () {\n this.done = true\n this.ontimeout = null\n if (this.interval) clearInterval(this._timer)\n else clearTimeout(this._timer)\n }\n\n static once (ms, fn, ctx) {\n return new this(ms, fn, ctx, false)\n }\n\n static on (ms, fn, ctx) {\n return new this(ms, fn, ctx, true)\n }\n}\n\nfunction callTimeout (self) {\n self.done = true\n self.ontimeout.call(self.context)\n}\n\nfunction callInterval (self) {\n self.ontimeout.call(self.context)\n}\n{\n \"name\": \"timeout-refresh\",\n \"version\": \"2.0.1\",\n \"description\": \"Efficiently refresh a timer\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^16.0.4\",\n \"tape\": \"^5.5.2\"\n },\n \"browser\": {\n \"index.js\": \"./browser.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/timeout-refresh.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/timeout-refresh/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/timeout-refresh\",\n \"react-native\": {\n \"index.js\": \"./browser.js\",\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nrequire.addon = require('require-addon')\n\nmodule.exports = require.addon('.', __filename)\n// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nconst v4Seg = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'\nconst v4Str = `(${v4Seg}[.]){3}${v4Seg}`\nconst IPv4Pattern = new RegExp(`^${v4Str}$`)\n\nconst v6Seg = '(?:[0-9a-fA-F]{1,4})'\nconst IPv6Pattern = new RegExp('^(' +\n `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` +\n `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` +\n `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` +\n `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` +\n `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` +\n `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` +\n `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` +\n `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` +\n')(%[0-9a-zA-Z-.:]{1,})?$')\n\nconst isIPv4 = exports.isIPv4 = function isIPv4 (host) {\n return IPv4Pattern.test(host)\n}\n\nconst isIPv6 = exports.isIPv6 = function isIPv6 (host) {\n return IPv6Pattern.test(host)\n}\n\nexports.isIP = function isIP (host) {\n if (isIPv4(host)) return 4\n if (isIPv6(host)) return 6\n return 0\n}\nconst events = require('events')\nconst b4a = require('b4a')\nconst binding = require('../binding')\n\nmodule.exports = class NetworkInterfaces extends events.EventEmitter {\n constructor (udx) {\n super()\n\n this._handle = b4a.allocUnsafe(binding.sizeof_udx_napi_interface_event_t)\n this._watching = false\n this._destroying = null\n\n binding.udx_napi_interface_event_init(udx._handle, this._handle, this,\n this._onevent,\n this._onclose\n )\n\n this.interfaces = binding.udx_napi_interface_event_get_addrs(this._handle)\n }\n\n _onclose () {\n this.emit('close')\n }\n\n _onevent () {\n this.interfaces = binding.udx_napi_interface_event_get_addrs(this._handle)\n\n this.emit('change', this.interfaces)\n }\n\n watch () {\n if (this._watching) return this\n this._watching = true\n\n binding.udx_napi_interface_event_start(this._handle)\n\n return this\n }\n\n unwatch () {\n if (!this._watching) return this\n this._watching = false\n\n binding.udx_napi_interface_event_stop(this._handle)\n\n return this\n }\n\n async destroy () {\n if (this._destroying) return this._destroying\n this._destroying = events.once(this, 'close')\n\n binding.udx_napi_interface_event_close(this._handle)\n\n return this._destroying\n }\n\n [Symbol.iterator] () {\n return this.interfaces[Symbol.iterator]()\n }\n}\nconst events = require('events')\nconst b4a = require('b4a')\nconst binding = require('../binding')\nconst ip = require('./ip')\n\nmodule.exports = class UDXSocket extends events.EventEmitter {\n constructor (udx, opts = {}) {\n super()\n\n this.udx = udx\n\n this._handle = b4a.allocUnsafe(binding.sizeof_udx_napi_socket_t)\n this._inited = false\n this._host = null\n this._family = 0\n this._ipv6Only = opts.ipv6Only === true\n this._reuseAddress = opts.reuseAddress === true\n this._port = 0\n this._reqs = []\n this._free = []\n this._closing = null\n this._closed = false\n\n this._view64 = new BigUint64Array(this._handle.buffer, this._handle.byteOffset, this._handle.byteLength >> 3)\n\n this.streams = new Set()\n\n this.userData = null\n }\n\n get bound () {\n return this._port !== 0\n }\n\n get closing () {\n return this._closing !== null\n }\n\n get idle () {\n return this.streams.size === 0\n }\n\n get busy () {\n return this.streams.size > 0\n }\n\n get bytesTransmitted () {\n if (this._inited !== true) return 0\n return Number(this._view64[binding.offsetof_udx_socket_t_bytes_tx >> 3])\n }\n\n get packetsTransmitted () {\n if (this._inited !== true) return 0\n return Number(this._view64[binding.offsetof_udx_socket_t_packets_tx >> 3])\n }\n\n get bytesReceived () {\n if (this._inited !== true) return 0\n return Number(this._view64[binding.offsetof_udx_socket_t_bytes_rx >> 3])\n }\n\n get packetsReceived () {\n if (this._inited !== true) return 0\n return Number(this._view64[binding.offsetof_udx_socket_t_packets_rx >> 3])\n }\n\n get packetsDroppedByKernel () {\n if (this._inited !== true) return 0\n return Number(this._view64[binding.offsetof_udx_socket_t_packets_dropped_by_kernel >> 3])\n }\n\n toJSON () {\n return {\n bound: this.bound,\n closing: this.closing,\n streams: this.streams.size,\n address: this.address(),\n ipv6Only: this._ipv6Only,\n reuseAddress: this._reuseAddress,\n idle: this.idle,\n busy: this.busy\n }\n }\n\n _init () {\n if (this._inited) return\n\n binding.udx_napi_socket_init(this.udx._handle, this._handle, this,\n this._onsend,\n this._onmessage,\n this._onclose,\n this._reallocMessage\n )\n\n this._inited = true\n }\n\n _onsend (id, err) {\n const req = this._reqs[id]\n\n const onflush = req.onflush\n\n req.buffer = null\n req.onflush = null\n\n this._free.push(id)\n\n onflush(err >= 0)\n\n // gc the free list\n if (this._free.length >= 16 && this._free.length === this._reqs.length) {\n this._free = []\n this._reqs = []\n }\n }\n\n _onmessage (len, port, host, family) {\n this.emit('message', this.udx._consumeMessage(len), { host, family, port })\n return this.udx._buffer\n }\n\n _onclose () {\n this.emit('close')\n }\n\n _reallocMessage () {\n return this.udx._reallocMessage()\n }\n\n _onidle () {\n this.emit('idle')\n }\n\n _onbusy () {\n this.emit('busy')\n }\n\n _addStream (stream) {\n if (this.streams.has(stream)) return false\n this.streams.add(stream)\n if (this.streams.size === 1) this._onbusy()\n return true\n }\n\n _removeStream (stream) {\n if (!this.streams.has(stream)) return false\n this.streams.delete(stream)\n const closed = this._closeMaybe()\n if (this.idle && !closed) this._onidle()\n return true\n }\n\n address () {\n if (!this.bound) return null\n return { host: this._host, family: this._family, port: this._port }\n }\n\n bind (port, host) {\n if (this.bound) throw new Error('Already bound')\n if (this.closing) throw new Error('Socket is closed')\n\n if (!port) port = 0\n\n let flags = 0\n if (this._ipv6Only) flags |= binding.UV_UDP_IPV6ONLY\n if (this._reuseAddress) flags |= binding.UV_UDP_REUSEADDR\n\n let family\n\n if (host) {\n family = ip.isIP(host)\n if (!family) throw new Error(`${host} is not a valid IP address`)\n\n if (!this._inited) this._init()\n\n this._port = binding.udx_napi_socket_bind(this._handle, port, host, family, flags)\n } else {\n if (!this._inited) this._init()\n\n try {\n host = '::'\n family = 6\n this._port = binding.udx_napi_socket_bind(this._handle, port, host, family, flags)\n } catch {\n host = '0.0.0.0'\n family = 4\n this._port = binding.udx_napi_socket_bind(this._handle, port, host, family, flags)\n }\n }\n\n this._host = host\n this._family = family\n\n this.emit('listening')\n }\n\n async close () {\n if (this._closing) return this._closing\n this._closing = new Promise(resolve => this.once('close', resolve))\n this._closeMaybe()\n return this._closing\n }\n\n _closeMaybe () {\n if (this._closed || this._closing === null) return this._closed\n\n if (!this._inited) {\n this._closed = true\n this.emit('close')\n return true\n }\n\n if (this.idle) {\n binding.udx_napi_socket_close(this._handle)\n this._closed = true\n }\n\n return this._closed\n }\n\n setTTL (ttl) {\n if (!this._inited) throw new Error('Socket not active')\n binding.udx_napi_socket_set_ttl(this._handle, ttl)\n }\n\n getRecvBufferSize () {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_get_recv_buffer_size(this._handle)\n }\n\n setRecvBufferSize (size) {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_set_recv_buffer_size(this._handle, size)\n }\n\n getSendBufferSize () {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_get_send_buffer_size(this._handle)\n }\n\n setSendBufferSize (size) {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_set_send_buffer_size(this._handle, size)\n }\n\n addMembership (group, ifaceAddress) {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_set_membership(this._handle, group, ifaceAddress || '', true)\n }\n\n dropMembership (group, ifaceAddress) {\n if (!this._inited) throw new Error('Socket not active')\n return binding.udx_napi_socket_set_membership(this._handle, group, ifaceAddress || '', false)\n }\n\n async send (buffer, port, host, ttl) {\n if (this.closing) return false\n\n if (!host) host = '127.0.0.1'\n\n const family = ip.isIP(host)\n if (!family) throw new Error(`${host} is not a valid IP address`)\n\n if (!this.bound) this.bind(0)\n\n const id = this._allocSend()\n const req = this._reqs[id]\n\n req.buffer = buffer\n\n const promise = new Promise((resolve) => {\n req.onflush = resolve\n })\n\n binding.udx_napi_socket_send_ttl(this._handle, req.handle, id, buffer, port, host, family, ttl || 0)\n\n return promise\n }\n\n trySend (buffer, port, host, ttl) {\n if (this.closing) return\n\n if (!host) host = '127.0.0.1'\n\n const family = ip.isIP(host)\n if (!family) throw new Error(`${host} is not a valid IP address`)\n\n if (!this.bound) this.bind(0)\n\n const id = this._allocSend()\n const req = this._reqs[id]\n\n req.buffer = buffer\n req.onflush = noop\n\n binding.udx_napi_socket_send_ttl(this._handle, req.handle, id, buffer, port, host, family, ttl || 0)\n }\n\n _allocSend () {\n if (this._free.length > 0) return this._free.pop()\n const handle = b4a.allocUnsafe(binding.sizeof_udx_socket_send_t)\n return this._reqs.push({ handle, buffer: null, onflush: null }) - 1\n }\n}\n\nfunction noop () {}\nconst streamx = require('streamx')\nconst b4a = require('b4a')\nconst binding = require('../binding')\nconst ip = require('./ip')\n\nconst MAX_PACKET = 2048\nconst BUFFER_SIZE = 65536 + MAX_PACKET\n\nmodule.exports = class UDXStream extends streamx.Duplex {\n constructor (udx, id, opts = {}) {\n super({ mapWritable: toBuffer, eagerOpen: true })\n\n this.udx = udx\n this.socket = null\n\n this._handle = b4a.allocUnsafe(binding.sizeof_udx_napi_stream_t)\n this._view = new Uint32Array(this._handle.buffer, this._handle.byteOffset, this._handle.byteLength >> 2)\n this._view16 = new Uint16Array(this._handle.buffer, this._handle.byteOffset, this._handle.byteLength >> 1)\n this._view64 = new BigUint64Array(this._handle.buffer, this._handle.byteOffset, this._handle.byteLength >> 3)\n\n this._wreqs = []\n this._wfree = []\n\n this._sreqs = []\n this._sfree = []\n this._closed = false\n\n this._flushing = 0\n this._flushes = []\n\n this._buffer = null\n this._reallocData()\n\n this._onwrite = null\n this._ondestroy = null\n this._firewall = opts.firewall || firewallAll\n\n this._remoteChanging = null\n this._previousSocket = null\n\n this.id = id\n this.remoteId = 0\n this.remoteHost = null\n this.remoteFamily = 0\n this.remotePort = 0\n\n this.userData = null\n\n binding.udx_napi_stream_init(this.udx._handle, this._handle, id, opts.framed ? 1 : 0, this,\n this._ondata,\n this._onend,\n this._ondrain,\n this._onack,\n this._onsend,\n this._onmessage,\n this._onclose,\n this._onfirewall,\n this._onremotechanged,\n this._reallocData,\n this._reallocMessage\n )\n\n if (opts.seq) binding.udx_napi_stream_set_seq(this._handle, opts.seq)\n\n binding.udx_napi_stream_recv_start(this._handle, this._buffer)\n }\n\n get connected () {\n return this.socket !== null\n }\n\n get mtu () {\n return this._view16[binding.offsetof_udx_stream_t_mtu >> 1]\n }\n\n get rtt () {\n return this._view[binding.offsetof_udx_stream_t_srtt >> 2]\n }\n\n get cwnd () {\n return this._view[binding.offsetof_udx_stream_t_cwnd >> 2]\n }\n\n get rtoCount () {\n return this._view16[binding.offsetof_udx_stream_t_rto_count >> 1]\n }\n\n get retransmits () {\n return this._view16[binding.offsetof_udx_stream_t_retransmit_count >> 1]\n }\n\n get fastRecoveries () {\n return this._view16[binding.offsetof_udx_stream_t_fast_recovery_count >> 1]\n }\n\n get inflight () {\n return this._view[binding.offsetof_udx_stream_t_inflight >> 2]\n }\n\n get bytesTransmitted () {\n return Number(this._view64[binding.offsetof_udx_stream_t_bytes_tx >> 3])\n }\n\n get packetsTransmitted () {\n return Number(this._view64[binding.offsetof_udx_stream_t_packets_tx >> 3])\n }\n\n get bytesReceived () {\n return Number(this._view64[binding.offsetof_udx_stream_t_bytes_rx >> 3])\n }\n\n get packetsReceived () {\n return Number(this._view64[binding.offsetof_udx_stream_t_packets_rx >> 3])\n }\n\n get localHost () {\n return this.socket ? this.socket.address().host : null\n }\n\n get localFamily () {\n return this.socket ? this.socket.address().family : 0\n }\n\n get localPort () {\n return this.socket ? this.socket.address().port : 0\n }\n\n setInteractive (bool) {\n if (!this._closed) return\n binding.udx_napi_stream_set_mode(this._handle, bool ? 0 : 1)\n }\n\n connect (socket, remoteId, port, host, opts = {}) {\n if (this._closed) return\n\n if (this.connected) throw new Error('Already connected')\n if (socket.closing) throw new Error('Socket is closed')\n\n if (typeof host === 'object') {\n opts = host\n host = null\n }\n\n if (!host) host = '127.0.0.1'\n\n const family = ip.isIP(host)\n if (!family) throw new Error(`${host} is not a valid IP address`)\n if (!(port > 0 && port < 65536)) throw new Error(`${port} is not a valid port`)\n\n if (!socket.bound) socket.bind(0)\n\n this.remoteId = remoteId\n this.remotePort = port\n this.remoteHost = host\n this.remoteFamily = family\n this.socket = socket\n\n if (opts.ack) binding.udx_napi_stream_set_ack(this._handle, opts.ack)\n\n binding.udx_napi_stream_connect(this._handle, socket._handle, remoteId, port, host, family)\n\n this.socket._addStream(this)\n\n this.emit('connect')\n }\n\n changeRemote (socket, remoteId, port, host) {\n if (this._remoteChanging) throw new Error('Remote already changing')\n\n if (!this.connected) throw new Error('Not yet connected')\n if (socket.closing) throw new Error('Socket is closed')\n\n if (this.socket.udx !== socket.udx) {\n throw new Error('Cannot change to a socket on another UDX instance')\n }\n\n if (!host) host = '127.0.0.1'\n\n const family = ip.isIP(host)\n if (!family) throw new Error(`${host} is not a valid IP address`)\n if (!(port > 0 && port < 65536)) throw new Error(`${port} is not a valid port`)\n\n if (this.socket !== socket) this._previousSocket = this.socket\n\n this.remoteId = remoteId\n this.remotePort = port\n this.remoteHost = host\n this.remoteFamily = family\n this.socket = socket\n\n this._remoteChanging = new Promise((resolve, reject) => {\n const onchanged = () => {\n this.off('close', onclose)\n resolve()\n }\n\n const onclose = () => {\n this.off('remote-changed', onchanged)\n reject(new Error('Stream is closed'))\n }\n\n this\n .once('remote-changed', onchanged)\n .once('close', onclose)\n })\n\n binding.udx_napi_stream_change_remote(this._handle, socket._handle, remoteId, port, host, family)\n\n this.socket._addStream(this)\n\n return this._remoteChanging\n }\n\n relayTo (destination) {\n if (this._closed) return\n\n binding.udx_napi_stream_relay_to(this._handle, destination._handle)\n }\n\n async send (buffer) {\n if (!this.connected || this._closed) return false\n\n const id = this._allocSend()\n const req = this._sreqs[id]\n\n req.buffer = buffer\n\n const promise = new Promise((resolve) => {\n req.onflush = resolve\n })\n\n binding.udx_napi_stream_send(this._handle, req.handle, id, buffer)\n\n return promise\n }\n\n trySend (buffer) {\n if (!this.connected || this._closed) return\n\n const id = this._allocSend()\n const req = this._sreqs[id]\n\n req.buffer = buffer\n req.onflush = noop\n\n binding.udx_napi_stream_send(this._handle, req.handle, id, buffer)\n }\n\n async flush () {\n if ((await streamx.Writable.drained(this)) === false) return false\n if (this.destroying) return false\n\n const missing = this._wreqs.length - this._wfree.length\n if (missing === 0) return true\n\n return new Promise((resolve) => {\n this._flushes.push({ flush: this._flushing++, missing, resolve })\n })\n }\n\n toJSON () {\n return {\n id: this.id,\n connected: this.connected,\n destroying: this.destroying,\n destroyed: this.destroyed,\n remoteId: this.remoteId,\n remoteHost: this.remoteHost,\n remoteFamily: this.remoteFamily,\n remotePort: this.remotePort,\n mtu: this.mtu,\n rtt: this.rtt,\n cwnd: this.cwnd,\n inflight: this.inflight,\n socket: this.socket ? this.socket.toJSON() : null\n }\n }\n\n _read (cb) {\n cb(null)\n }\n\n _writeContinue (err) {\n if (this._onwrite === null) return\n const cb = this._onwrite\n this._onwrite = null\n cb(err)\n }\n\n _destroyContinue (err) {\n if (this._ondestroy === null) return\n const cb = this._ondestroy\n this._ondestroy = null\n cb(err)\n }\n\n _writev (buffers, cb) {\n if (!this.connected) throw customError('Writing while not connected not currently supported', 'ERR_ASSERTION')\n\n let drained = true\n\n if (buffers.length === 1) {\n const id = this._allocWrite(1)\n const req = this._wreqs[id]\n\n req.flush = this._flushing\n req.buffer = buffers[0]\n\n drained = binding.udx_napi_stream_write(this._handle, req.handle, id, req.buffer) !== 0\n } else {\n const id = this._allocWrite(nextBatchSize(buffers.length))\n const req = this._wreqs[id]\n\n req.flush = this._flushing\n req.buffers = buffers\n\n drained = binding.udx_napi_stream_writev(this._handle, req.handle, id, req.buffers) !== 0\n }\n\n if (drained) cb(null)\n else this._onwrite = cb\n }\n\n _final (cb) {\n const id = this._allocWrite(1)\n const req = this._wreqs[id]\n\n req.flush = this._flushes\n req.buffer = b4a.allocUnsafe(0)\n\n const drained = binding.udx_napi_stream_write_end(this._handle, req.handle, id, req.buffer) !== 0\n\n if (drained) cb(null)\n else this._onwrite = cb\n }\n\n _predestroy () {\n if (!this._closed) binding.udx_napi_stream_destroy(this._handle)\n this._closed = true\n this._writeContinue(null)\n }\n\n _destroy (cb) {\n if (this.connected) this._ondestroy = cb\n else cb(null)\n }\n\n _ondata (read) {\n this.push(this._consumeData(read))\n return this._buffer\n }\n\n _onend (read) {\n if (read > 0) this.push(this._consumeData(read))\n this.push(null)\n }\n\n _ondrain () {\n this._writeContinue(null)\n }\n\n _flushAck (flush) {\n for (let i = this._flushes.length - 1; i >= 0; i--) {\n const f = this._flushes[i]\n if (f.flush < flush) break\n f.missing--\n }\n\n while (this._flushes.length > 0 && this._flushes[0].missing === 0) {\n this._flushes.shift().resolve(true)\n }\n }\n\n _onack (id) {\n const req = this._wreqs[id]\n\n req.buffers = req.buffer = null\n this._wfree.push(id)\n\n if (this._flushes.length > 0) this._flushAck(req.flush)\n\n // gc the free list\n if (this._wfree.length >= 64 && this._wfree.length === this._wreqs.length) {\n this._wfree = []\n this._wreqs = []\n }\n }\n\n _onsend (id, err) {\n const req = this._sreqs[id]\n\n const onflush = req.onflush\n\n req.buffer = null\n req.onflush = null\n\n this._sfree.push(id)\n\n onflush(err >= 0)\n\n // gc the free list\n if (this._sfree.length >= 16 && this._sfree.length === this._sreqs.length) {\n this._sfree = []\n this._sreqs = []\n }\n }\n\n _onmessage (len) {\n this.emit('message', this.udx._consumeMessage(len))\n return this.udx._buffer\n }\n\n _onclose (err) {\n this._closed = true\n\n if (this.socket) {\n this.socket._removeStream(this)\n this.socket = null\n }\n\n if (this._previousSocket) {\n this._previousSocket._removeStream(this)\n this._previousSocket = null\n }\n\n // no error, we don't need to do anything\n if (!err) return this._destroyContinue(null)\n\n if (this._ondestroy === null) this.destroy(err)\n else this._destroyContinue(err)\n }\n\n _onfirewall (socket, port, host, family) {\n return this._firewall(socket, port, host, family) ? 1 : 0\n }\n\n _onremotechanged () {\n if (this._previousSocket) {\n this._previousSocket._removeStream(this)\n this._previousSocket = null\n }\n\n this._remoteChanging = null\n this.emit('remote-changed')\n }\n\n _consumeData (len) {\n const next = this._buffer.subarray(0, len)\n this._buffer = this._buffer.subarray(len)\n if (this._buffer.byteLength < MAX_PACKET) this._reallocData()\n return next\n }\n\n _reallocData () {\n this._buffer = b4a.allocUnsafe(BUFFER_SIZE)\n return this._buffer\n }\n\n _reallocMessage () {\n return this.udx._reallocMessage()\n }\n\n _allocWrite (size) {\n if (this._wfree.length === 0) {\n const handle = b4a.allocUnsafe(binding.udx_napi_stream_write_sizeof(size))\n return this._wreqs.push({ handle, size, buffers: null, buffer: null, flush: 0 }) - 1\n }\n\n const free = this._wfree.pop()\n if (size === 1) return free\n\n const next = this._wreqs[free]\n if (next.size < size) {\n next.handle = b4a.allocUnsafe(binding.udx_napi_stream_write_sizeof(size))\n next.size = size\n }\n\n return free\n }\n\n _allocSend () {\n if (this._sfree.length > 0) return this._sfree.pop()\n const handle = b4a.allocUnsafe(binding.sizeof_udx_stream_send_t)\n return this._sreqs.push({ handle, buffer: null, resolve: null, reject: null }) - 1\n }\n}\n\nfunction noop () {}\n\nfunction toBuffer (data) {\n return typeof data === 'string' ? b4a.from(data) : data\n}\n\nfunction firewallAll (socket, port, host) {\n return true\n}\n\nfunction customError (message, code) {\n const error = new Error(message)\n error.code = code\n return error\n}\n\nfunction nextBatchSize (n) { // try to coerce the the writevs into sameish size\n if (n === 1) return 1\n // group all < 8 to the same size, low mem overhead but save some small allocs\n if (n < 8) return 8\n if (n < 16) return 16\n if (n < 32) return 32\n if (n < 64) return 64\n return n\n}\nconst b4a = require('b4a')\nconst binding = require('../binding')\nconst ip = require('./ip')\nconst Socket = require('./socket')\nconst Stream = require('./stream')\nconst NetworkInterfaces = require('./network-interfaces')\n\nconst MAX_MESSAGE = 4096\nconst BUFFER_SIZE = 65536 + MAX_MESSAGE\n\nmodule.exports = class UDX {\n constructor () {\n this._handle = b4a.allocUnsafe(binding.sizeof_udx_napi_t)\n this._watchers = new Set()\n this._view64 = new BigUint64Array(this._handle.buffer, this._handle.byteOffset, this._handle.byteLength >> 3)\n\n this._buffer = null\n this._reallocMessage()\n\n binding.udx_napi_init(this._handle, this._buffer)\n }\n\n static isIPv4 (host) {\n return ip.isIPv4(host)\n }\n\n static isIPv6 (host) {\n return ip.isIPv6(host)\n }\n\n static isIP (host) {\n return ip.isIP(host)\n }\n\n get bytesTransmitted () {\n return Number(this._view64[binding.offsetof_udx_t_bytes_tx >> 3])\n }\n\n get packetsTransmitted () {\n return Number(this._view64[binding.offsetof_udx_t_packets_tx >> 3])\n }\n\n get bytesReceived () {\n return Number(this._view64[binding.offsetof_udx_t_bytes_rx >> 3])\n }\n\n get packetsReceived () {\n return Number(this._view64[binding.offsetof_udx_t_packets_rx >> 3])\n }\n\n get packetsDroppedByKernel () {\n return Number(this._view64[binding.offsetof_udx_t_packets_dropped_by_kernel >> 3])\n }\n\n _consumeMessage (len) {\n const next = this._buffer.subarray(0, len)\n this._buffer = this._buffer.subarray(len)\n if (this._buffer.byteLength < MAX_MESSAGE) this._reallocMessage()\n return next\n }\n\n _reallocMessage () {\n // TODO: move reallocation to native\n this._buffer = b4a.allocUnsafe(BUFFER_SIZE)\n return this._buffer\n }\n\n createSocket (opts) {\n return new Socket(this, opts)\n }\n\n createStream (id, opts) {\n return new Stream(this, id, opts)\n }\n\n networkInterfaces () {\n let [watcher = null] = this._watchers\n if (watcher) return watcher.interfaces\n\n watcher = new NetworkInterfaces(this)\n watcher.destroy()\n\n return watcher.interfaces\n }\n\n watchNetworkInterfaces (onchange) {\n const watcher = new NetworkInterfaces(this)\n\n this._watchers.add(watcher)\n watcher.on('close', () => {\n this._watchers.delete(watcher)\n })\n\n if (onchange) watcher.on('change', onchange)\n\n return watcher.watch()\n }\n\n async lookup (host, opts = {}) {\n const {\n family = 0\n } = opts\n\n const req = b4a.allocUnsafe(binding.sizeof_udx_napi_lookup_t)\n const ctx = {\n req,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n ctx.resolve = resolve\n ctx.reject = reject\n })\n\n binding.udx_napi_lookup(this._handle, req, host, family, ctx, onlookup)\n\n return promise\n }\n}\n\nfunction onlookup (err, host, family) {\n if (err) this.reject(err)\n else this.resolve({ host, family })\n}\n{\n \"name\": \"udx-native\",\n \"version\": \"1.18.2\",\n \"description\": \"udx is reliable, multiplexed, and congestion-controlled streams over udp\",\n \"main\": \"lib/udx.js\",\n \"files\": [\n \"lib\",\n \"prebuilds\",\n \"binding.cc\",\n \"binding.js\",\n \"CMakeLists.txt\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"addon\": true,\n \"scripts\": {\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:node\": \"node test/all.js\",\n \"test:bare\": \"bare test/all.js\",\n \"test:all\": \"brittle test/*.js test/slow/*.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\",\n \"bench\": \"brittle test/bench/*.js\",\n \"lint\": \"standard\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/udx-native.git\"\n },\n \"keywords\": [\n \"tcp\",\n \"udp\",\n \"stream\",\n \"reliable\"\n ],\n \"author\": \"Holepunch Inc.\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/udx-native/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/udx-native#readme\",\n \"engines\": {\n \"bare\": \">=1.17.4\"\n },\n \"dependencies\": {\n \"b4a\": \"^1.5.0\",\n \"bare-events\": \"^2.2.0\",\n \"require-addon\": \"^1.1.0\",\n \"streamx\": \"^2.22.0\"\n },\n \"devDependencies\": {\n \"bare-compat-napi\": \"^1.3.0\",\n \"brittle\": \"^3.1.0\",\n \"cmake-bare\": \"^1.1.10\",\n \"cmake-fetch\": \"^1.0.1\",\n \"cmake-napi\": \"^1.0.5\",\n \"is-ci\": \"^3.0.1\",\n \"standard\": \"^17.1.0\",\n \"tiny-byte-size\": \"^1.1.0\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nexports.add = add\nexports.has = has\nexports.remove = remove\nexports.swap = swap\n\nfunction add (list, item) {\n if (has(list, item)) return item\n item._index = list.length\n list.push(item)\n return item\n}\n\nfunction has (list, item) {\n return item._index < list.length && list[item._index] === item\n}\n\nfunction remove (list, item) {\n if (!has(list, item)) return null\n\n var last = list.pop()\n if (last !== item) {\n list[item._index] = last\n last._index = item._index\n }\n\n return item\n}\n\nfunction swap (list, a, b) {\n if (!has(list, a) || !has(list, b)) return\n var tmp = a._index\n a._index = b._index\n list[a._index] = a\n b._index = tmp\n list[b._index] = b\n}\n{\n \"name\": \"unordered-set\",\n \"version\": \"2.0.1\",\n \"description\": \"A couple of functions that make it easy to maintain an unordered set as an array in an efficient way\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^6.0.4\",\n \"tape\": \"^4.4.0\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/unordered-set.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/unordered-set/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/unordered-set\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst b4a = require('b4a')\n\nunslab.all = all\nunslab.is = is\n\nmodule.exports = unslab\n\nfunction unslab (buf) {\n if (buf === null || buf.buffer.byteLength === buf.byteLength) return buf\n const copy = b4a.allocUnsafeSlow(buf.byteLength)\n copy.set(buf, 0)\n return copy\n}\n\nfunction is (buf) {\n return buf.buffer.byteLength !== buf.byteLength\n}\n\nfunction all (list) {\n let size = 0\n for (let i = 0; i < list.length; i++) {\n const buf = list[i]\n size += buf === null || buf.buffer.byteLength === buf.byteLength ? 0 : buf.byteLength\n }\n\n const copy = b4a.allocUnsafeSlow(size)\n const result = new Array(list.length)\n\n let offset = 0\n for (let i = 0; i < list.length; i++) {\n let buf = list[i]\n\n if (buf !== null && buf.buffer.byteLength !== buf.byteLength) {\n copy.set(buf, offset)\n buf = copy.subarray(offset, offset += buf.byteLength)\n }\n\n result[i] = buf\n }\n\n return result\n}\n{\n \"name\": \"unslab\",\n \"version\": \"1.3.0\",\n \"description\": \"Unslab some slab'ed buffers\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.6.6\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.5.2\",\n \"standard\": \"^17.1.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/unslab.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/unslab/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/unslab\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst { runtime, platform, arch } = typeof Bare !== 'undefined'\n ? { runtime: 'bare', platform: global.Bare.platform, arch: global.Bare.arch }\n : typeof process !== 'undefined'\n ? { runtime: 'node', platform: global.process.platform, arch: global.process.arch }\n : typeof Window !== 'undefined'\n ? { runtime: 'browser', platform: 'unknown', arch: 'unknown' }\n : { runtime: 'unknown', platform: 'unknown', arch: 'unknown' }\n\nexports.runtime = runtime\nexports.platform = platform\nexports.arch = arch\nexports.isBare = runtime === 'bare'\nexports.isBareKit = exports.isBare && typeof BareKit !== 'undefined'\nexports.isPear = exports.isBare && typeof Pear !== 'undefined'\nexports.isNode = runtime === 'node'\nexports.isBrowser = runtime === 'browser'\nexports.isWindows = platform === 'win32'\nexports.isLinux = platform === 'linux'\nexports.isMac = platform === 'darwin'\nexports.isIOS = platform === 'ios' || platform === 'ios-simulator'\nexports.isAndroid = platform === 'android'\nexports.isElectron = typeof process !== 'undefined' && !!global.process.versions.electron\nexports.isElectronRenderer = exports.isElectron && global.process.type === 'renderer'\nexports.isElectronWorker = exports.isElectron && global.process.type === 'worker'\n{\n \"name\": \"which-runtime\",\n \"version\": \"1.3.0\",\n \"description\": \"Detect if you are in Bare or Node and which os etc\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^17.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/which-runtime.git\"\n },\n \"author\": \"Holepunch Inc.\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/which-runtime/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/which-runtime\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nmodule.exports = class MaxCache {\n constructor ({ maxSize, maxAge, createMap, ongc }) {\n this.maxSize = maxSize\n this.maxAge = maxAge\n this.ongc = ongc || null\n\n this._createMap = createMap || defaultCreateMap\n this._latest = this._createMap()\n this._oldest = this._createMap()\n this._retained = this._createMap()\n this._gced = false\n this._interval = null\n\n if (this.maxAge > 0 && this.maxAge < Infinity) {\n const tick = Math.ceil(2 / 3 * this.maxAge)\n this._interval = setInterval(this._gcAuto.bind(this), tick)\n if (this._interval.unref) this._interval.unref()\n }\n }\n\n * [Symbol.iterator] () {\n for (const it of [this._latest, this._oldest, this._retained]) {\n yield * it\n }\n }\n\n * keys () {\n for (const it of [this._latest, this._oldest, this._retained]) {\n yield * it.keys()\n }\n }\n\n * values () {\n for (const it of [this._latest, this._oldest, this._retained]) {\n yield * it.values()\n }\n }\n\n destroy () {\n this.clear()\n clearInterval(this._interval)\n this._interval = null\n }\n\n clear () {\n this._gced = true\n this._latest.clear()\n this._oldest.clear()\n this._retained.clear()\n }\n\n set (k, v) {\n if (this._retained.has(k)) return this\n this._latest.set(k, v)\n this._oldest.delete(k) || this._retained.delete(k)\n if (this._latest.size >= this.maxSize) this._gc()\n return this\n }\n\n retain (k, v) {\n this._retained.set(k, v)\n this._latest.delete(k) || this._oldest.delete(k)\n return this\n }\n\n delete (k) {\n return this._latest.delete(k) || this._oldest.delete(k) || this._retained.delete(k)\n }\n\n has (k) {\n return this._latest.has(k) || this._oldest.has(k) || this._retained.has(k)\n }\n\n get (k) {\n if (this._latest.has(k)) {\n return this._latest.get(k)\n }\n\n if (this._oldest.has(k)) {\n const v = this._oldest.get(k)\n this._latest.set(k, v)\n this._oldest.delete(k)\n return v\n }\n\n if (this._retained.has(k)) {\n return this._retained.get(k)\n }\n\n return null\n }\n\n _gcAuto () {\n if (!this._gced) this._gc()\n this._gced = false\n }\n\n _gc () {\n this._gced = true\n if (this.ongc !== null && this._oldest.size > 0) this.ongc(this._oldest)\n this._oldest = this._latest\n this._latest = this._createMap()\n }\n}\n\nfunction defaultCreateMap () {\n return new Map()\n}\n{\n \"name\": \"xache\",\n \"version\": \"1.2.1\",\n \"description\": \"Yet another auto expiring, max sizable cache\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"standard\": \"^17.1.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/xache.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/xache/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/xache\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\nconst b4a = require('b4a')\n\nconst ALPHABET = 'ybndrfg8ejkmcpqxot1uwisza345h769'\nconst MIN = 0x31 // 1\nconst MAX = 0x7a // z\nconst REVERSE = new Int8Array(1 + MAX - MIN)\n\nREVERSE.fill(-1)\n\nfor (let i = 0; i < ALPHABET.length; i++) {\n const v = ALPHABET.charCodeAt(i) - MIN\n REVERSE[v] = i\n}\n\nexports.encode = encode\nexports.decode = decode\nexports.ALPHABET = ALPHABET\n\nfunction decode (s, out) {\n let pb = 0\n let ps = 0\n\n const r = s.length & 7\n const q = (s.length - r) / 8\n\n if (!out) out = b4a.allocUnsafe(Math.ceil(s.length * 5 / 8))\n\n // 0 5 2 7 4 1 6 3 (+5 mod 8)\n for (let i = 0; i < q; i++) {\n const a = quintet(s, ps++)\n const b = quintet(s, ps++)\n const c = quintet(s, ps++)\n const d = quintet(s, ps++)\n const e = quintet(s, ps++)\n const f = quintet(s, ps++)\n const g = quintet(s, ps++)\n const h = quintet(s, ps++)\n\n out[pb++] = (a << 3) | (b >>> 2)\n out[pb++] = ((b & 0b11) << 6) | (c << 1) | (d >>> 4)\n out[pb++] = ((d & 0b1111) << 4) | (e >>> 1)\n out[pb++] = ((e & 0b1) << 7) | (f << 2) | (g >>> 3)\n out[pb++] = ((g & 0b111) << 5) | h\n }\n\n if (r === 0) return out.subarray(0, pb)\n\n const a = quintet(s, ps++)\n const b = quintet(s, ps++)\n\n out[pb++] = (a << 3) | (b >>> 2)\n\n if (r <= 2) return out.subarray(0, pb)\n\n const c = quintet(s, ps++)\n const d = quintet(s, ps++)\n\n out[pb++] = ((b & 0b11) << 6) | (c << 1) | (d >>> 4)\n\n if (r <= 4) return out.subarray(0, pb)\n\n const e = quintet(s, ps++)\n\n out[pb++] = ((d & 0b1111) << 4) | (e >>> 1)\n\n if (r <= 5) return out.subarray(0, pb)\n\n const f = quintet(s, ps++)\n const g = quintet(s, ps++)\n\n out[pb++] = ((e & 0b1) << 7) | (f << 2) | (g >>> 3)\n\n if (r <= 7) return out.subarray(0, pb)\n\n const h = quintet(s, ps++)\n\n out[pb++] = ((g & 0b111) << 5) | h\n\n return out.subarray(0, pb)\n}\n\nfunction encode (buf) {\n if (typeof buf === 'string') buf = b4a.from(buf)\n\n const max = buf.byteLength * 8\n\n let s = ''\n\n for (let p = 0; p < max; p += 5) {\n const i = p >>> 3\n const j = p & 7\n\n if (j <= 3) {\n s += ALPHABET[(buf[i] >>> (3 - j)) & 0b11111]\n continue\n }\n\n const of = j - 3\n const h = (buf[i] << of) & 0b11111\n const l = (i >= buf.byteLength ? 0 : buf[i + 1]) >>> (8 - of)\n\n s += ALPHABET[h | l]\n }\n\n return s\n}\n\nfunction quintet (s, i) {\n if (i > s.length) {\n return 0\n }\n\n const v = s.charCodeAt(i)\n\n if (v < MIN || v > MAX) {\n throw Error('Invalid character in base32 input: \"' + s[i] + '\" at position ' + i)\n }\n\n const bits = REVERSE[v - MIN]\n\n if (bits === -1) {\n throw Error('Invalid character in base32 input: \"' + s[i] + '\" at position ' + i)\n }\n\n return bits\n}\n{\n \"name\": \"z32\",\n \"version\": \"1.1.0\",\n \"description\": \"Encode & decode z-base32\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.5.3\"\n },\n \"devDependencies\": {\n \"base-x\": \"^4.0.0\",\n \"base32\": \"0.0.7\",\n \"brittle\": \"^3.1.3\",\n \"nanobench\": \"^3.0.0\",\n \"rfc4648\": \"^1.5.2\",\n \"standard\": \"^17.0.0\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/z32.git\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\",\n \"bench\": \"node benchmark.js\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/z32/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/z32\",\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n }\n}\n{\n \"name\": \"hexa_keeper\",\n \"version\": \"2.5.2\",\n \"private\": true,\n \"scripts\": {\n \"ios\": \"npx react-native run-ios --scheme=hexa_keeper_dev --simulator 'iPhone 16' \",\n \"iosProd\": \"npx react-native run-ios --scheme=hexa_keeper --simulator 'iPhone 16' \",\n \"start\": \"react-native start\",\n \"test\": \"jest\",\n \"lint\": \"eslint . --ext .js,.jsx,.ts,.tsx\",\n \"lint:fix\": \"npm run lint -- --fix\",\n \"prepare\": \"husky install && chmod +x setup.sh && sh ./setup.sh\",\n \"postinstall\": \"patch-package && node patches/patch-sifir_android.mjs\",\n \"bare-pack\": \"npx bare-pack --target ios --target android --linked --out src/services/p2p/app.bundle.mjs src/services/p2p/worklet.mjs\",\n \"androidDevelopmentDebug\": \"ENVFILE=.env react-native run-android --mode=developmentDebug --appIdSuffix=development\",\n \"androidDevelopmentRelease\": \"ENVFILE=.env react-native run-android --mode=developmentRelease --appIdSuffix=development\",\n \"androidProductionDebug\": \"ENVFILE=.env react-native run-android --mode=productionDebug\",\n \"androidProductionRelease\": \"ENVFILE=.env react-native run-android --mode=productionRelease\",\n \"uninstall ios\": \"xcrun simctl uninstall booted io.hexawallet.keeper\",\n \"uninstall ios:dev\": \"xcrun simctl uninstall booted io.hexawallet.hexakeeper.dev\",\n \"uninstall android\": \"adb uninstall io.hexawallet.bitcoinkeeper\",\n \"uninstall android:dev\": \"adb uninstall io.hexawallet.keeper.development\",\n \"release android dev\": \"cd android && ENVFILE=.env bundle exec fastlane dev --env dev\",\n \"release android prod\": \"cd android && ENVFILE=.env.production bundle exec fastlane live --env production\",\n \"release ios dev\": \"cd ios && ENVFILE=.env bundle exec fastlane dev --env dev\",\n \"release ios prod\": \"cd ios && ENVFILE=.env.production bundle exec fastlane live --env production\"\n },\n \"dependencies\": {\n \"@bitcoinerlab/miniscript\": \"1.4.0\",\n \"@ngraveio/bc-ur\": \"1.1.6\",\n \"@noble/secp256k1\": \"1.6.3\",\n \"@react-native-clipboard/clipboard\": \"1.16.2\",\n \"@react-native-community/netinfo\": \"11.4.1\",\n \"@react-native-firebase/app\": \"14.11.1\",\n \"@react-native-firebase/messaging\": \"14.11.1\",\n \"@react-navigation/native\": \"6.0.8\",\n \"@react-navigation/native-stack\": \"6.5.0\",\n \"@realm/react\": \"0.6.2\",\n \"@reduxjs/toolkit\": \"1.8.2\",\n \"@sentry/react-native\": \"5.33.1\",\n \"@shipt/segmented-arc-for-react-native\": \"1.2.1\",\n \"@testing-library/react-native\": \"11.0.0\",\n \"assert\": \"2.0.0\",\n \"axios\": \"1.8.2\",\n \"b4a\": \"1.6.7\",\n \"bare-rpc\": \"0.2.6\",\n \"base-64\": \"1.0.0\",\n \"base58check\": \"2.0.0\",\n \"bip21\": \"2.0.3\",\n \"bip39\": \"3.0.4\",\n \"bitcoinjs-lib\": \"6.1.5\",\n \"bitcoinjs-message\": \"2.2.0\",\n \"buffer\": \"6.0.3\",\n \"buffer-reverse\": \"1.0.1\",\n \"cktap-protocol-react-native\": \"git+https://github.com/bithyve/cktap-protocol-react-native.git#7be02d23550e88a5a0b5790138734d34c688ef5d\",\n \"coinselect\": \"3.1.13\",\n \"compressing\": \"1.10.1\",\n \"constants\": \"0.0.2\",\n \"crypto\": \"1.0.1\",\n \"crypto-js\": \"4.2.0\",\n \"deprecated-react-native-prop-types\": \"4.2.1\",\n \"ecpair\": \"2.0.1\",\n \"electrum-client\": \"git+https://github.com/bithyve/rn-electrum-client.git#76c0ea35e1a50c47f3a7f818d529ebd100161496\",\n \"events\": \"1.0.0\",\n \"hyperswarm\": \"4.11.7\",\n \"idx\": \"2.5.6\",\n \"libportal-react-native\": \"git+https://github.com/bithyve/libportal-react-native.git#3f9373785265f3e18218eefb8958109feec8f7c3\",\n \"lodash\": \"4.17.21\",\n \"moment\": \"2.29.4\",\n \"native-base\": \"3.4.28\",\n \"node-rsa\": \"1.1.1\",\n \"otplib\": \"12.0.1\",\n \"pako\": \"2.1.0\",\n \"patch-package\": \"6.4.7\",\n \"path-browserify\": \"0.0.0\",\n \"process\": \"0.11.0\",\n \"react\": \"18.2.0\",\n \"react-localization\": \"1.0.19\",\n \"react-native\": \"0.74.7\",\n \"react-native-background-timer\": \"2.4.1\",\n \"react-native-bare-kit\": \"0.5.6\",\n \"react-native-biometrics\": \"2.2.0\",\n \"react-native-blob-util\": \"0.18.3\",\n \"react-native-camera\": \"4.2.1\",\n \"react-native-change-icon\": \"5.0.0\",\n \"react-native-config\": \"1.4.6\",\n \"react-native-contacts\": \"7.0.8\",\n \"react-native-crypto\": \"2.2.0\",\n \"react-native-device-info\": \"10.0.2\",\n \"react-native-document-picker\": \"8.2.2\",\n \"react-native-fast-image\": \"8.6.3\",\n \"react-native-fs\": \"2.20.0\",\n \"react-native-gesture-handler\": \"2.14.0\",\n \"react-native-get-random-values\": \"1.8.0\",\n \"react-native-gifted-charts\": \"1.4.15\",\n \"react-native-hce\": \"0.2.0\",\n \"react-native-html-to-pdf\": \"0.12.0\",\n \"react-native-iap\": \"12.15.2\",\n \"react-native-image-picker\": \"4.10.3\",\n \"react-native-keychain\": \"8.1.2\",\n \"react-native-linear-gradient\": \"2.8.3\",\n \"react-native-localize\": \"2.2.2\",\n \"react-native-mmkv\": \"2.11.0\",\n \"react-native-modal\": \"13.0.1\",\n \"react-native-nfc-manager\": \"3.16.0\",\n \"react-native-pdf\": \"6.7.1\",\n \"react-native-qr-decode-image-camera\": \"1.1.2\",\n \"react-native-qrcode-svg\": \"6.3.2\",\n \"react-native-randombytes\": \"3.6.1\",\n \"react-native-reanimated\": \"3.15.0\",\n \"react-native-responsive-screen\": \"1.4.2\",\n \"react-native-rsa-native\": \"2.0.5\",\n \"react-native-safe-area-context\": \"4.10.5\",\n \"react-native-screens\": \"3.34.0\",\n \"react-native-send-intent\": \"1.3.0\",\n \"react-native-shadow-2\": \"6.0.3\",\n \"react-native-share\": \"9.2.3\",\n \"react-native-svg\": \"13.13.0\",\n \"react-native-tcp-socket\": \"5.6.2\",\n \"react-redux\": \"7.2.8\",\n \"readable-stream\": \"1.0.33\",\n \"realm\": \"12.14.2\",\n \"redux\": \"4.1.2\",\n \"redux-persist\": \"6.0.0\",\n \"redux-saga\": \"1.1.3\",\n \"semver\": \"7.3.8\",\n \"socket.io-client\": \"4.5.4\",\n \"stream\": \"0.0.2\",\n \"stream-browserify\": \"1.0.0\",\n \"tronweb\": \"6.0.3\",\n \"url\": \"0.10.1\"\n },\n \"devDependencies\": {\n \"@babel/core\": \"7.26.0\",\n \"@babel/preset-env\": \"7.26.0\",\n \"@babel/runtime\": \"7.26.10\",\n \"@react-native/babel-preset\": \"0.74.89\",\n \"@react-native/eslint-config\": \"0.74.89\",\n \"@react-native/metro-config\": \"0.74.89\",\n \"@react-native/typescript-config\": \"0.74.89\",\n \"@testing-library/jest-native\": \"5.4.3\",\n \"@types/jest\": \"29.5.14\",\n \"@types/react\": \"18.3.12\",\n \"@types/react-native\": \"0.66.34\",\n \"@types/react-test-renderer\": \"18.3.0\",\n \"@typescript-eslint/eslint-plugin\": \"5.62.0\",\n \"@typescript-eslint/parser\": \"5.62.0\",\n \"babel-jest\": \"29.7.0\",\n \"babel-plugin-module-resolver\": \"4.1.0\",\n \"babel-plugin-transform-remove-console\": \"6.9.4\",\n \"eslint\": \"8.57.1\",\n \"eslint-config-airbnb\": \"19.0.4\",\n \"eslint-config-prettier\": \"8.10.0\",\n \"eslint-import-resolver-typescript\": \"3.6.3\",\n \"eslint-plugin-import\": \"2.31.0\",\n \"eslint-plugin-jsx-a11y\": \"6.10.2\",\n \"eslint-plugin-react\": \"7.37.2\",\n \"eslint-plugin-react-hooks\": \"4.6.2\",\n \"eslint-plugin-react-native\": \"4.1.0\",\n \"husky\": \"8.0.3\",\n \"jest\": \"29.7.0\",\n \"lint-staged\": \"13.3.0\",\n \"prettier\": \"2.8.8\",\n \"react-dom\": \"19.0.0\",\n \"react-native-codegen\": \"0.70.7\",\n \"react-native-svg-transformer\": \"1.5.0\",\n \"react-test-renderer\": \"18.3.0\",\n \"redux-mock-store\": \"1.5.5\",\n \"rn-nodeify\": \"github:tradle/rn-nodeify#338d8d6ba8438403093e9409e9a9d88ad884926f\",\n \"typescript\": \"5.0.4\"\n },\n \"resolutions\": {\n \"@types/react\": \"18.3.12\"\n },\n \"react-native\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"browser\": {\n \"crypto\": \"react-native-crypto\",\n \"path\": \"path-browserify\",\n \"_stream_transform\": \"readable-stream/transform\",\n \"_stream_readable\": \"readable-stream/readable\",\n \"_stream_writable\": \"readable-stream/writable\",\n \"_stream_duplex\": \"readable-stream/duplex\",\n \"_stream_passthrough\": \"readable-stream/passthrough\",\n \"stream\": \"stream-browserify\"\n },\n \"engines\": {\n \"node\": \">=18\"\n }\n}\nexport const RPC_KEY = 1;\nexport const RPC_KEY_RECEIVED = 2;\nexport const GET_KEYS = 4;\nexport const GET_PEERS = 5;\nexport const RPC_TEST = 3;\nexport const ON_CONNECTION = 6;\nexport const ON_MESSAGE = 7;\nexport const ON_ERROR = 8;\nexport const SEND_MESSAGE = 9;\nexport const JOIN_PEER = 10;\nimport Hyperswarm from 'hyperswarm';\nimport {\n GET_KEYS,\n GET_PEERS,\n JOIN_PEER,\n ON_CONNECTION,\n ON_ERROR,\n ON_MESSAGE,\n RPC_KEY,\n RPC_KEY_RECEIVED,\n SEND_MESSAGE,\n} from './rpc-commands.mjs';\nimport RPC from 'bare-rpc';\nimport b4a from 'b4a';\n\nimport { Buffer } from 'buffer';\nglobal.Buffer = Buffer;\n\nconst { IPC } = BareKit;\n\nlet keyPair;\nconst connections = new Map();\n\nconst RELAY_PUB_KEY = '2af7832f1b5ec80f34e5cd434443303eb02263f46be9b3898d9909e743849e46'; // replace with actual relay public key from .env while building the app.bundle.mjs (config.ts import fails to load in worklet)\n\nconst rpc = new RPC(IPC, (req, error) => {\n try {\n const data = b4a.toString(req.data);\n console.log('REQ', req.command, data);\n if (req.command === RPC_KEY_RECEIVED) {\n } else if (req.command === GET_KEYS) {\n req.reply(JSON.stringify(getKeys()));\n } else if (req.command === GET_PEERS) {\n req.reply(JSON.stringify(getPeers()));\n } else if (req.command === SEND_MESSAGE) {\n sendMessage(data);\n } else if (req.command === JOIN_PEER) {\n joinPeer(data);\n }\n\n if (error) {\n console.log(error);\n }\n } catch (error) {\n console.log(error);\n }\n});\n\nconst swarm = new Hyperswarm({\n seed: Buffer.from(Bare.argv[0], 'hex'),\n});\n\nkeyPair = swarm.keyPair;\n\nswarm.on('connection', (conn, info) => {\n console.log(`JOINING info.publicKey.toString('hex')`);\n\n connections.set(info.publicKey.toString('hex'), conn);\n\n console.log(`connected to ${info.publicKey.toString('hex')}`);\n const req = rpc.request(ON_CONNECTION);\n req.send(\n JSON.stringify({\n publicKey: info.publicKey.toString('hex'),\n })\n );\n\n conn.on('data', (data) => {\n console.log(`received: ${data.toString()}`);\n const req = rpc.request(ON_MESSAGE);\n req.send(\n JSON.stringify({\n data: data.toString(),\n publicKey: info.publicKey.toString('hex'),\n })\n );\n });\n\n conn.on('error', (err) => {\n console.error(`connection error:`, err);\n const req = rpc.request(ON_ERROR);\n req.send(\n JSON.stringify({\n err,\n })\n );\n });\n});\n\nconst getKeys = () => {\n return {\n publicKey: swarm.keyPair.publicKey.toString('hex'),\n secretKey: swarm.keyPair.secretKey.toString('hex'),\n };\n};\n\nconst getPeers = () => {\n return JSON.stringify(Array.from(swarm.peers.entries()));\n};\n\nconst joinPeer = async (pubKey) => {\n swarm.joinPeer(Buffer.from(pubKey, 'hex'));\n await swarm.flush();\n console.log(`joinpeer ${pubKey}`);\n};\n\nconst sendMessage = async (payload) => {\n console.log('sendMessage', payload);\n const relayConn = connections.get(RELAY_PUB_KEY);\n if (relayConn) {\n relayConn.write(payload);\n } else {\n console.error(`No connection found for publicKey: ${payload.pubKey}`);\n }\n};\n\nconst req = rpc.request(RPC_KEY);\nreq.send(\n JSON.stringify({\n publicKey: swarm.keyPair.publicKey.toString('hex'),\n secretKey: swarm.keyPair.secretKey.toString('hex'),\n })\n);\n\nconst d = swarm.join(swarm.keyPair.publicKey, { server: true, client: false });\nawait swarm.join(Buffer.from(RELAY_PUB_KEY, 'hex'), { client: true, server: true });\nawait swarm.flush();\n\n// const replyBuffer = await req.reply()\n// console.log(replyBuffer.toString())\n\n// IPC.write(JSON.stringify({\n// publicKey: swarm.keyPair.publicKey.toString('hex'),\n// secretKey: swarm.keyPair.secretKey.toString('hex'),\n// }));\n" diff --git a/src/services/p2p/enc-X25519.ts b/src/services/p2p/enc-X25519.ts new file mode 100644 index 000000000..87067c3e9 --- /dev/null +++ b/src/services/p2p/enc-X25519.ts @@ -0,0 +1,358 @@ +import { x25519, ed25519 } from '@noble/curves/ed25519'; +import { randomBytes } from 'crypto'; +import { createCipheriv, createDecipheriv, createHash } from 'crypto'; + +/** + * X25519 Encryption and Decryption utilities + * Uses Curve25519 for key exchange and AES-256-GCM for symmetric encryption + */ + +export enum KeyType { + X25519 = 'x25519', + Ed25519 = 'ed25519', +} + +export interface KeyPair { + privateKey: Uint8Array; + publicKey: Uint8Array; +} + +export interface EncryptedData { + ciphertext: Uint8Array; + nonce: Uint8Array; + tag: Uint8Array; + ephemeralPublicKey: Uint8Array; +} + +/** + * Generate a new X25519 key pair + * @returns Object containing private and public keys + */ +export function generateKeyPair(): KeyPair { + const privateKey = x25519.utils.randomPrivateKey(); + const publicKey = x25519.getPublicKey(privateKey); + + return { + privateKey, + publicKey, + }; +} + +/** + * Derive a shared secret using X25519 key exchange + * @param privateKey - Your private key + * @param publicKey - The other party's public key + * @returns Shared secret as Uint8Array + */ +export function deriveSharedSecret(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array { + return x25519.getSharedSecret(privateKey, publicKey); +} + +/** + * Derive an encryption key from shared secret using HKDF-like approach + * @param sharedSecret - The shared secret from key exchange + * @param salt - Optional salt (defaults to empty) + * @param info - Optional context info (defaults to 'encryption') + * @returns 32-byte encryption key + */ +export function deriveEncryptionKey( + sharedSecret: Uint8Array, + salt: Uint8Array = new Uint8Array(0), + info: string = 'encryption' +): Uint8Array { + // Simple HKDF-like key derivation using SHA-256 + const hash = createHash('sha256'); + hash.update(sharedSecret); + hash.update(salt); + hash.update(Buffer.from(info, 'utf8')); + return new Uint8Array(hash.digest()); +} + +/** + * Encrypt data using AES-256-GCM with the derived key + * @param data - Data to encrypt + * @param key - 32-byte encryption key + * @returns Object containing ciphertext, nonce, and authentication tag + */ +export function encryptWithKey( + data: Uint8Array, + key: Uint8Array +): { + ciphertext: Uint8Array; + nonce: Uint8Array; + tag: Uint8Array; +} { + if (key.length !== 32) { + throw new Error('Key must be 32 bytes for AES-256'); + } + + const nonce = randomBytes(12); // 96-bit nonce for GCM + const cipher = createCipheriv('aes-256-gcm', key, nonce); + + const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); + + const tag = cipher.getAuthTag(); + + return { + ciphertext: new Uint8Array(ciphertext), + nonce: new Uint8Array(nonce), + tag: new Uint8Array(tag), + }; +} + +/** + * Decrypt data using AES-256-GCM with the derived key + * @param ciphertext - Encrypted data + * @param key - 32-byte encryption key + * @param nonce - 12-byte nonce + * @param tag - Authentication tag + * @returns Decrypted data + */ +export function decryptWithKey( + ciphertext: Uint8Array, + key: Uint8Array, + nonce: Uint8Array, + tag: Uint8Array +): Uint8Array { + if (key.length !== 32) { + throw new Error('Key must be 32 bytes for AES-256'); + } + + if (nonce.length !== 12) { + throw new Error('Nonce must be 12 bytes for GCM'); + } + + const decipher = createDecipheriv('aes-256-gcm', key, nonce); + decipher.setAuthTag(tag); + + const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]); + + return new Uint8Array(decrypted); +} + +/** + * Internal encrypt function that works with Uint8Arrays + * @param data - Data to encrypt + * @param recipientPublicKey - Recipient's X25519 public key + * @returns Encrypted data package + */ +function encryptInternal(data: Uint8Array, recipientPublicKey: Uint8Array): EncryptedData { + // Generate ephemeral key pair + const ephemeralKeyPair = generateKeyPair(); + + // Derive shared secret + const sharedSecret = deriveSharedSecret(ephemeralKeyPair.privateKey, recipientPublicKey); + + // Derive encryption key + const encryptionKey = deriveEncryptionKey(sharedSecret); + + // Encrypt the data + const { ciphertext, nonce, tag } = encryptWithKey(data, encryptionKey); + + return { + ciphertext, + nonce, + tag, + ephemeralPublicKey: ephemeralKeyPair.publicKey, + }; +} + +/** + * Internal decrypt function that works with Uint8Arrays + * @param encryptedData - Encrypted data package + * @param privateKey - Your X25519 private key + * @returns Decrypted data + */ +function decryptInternal(encryptedData: EncryptedData, privateKey: Uint8Array): Uint8Array { + const { ciphertext, nonce, tag, ephemeralPublicKey } = encryptedData; + + // Derive shared secret using ephemeral public key and our private key + const sharedSecret = deriveSharedSecret(privateKey, ephemeralPublicKey); + + // Derive encryption key + const encryptionKey = deriveEncryptionKey(sharedSecret); + + // Decrypt the data + return decryptWithKey(ciphertext, encryptionKey, nonce, tag); +} + +/** + * Utility function to convert hex string to Uint8Array + * @param hex - Hex string + * @returns Uint8Array + */ +export function hexToUint8Array(hex: string): Uint8Array { + if (hex.length % 2 !== 0) { + throw new Error('Hex string must have even length'); + } + + const array = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + array[i / 2] = parseInt(hex.substr(i, 2), 16); + } + return array; +} + +/** + * Utility function to convert Uint8Array to hex string + * @param array - Uint8Array + * @returns Hex string + */ +export function uint8ArrayToHex(array: Uint8Array): string { + return Array.from(array) + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(''); +} + +/** + * Convert Ed25519 private key to X25519 private key and derive corresponding public key + * This is needed when working with hyperswarm keys which are Ed25519 format + * Uses native @noble/curves conversion functions for proper Ed25519 to X25519 conversion + * @param ed25519PrivateKey - Ed25519 private key as hex string + * @returns Object with X25519 private and public keys as hex strings + */ +export function ed25519ToX25519KeyPair(ed25519PrivateKey: string): { + privateKey: string; + publicKey: string; +} { + const ed25519PrivateKeyBytes = hexToUint8Array(ed25519PrivateKey); + + // Convert Ed25519 private key to X25519 private key using native noble/curves function + const x25519PrivateKeyBytes = ed25519.utils.toMontgomeryPriv(ed25519PrivateKeyBytes); + + // Generate the corresponding X25519 public key + const x25519PublicKeyBytes = x25519.getPublicKey(x25519PrivateKeyBytes); + + return { + privateKey: uint8ArrayToHex(x25519PrivateKeyBytes), + publicKey: uint8ArrayToHex(x25519PublicKeyBytes), + }; +} + +/** + * Convert Ed25519 private key to X25519 private key + * This is needed when working with hyperswarm keys which are Ed25519 format + * @param ed25519PrivateKey - Ed25519 private key as hex string + * @returns X25519 private key as hex string + */ +export function ed25519ToX25519PrivateKey(ed25519PrivateKey: string): string { + const keyPair = ed25519ToX25519KeyPair(ed25519PrivateKey); + return keyPair.privateKey; +} + +/** + * Convert Ed25519 public key to X25519 public key + * Uses native @noble/curves conversion function for proper Ed25519 to X25519 conversion + * @param ed25519PublicKey - Ed25519 public key as hex string + * @returns X25519 public key as hex string + */ +export function ed25519ToX25519PublicKey(ed25519PublicKey: string): string { + const ed25519PublicKeyBytes = hexToUint8Array(ed25519PublicKey); + const x25519PublicKeyBytes = ed25519.utils.toMontgomery(ed25519PublicKeyBytes); + return uint8ArrayToHex(x25519PublicKeyBytes); +} + +/** + * Serialize encrypted data to a hex string for transmission/storage + * @param encryptedData - Encrypted data package + * @returns Serialized hex string + */ +export function serializeEncryptedData(encryptedData: EncryptedData): string { + const { ciphertext, nonce, tag, ephemeralPublicKey } = encryptedData; + + // Format: [ephemeralPublicKey(32) | nonce(12) | tag(16) | ciphertext(variable)] + const buffer = new Uint8Array(32 + 12 + 16 + ciphertext.length); + + buffer.set(ephemeralPublicKey, 0); + buffer.set(nonce, 32); + buffer.set(tag, 44); + buffer.set(ciphertext, 60); + + return uint8ArrayToHex(buffer); +} + +/** + * Deserialize encrypted data from a hex string + * @param hex - Serialized encrypted data as hex string + * @returns Encrypted data package + */ +export function deserializeEncryptedData(hex: string): EncryptedData { + const buffer = hexToUint8Array(hex); + + if (buffer.length < 60) { + throw new Error('Invalid encrypted data buffer length'); + } + + return { + ephemeralPublicKey: buffer.slice(0, 32), + nonce: buffer.slice(32, 44), + tag: buffer.slice(44, 60), + ciphertext: buffer.slice(60), + }; +} + +/** + * Encrypt a string message for a recipient + * @param message - String message to encrypt + * @param recipientPublicKey - Recipient's public key as hex string + * @param keyType - Type of the recipient's public key + * @returns Serialized encrypted data as hex string + */ +export function encryptMessage( + message: string, + recipientPublicKey: string, + keyType: KeyType = KeyType.Ed25519 // Default to Ed25519 +): string { + const messageBytes = new TextEncoder().encode(message); + + // Convert Ed25519 to X25519 if needed + const x25519PublicKey = + keyType === KeyType.Ed25519 ? ed25519ToX25519PublicKey(recipientPublicKey) : recipientPublicKey; + + const recipientPublicKeyBytes = hexToUint8Array(x25519PublicKey); + const encryptedData = encryptInternal(messageBytes, recipientPublicKeyBytes); + return serializeEncryptedData(encryptedData); +} + +/** + * Decrypt a string message using your private key + * @param encryptedHex - Serialized encrypted data as hex string + * @param privateKey - Your private key as hex string (supports hyperswarm concatenated format) + * @param keyType - Type of your private key + * @returns Decrypted string message + */ +export function decryptMessage( + encryptedHex: string, + privateKey: string, + keyType: KeyType = KeyType.Ed25519 +): string { + const encryptedData = deserializeEncryptedData(encryptedHex); + + let actualPrivateKey = privateKey; + + // Handle hyperswarm key format for Ed25519 keys + if (keyType === KeyType.Ed25519) { + // Hyperswarm secretKey is often 64 bytes (private + public concatenated) + // Extract the first 32 bytes (64 hex chars) for the private key + actualPrivateKey = privateKey.length === 128 ? privateKey.slice(0, 64) : privateKey; + } + + // Convert Ed25519 to X25519 if needed + const x25519PrivateKey = + keyType === KeyType.Ed25519 ? ed25519ToX25519PrivateKey(actualPrivateKey) : actualPrivateKey; + + const decryptedBytes = decryptInternal(encryptedData, hexToUint8Array(x25519PrivateKey)); + return new TextDecoder().decode(decryptedBytes); +} + +/** + * Generate a key pair with hex string outputs + * @returns Object containing private and public keys as hex strings + */ +export function generateKeyPairHex(): { privateKey: string; publicKey: string } { + const keyPair = generateKeyPair(); + return { + privateKey: uint8ArrayToHex(keyPair.privateKey), + publicKey: uint8ArrayToHex(keyPair.publicKey), + }; +} diff --git a/src/services/p2p/interface.ts b/src/services/p2p/interface.ts new file mode 100644 index 000000000..f04a00be1 --- /dev/null +++ b/src/services/p2p/interface.ts @@ -0,0 +1,50 @@ +export enum MessageType { + Alert = 'Alert', + Text = 'Text', + Image = 'Image', + RequestSats = 'RequestSats', + SendSats = 'SendSats', + RequestAsset = 'RequestAsset', + SendAsset = 'SendAsset', +} + +export enum CommunityType { + Peer = 'Peer', + Group = 'Group', + Broadcast = 'Broadcast', +} + +export interface Message { + id: string; + communityId: string; + type: MessageType; + text: string; + createdAt: number; + sender: string; + block: number; + unread: boolean; + fileUrl?: string; +} + +export interface Community { + id: string; + name: string; + type: CommunityType; + createdAt: number; + updatedAt: number; + with?: string; + key?: string; +} + +export interface Contact { + appID: string; + contactKey: string; + imageUrl?: string; + name: string; +} + +export enum deeplinkType { + Contact = 'contact', + Group = 'group', + Broadcast = 'broadcast', +} diff --git a/src/services/p2p/rpc-commands.mjs b/src/services/p2p/rpc-commands.mjs new file mode 100644 index 000000000..4ac589141 --- /dev/null +++ b/src/services/p2p/rpc-commands.mjs @@ -0,0 +1,10 @@ +export const RPC_KEY = 1; +export const RPC_KEY_RECEIVED = 2; +export const GET_KEYS = 4; +export const GET_PEERS = 5; +export const RPC_TEST = 3; +export const ON_CONNECTION = 6; +export const ON_MESSAGE = 7; +export const ON_ERROR = 8; +export const SEND_MESSAGE = 9; +export const JOIN_PEER = 10; diff --git a/src/services/p2p/worklet.mjs b/src/services/p2p/worklet.mjs new file mode 100644 index 000000000..ebcdbfd84 --- /dev/null +++ b/src/services/p2p/worklet.mjs @@ -0,0 +1,135 @@ +import Hyperswarm from 'hyperswarm'; +import { + GET_KEYS, + GET_PEERS, + JOIN_PEER, + ON_CONNECTION, + ON_ERROR, + ON_MESSAGE, + RPC_KEY, + RPC_KEY_RECEIVED, + SEND_MESSAGE, +} from './rpc-commands.mjs'; +import RPC from 'bare-rpc'; +import b4a from 'b4a'; + +import { Buffer } from 'buffer'; +global.Buffer = Buffer; + +const { IPC } = BareKit; + +let keyPair; +const connections = new Map(); + +const RELAY_PUB_KEY = ''; // replace with actual relay public key from .env while building the app.bundle.mjs (config.ts import fails to load in worklet) + +const rpc = new RPC(IPC, (req, error) => { + try { + const data = b4a.toString(req.data); + console.log('REQ', req.command, data); + if (req.command === RPC_KEY_RECEIVED) { + } else if (req.command === GET_KEYS) { + req.reply(JSON.stringify(getKeys())); + } else if (req.command === GET_PEERS) { + req.reply(JSON.stringify(getPeers())); + } else if (req.command === SEND_MESSAGE) { + sendMessage(data); + } else if (req.command === JOIN_PEER) { + joinPeer(data); + } + + if (error) { + console.log(error); + } + } catch (error) { + console.log(error); + } +}); + +const swarm = new Hyperswarm({ + seed: Buffer.from(Bare.argv[0], 'hex'), +}); + +keyPair = swarm.keyPair; + +swarm.on('connection', (conn, info) => { + console.log(`JOINING info.publicKey.toString('hex')`); + + connections.set(info.publicKey.toString('hex'), conn); + + console.log(`connected to ${info.publicKey.toString('hex')}`); + const req = rpc.request(ON_CONNECTION); + req.send( + JSON.stringify({ + publicKey: info.publicKey.toString('hex'), + }) + ); + + conn.on('data', (data) => { + console.log(`received: ${data.toString()}`); + const req = rpc.request(ON_MESSAGE); + req.send( + JSON.stringify({ + data: data.toString(), + publicKey: info.publicKey.toString('hex'), + }) + ); + }); + + conn.on('error', (err) => { + console.error(`connection error:`, err); + const req = rpc.request(ON_ERROR); + req.send( + JSON.stringify({ + err, + }) + ); + }); +}); + +const getKeys = () => { + return { + publicKey: swarm.keyPair.publicKey.toString('hex'), + secretKey: swarm.keyPair.secretKey.toString('hex'), + }; +}; + +const getPeers = () => { + return JSON.stringify(Array.from(swarm.peers.entries())); +}; + +const joinPeer = async (pubKey) => { + swarm.joinPeer(Buffer.from(pubKey, 'hex')); + await swarm.flush(); + console.log(`joinpeer ${pubKey}`); +}; + +const sendMessage = async (payload) => { + console.log('sendMessage', payload); + const relayConn = connections.get(RELAY_PUB_KEY); + if (relayConn) { + relayConn.write(payload); + } else { + console.error(`No connection found for publicKey: ${payload.pubKey}`); + } +}; + +const req = rpc.request(RPC_KEY); +req.send( + JSON.stringify({ + publicKey: swarm.keyPair.publicKey.toString('hex'), + secretKey: swarm.keyPair.secretKey.toString('hex'), + }) +); + +const d = swarm.join(swarm.keyPair.publicKey, { server: true, client: false }); +await swarm.join(Buffer.from(RELAY_PUB_KEY, 'hex'), { client: true, server: true }); +await swarm.flush(); + +// const replyBuffer = await req.reply() +// console.log(replyBuffer.toString()) + +// IPC.write(JSON.stringify({ +// publicKey: swarm.keyPair.publicKey.toString('hex'), +// secretKey: swarm.keyPair.secretKey.toString('hex'), +// })); diff --git a/src/storage/realm/enum.ts b/src/storage/realm/enum.ts index d61a8009a..74c5b11ee 100644 --- a/src/storage/realm/enum.ts +++ b/src/storage/realm/enum.ts @@ -44,5 +44,8 @@ export enum RealmSchema { USDTWalletPresentationData = 'USDTWalletPresentationData', USDTWalletDerivationDetails = 'USDTWalletDerivationDetails', USDTWalletAccountStatus = 'USDTWalletAccountStatus', + Community = 'Community', + Message = 'Message', + Contact = 'Contact', SwapHistory = 'SwapHistory', } diff --git a/src/storage/realm/realm.ts b/src/storage/realm/realm.ts index d1e0e9126..d8b70d8e1 100644 --- a/src/storage/realm/realm.ts +++ b/src/storage/realm/realm.ts @@ -10,7 +10,7 @@ export class RealmDatabase { public static file = REALM_FILE; - public static schemaVersion = 106; + public static schemaVersion = 109; /** * initializes/opens realm w/ appropriate configuration diff --git a/src/storage/realm/schema/app.ts b/src/storage/realm/schema/app.ts index cb3e9e27f..696ac467c 100644 --- a/src/storage/realm/schema/app.ts +++ b/src/storage/realm/schema/app.ts @@ -15,6 +15,8 @@ export const KeeperAppSchema: ObjectSchema = { subscription: RealmSchema.StoreSubscription, backup: RealmSchema.Backup, enableAnalytics: { type: 'bool', default: false }, + contactsKey: 'string?{}', + profilePicture: 'string?', }, primaryKey: 'id', }; diff --git a/src/storage/realm/schema/community.ts b/src/storage/realm/schema/community.ts new file mode 100644 index 000000000..fc2148409 --- /dev/null +++ b/src/storage/realm/schema/community.ts @@ -0,0 +1,42 @@ +import { ObjectSchema } from 'realm'; +import { RealmSchema } from '../enum'; + +export const MessageSchema: ObjectSchema = { + name: RealmSchema.Message, + primaryKey: 'id', + properties: { + id: 'string', + communityId: 'string', + block: 'int?', + unread: 'bool', + createdAt: { type: 'int', default: Date.now() }, + text: 'string?', + type: 'string?', + sender: 'string', + fileUrl: 'string?', + }, +}; + +export const ContactSchema: ObjectSchema = { + name: RealmSchema.Contact, + primaryKey: 'contactKey', + properties: { + contactKey: 'string', + appID: 'string', + imageUrl: 'string?', + name: 'string', + }, +}; + +export const CommunitySchema: ObjectSchema = { + name: RealmSchema.Community, + primaryKey: 'id', + properties: { + id: 'string', + name: 'string?', + createdAt: { type: 'int', default: Date.now() }, + type: 'string', + with: 'string?', + key: 'string?', + }, +}; diff --git a/src/storage/realm/schema/index.ts b/src/storage/realm/schema/index.ts index 3bfd9dc20..c4a3149ea 100644 --- a/src/storage/realm/schema/index.ts +++ b/src/storage/realm/schema/index.ts @@ -51,6 +51,7 @@ import { USDTWalletDerivationDetailsSchema, USDTWalletAccountStatusSchema, } from './usdtWallet'; +import { CommunitySchema, ContactSchema, MessageSchema } from './community'; export default [ KeeperAppSchema, @@ -101,5 +102,8 @@ export default [ USDTWalletDerivationDetailsSchema, USDTWalletPresentationDataSchema, USDTWalletAccountStatusSchema, + MessageSchema, + ContactSchema, + CommunitySchema, SwapHistorySchema, ]; diff --git a/src/store/sagas/login.ts b/src/store/sagas/login.ts index dfd5a901e..f5e441dff 100644 --- a/src/store/sagas/login.ts +++ b/src/store/sagas/login.ts @@ -189,7 +189,7 @@ function* credentialsAuthWorker({ payload }) { yield select((state) => state.storage); // setting correct app id from realm at login - const keeperApp = yield call(dbManager.getObjectByIndex, RealmSchema.KeeperApp); + const keeperApp: KeeperApp = yield call(dbManager.getObjectByIndex, RealmSchema.KeeperApp); if (keeperApp?.id) yield put(setAppId(keeperApp.id)); // Store temporary account details diff --git a/src/theme/Colors.ts b/src/theme/Colors.ts index c7e905a55..c67713372 100644 --- a/src/theme/Colors.ts +++ b/src/theme/Colors.ts @@ -153,7 +153,11 @@ const Colors = { PersianGreen: 'rgba(0, 175, 146, 1)', CrimsonRed: 'rgba(217, 44, 44, 1)', graybeige: 'rgba(211, 209, 206, 0.15)', + stoneGrey: 'rgba(211, 209, 206, 0.5)', + coolGrey: 'rgba(211, 209, 206, 1)', + lightstone: 'rgba(121, 121, 121, 1)', lightGrayBeige: 'rgba(114, 114, 114, 1)', lightRed: 'rgba(217, 44, 44, 0.1)', + lightGrayBeige: 'rgba(114, 114, 114, 1)', }; export default Colors; diff --git a/src/utils/service-utilities/ChatEncryptionManager.ts b/src/utils/service-utilities/ChatEncryptionManager.ts new file mode 100644 index 000000000..e7e78dc71 --- /dev/null +++ b/src/utils/service-utilities/ChatEncryptionManager.ts @@ -0,0 +1,269 @@ +import CryptoJS from 'crypto-js'; +import { encode as base64Encode, decode as base64Decode } from '@stablelib/base64'; +import { x25519, ed25519 } from '@noble/curves/ed25519'; +import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils'; + +export interface KeyPair { + publicKey: string; // hex format + secretKey: string; // hex format (64 bytes: private + public) +} + +export interface SessionKeys { + aesKey: string; // hex format +} + +export interface EncryptedKeys { + encryptedKeys: string; // base64 encrypted session keys + iv: string; // hex format +} + +export interface EncryptedMessage { + ciphertext: string; // base64 format + iv: string; // hex format +} + +export class ChatEncryptionManagerError extends Error { + constructor(message: string, public readonly code?: string) { + super(message); + this.name = 'ChatEncryptionManagerError'; + } +} + +export class KeyExchangeError extends ChatEncryptionManagerError { + constructor(message: string) { + super(message, 'KEY_EXCHANGE_ERROR'); + this.name = 'KeyExchangeError'; + } +} + +export class EncryptionError extends ChatEncryptionManagerError { + constructor(message: string) { + super(message, 'ENCRYPTION_ERROR'); + this.name = 'EncryptionError'; + } +} + +export class DecryptionError extends ChatEncryptionManagerError { + constructor(message: string) { + super(message, 'DECRYPTION_ERROR'); + this.name = 'DecryptionError'; + } +} +export class ChatEncryptionManager { + /** + * Perform key exchange with peer's public key (hex format) + * @param keyPair - Your Ed25519 keypair in hex format + * @param peerPublicKeyHex - Peer's 32-byte public key in hex format + * @returns Base64 encoded shared secret + * @throws {KeyExchangeError} If key exchange fails + */ + static performKeyExchange(keyPair: KeyPair, peerPublicKeyHex: string): string { + try { + const publicKey = ChatEncryptionManager.hexToUint8Array(keyPair.publicKey); + const privateKeyHex = + keyPair.secretKey.length === 128 ? keyPair.secretKey.slice(0, 64) : keyPair.secretKey; + const privateKey = ChatEncryptionManager.hexToUint8Array(privateKeyHex); + + if (publicKey.length !== 32 || privateKey.length !== 32) { + throw new KeyExchangeError('Invalid key lengths: expected 32 bytes each'); + } + + const peerPublicKey = ChatEncryptionManager.hexToUint8Array(peerPublicKeyHex); + if (peerPublicKey.length !== 32) { + throw new KeyExchangeError(`Invalid peer key length: ${peerPublicKey.length}, expected 32`); + } + + // Create commutative shared secret + const myPubHex = ChatEncryptionManager.uint8ArrayToHex(publicKey); + const peerPubHex = ChatEncryptionManager.uint8ArrayToHex(peerPublicKey); + const sortedKeys = [myPubHex, peerPubHex].sort(); + const combined = 'hyperswarm_shared_secret:' + sortedKeys[0] + sortedKeys[1]; + + const hash = CryptoJS.SHA256(combined); + const sharedSecret = new Uint8Array(32); + for (let i = 0; i < 32; i++) { + sharedSecret[i] = (hash.words[Math.floor(i / 4)] >>> (24 - (i % 4) * 8)) & 0xff; + } + + const secretHex = ChatEncryptionManager.uint8ArrayToHex(sharedSecret); + const info = 'ChatEncryptionManager-KeyExchange-v1'; + const derivedKey = CryptoJS.SHA256(secretHex + info); + + const finalKey = new Uint8Array(32); + for (let i = 0; i < 32; i++) { + finalKey[i] = (derivedKey.words[Math.floor(i / 4)] >>> (24 - (i % 4) * 8)) & 0xff; + } + + return base64Encode(finalKey); + } catch (error) { + throw new KeyExchangeError( + `Key exchange failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + static deriveSharedSecret(privateKey: string, publicKey: string): string { + const privKeyBytes = hexToBytes(privateKey).slice(0, 32); + return bytesToHex(x25519.getSharedSecret(privKeyBytes, publicKey)); + } + + static derivePublicKey(privateKey: string): string { + return bytesToHex(x25519.getPublicKey(hexToBytes(privateKey).slice(0, 32))); + } + + /** + * Generate session keys for encryption + * @returns Object containing hex-encoded AES key + */ + static generateSessionKeys(): SessionKeys { + const aesKey = CryptoJS.lib.WordArray.random(32); // 256-bit + return { + aesKey: aesKey.toString(), + }; + } + + /** + * Encrypt session keys for transmission + * @param aesKeyHex - Hex-encoded AES key to encrypt + * @param sharedSecretB64 - Base64 encoded shared secret from key exchange + * @returns Encrypted keys with IV + * @throws {EncryptionError} If encryption fails + */ + static encryptKeys(aesKeyHex: string, sharedSecretB64: string): EncryptedKeys { + try { + const sharedSecret = CryptoJS.lib.WordArray.create(base64Decode(sharedSecretB64)); + const keysData = JSON.stringify({ + aesKey: aesKeyHex, + }); + + const iv = CryptoJS.lib.WordArray.random(16); + const encrypted = CryptoJS.AES.encrypt(keysData, sharedSecret, { + iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + + return { encryptedKeys: encrypted.toString(), iv: iv.toString() }; + } catch (error) { + throw new EncryptionError( + `Key encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Decrypt received session keys + * @param encryptedKeyData - Encrypted keys data with IV + * @param sharedSecretB64 - Base64 encoded shared secret + * @returns Decrypted session keys + * @throws {DecryptionError} If decryption fails + */ + static decryptKeys(encryptedKeyData: EncryptedKeys, sharedSecretB64: string): SessionKeys { + try { + const sharedSecret = CryptoJS.lib.WordArray.create(base64Decode(sharedSecretB64)); + const decrypted = CryptoJS.AES.decrypt(encryptedKeyData.encryptedKeys, sharedSecret, { + iv: CryptoJS.enc.Hex.parse(encryptedKeyData.iv), + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + + const keysData: SessionKeys = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8)); + + return keysData; + } catch (error) { + throw new DecryptionError( + `Key decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Encrypt message with AES only (no authentication) + * @param message - Plain text message to encrypt + * @param aesKeyHex - Hex-encoded AES key for this chat thread + * @returns Encrypted message + * @throws {EncryptionError} If encryption fails + */ + static encryptMessage(message: string, aesKeyHex: string): EncryptedMessage { + try { + const aesKey = CryptoJS.enc.Hex.parse(aesKeyHex); + const iv = CryptoJS.lib.WordArray.random(16); + const encrypted = CryptoJS.AES.encrypt(message, aesKey, { + iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + + const ciphertext = encrypted.toString(); + const ivHex = iv.toString(); + + return { ciphertext, iv: ivHex }; + } catch (error) { + throw new EncryptionError( + `Message encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Decrypt message with AES only (no authentication verification) + * @param encryptedData - Encrypted message data + * @param aesKeyHex - Hex-encoded AES key for this chat thread + * @returns Decrypted plain text message + * @throws {DecryptionError} If decryption fails + */ + static decryptMessage(encryptedData: EncryptedMessage, aesKeyHex: string): string { + try { + const aesKey = CryptoJS.enc.Hex.parse(aesKeyHex); + const decrypted = CryptoJS.AES.decrypt(encryptedData.ciphertext, aesKey, { + iv: CryptoJS.enc.Hex.parse(encryptedData.iv), + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + const result = decrypted.toString(CryptoJS.enc.Utf8); + if (!result) { + throw new DecryptionError('Decryption failed - invalid key or corrupted data'); + } + return result; + } catch (error) { + if (error instanceof DecryptionError) throw error; + throw new DecryptionError( + `Message decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Get public key in hex format from a keypair + * @param keyPair - Ed25519 keypair + * @returns Hex encoded public key + */ + static getPublicKeyHex(keyPair: KeyPair): string { + return keyPair.publicKey; + } + + /** + * Convert hex string to Uint8Array + * @private + */ + private static hexToUint8Array(hex: string): Uint8Array { + if (hex.length % 2 !== 0) { + throw new Error('Invalid hex string length'); + } + const bytes = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + bytes[i / 2] = parseInt(hex.substr(i, 2), 16); + } + return bytes; + } + + /** + * Convert Uint8Array to hex string + * @private + */ + private static uint8ArrayToHex(bytes: Uint8Array): string { + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, '0')) + .join(''); + } +} diff --git a/src/utils/service-utilities/config.ts b/src/utils/service-utilities/config.ts index fbf8648d3..2fa165067 100644 --- a/src/utils/service-utilities/config.ts +++ b/src/utils/service-utilities/config.ts @@ -98,6 +98,8 @@ class Configuration { public GASFREE_API_KEY: string = config.GASFREE_API_KEY?.trim(); public GASFREE_API_SECRET: string = config.GASFREE_API_SECRET?.trim(); + public RELAY_PEER_PUB_KEY: string = config.RELAY_PEER_PUB_KEY?.trim(); + public RENEWAL_WINDOW: number; public LETS_EXCHANGE_BASE_URL: string = config.LETS_EXCHANGE_BASE_URL?.trim(); diff --git a/yarn.lock b/yarn.lock index fdfd1e091..926d3c25c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1210,20 +1210,7 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" - integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.27.1" - "@babel/parser" "^7.27.1" - "@babel/template" "^7.27.1" - "@babel/types" "^7.27.1" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/traverse@^7.20.0", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.9", "@babel/traverse@^7.27.1": +"@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3", "@babel/traverse@^7.20.0", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.9", "@babel/traverse@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== @@ -1475,6 +1462,21 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@hyperswarm/secret-stream@^6.6.2": + version "6.8.1" + resolved "https://registry.yarnpkg.com/@hyperswarm/secret-stream/-/secret-stream-6.8.1.tgz#15e6d8cde923a77e86075045a06188258b35b022" + integrity sha512-F3fr8CKB6za9Ac7ifjgAe07qnnesl5kS0MtLsyKxA1Og8E+FZykdwLpgoLjnEa7G6E1L56lASLr42E4kd20sog== + dependencies: + b4a "^1.1.0" + hypercore-crypto "^3.3.1" + noise-curve-ed "^2.0.1" + noise-handshake "^4.0.0" + sodium-secretstream "^1.1.0" + sodium-universal "^5.0.0" + streamx "^2.14.0" + timeout-refresh "^2.0.0" + unslab "^1.3.0" + "@internationalized/date@^3.8.0": version "3.8.0" resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.8.0.tgz#24fb301029224351381aa87cba853ca1093af094" @@ -1816,6 +1818,13 @@ dependencies: "@noble/hashes" "1.4.0" +"@noble/curves@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.4.tgz#a748c6837ee7854a558cc3b951aedd87a5e7d6a5" + integrity sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw== + dependencies: + "@noble/hashes" "1.8.0" + "@noble/hashes@1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" @@ -1826,7 +1835,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== -"@noble/hashes@^1.2.0": +"@noble/hashes@1.8.0", "@noble/hashes@^1.2.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== @@ -3449,6 +3458,11 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== +"@stablelib/base64@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-2.0.1.tgz#f1546ab26896b3490d1ab531373c0dc39e12cee1" + integrity sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ== + "@svgr/babel-plugin-add-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" @@ -3571,11 +3585,6 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== -"@types/async@^3.2.6": - version "3.2.24" - resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.24.tgz#3a96351047575bbcf2340541b2d955a35339608f" - integrity sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw== - "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -4262,11 +4271,6 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async@^3.2.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" - integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -4307,6 +4311,11 @@ axobject-query@^4.1.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== +b4a@1.6.7, b4a@^1.1.0, b4a@^1.1.1, b4a@^1.3.0, b4a@^1.3.1, b4a@^1.5.0, b4a@^1.5.3, b4a@^1.6.1, b4a@^1.6.4, b4a@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + babel-core@^7.0.0-bridge.0: version "7.0.0-bridge.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" @@ -4442,6 +4451,151 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-addon-resolve@^1.3.0: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bare-addon-resolve/-/bare-addon-resolve-1.9.4.tgz#50ad7b26de493f978f09bbbbe987219c4ecbfd28" + integrity sha512-unn6Vy/Yke6F99vg/7tcrvM2KUvIhTNniaSqDbam4AWkd4NhvDVSrQiRYVlNzUV2P7SPobkCK7JFVxrJk9btCg== + dependencies: + bare-module-resolve "^1.10.0" + bare-semver "^1.0.0" + +bare-env@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-env/-/bare-env-3.0.0.tgz#d61bf1fd7d1b590120f936d429a6c062db761882" + integrity sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw== + dependencies: + bare-os "^3.0.1" + +bare-events@^2.0.0, bare-events@^2.2.0, bare-events@^2.3.1, bare-events@^2.5.3, bare-events@^2.5.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.6.0.tgz#11d9506da109e363a2f3af050fbb005ccdb3ee8f" + integrity sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg== + +bare-fs@^4.0.0: + version "4.1.6" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.1.6.tgz#0925521e7310f65cb1f154cab264f0b647a7cdef" + integrity sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ== + dependencies: + bare-events "^2.5.4" + bare-path "^3.0.0" + bare-stream "^2.6.4" + +bare-hrtime@^2.0.0: + version "2.0.11" + resolved "https://registry.yarnpkg.com/bare-hrtime/-/bare-hrtime-2.0.11.tgz#1e7fb1faa0206d987359ad16769173ebd295cd91" + integrity sha512-Mnb2rnGRSHzNHQwFrK8VVqD8Oob/CiSsBlgZGKQ1xTj25GPI19sXHjUPzrkHOU1DaoVj1ig1VO7rZGaYOfXPaw== + +bare-link@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/bare-link/-/bare-link-1.4.1.tgz#e15ca6d365aaeaf07bd881cbb757b7223ca2b1a8" + integrity sha512-vvKNzzr1SvT9CIblTDb/gC7VpEENSc2D8DO01at58heiI1Hbe3QMDBUoIwZEnWigYgGEHHr0e/Fk7mzxe123/Q== + dependencies: + bare-fs "^4.0.0" + bare-module-resolve "^1.8.2" + bare-os "^3.2.0" + bare-path "^3.0.0" + bare-process "^4.0.0" + bare-subprocess "^5.0.2" + bare-url "^2.0.9" + paparam "^1.5.0" + require-asset "^1.1.0" + +bare-module-resolve@^1.10.0, bare-module-resolve@^1.6.2, bare-module-resolve@^1.8.2: + version "1.11.1" + resolved "https://registry.yarnpkg.com/bare-module-resolve/-/bare-module-resolve-1.11.1.tgz#4ea4d1dd82947a00b4f4bbbfce2379eedd9c9e09" + integrity sha512-DCxeT9i8sTs3vUMA3w321OX/oXtNEu5EjObQOnTmCdNp5RXHBAvAaBDHvAi9ta0q/948QPz+co6SsGi6aQMYRg== + dependencies: + bare-semver "^1.0.0" + +bare-os@^3.0.1, bare-os@^3.2.0, bare-os@^3.3.1, bare-os@^3.5.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.6.1.tgz#9921f6f59edbe81afa9f56910658422c0f4858d4" + integrity sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g== + +bare-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" + integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== + dependencies: + bare-os "^3.0.1" + +bare-pipe@^4.0.0: + version "4.0.6" + resolved "https://registry.yarnpkg.com/bare-pipe/-/bare-pipe-4.0.6.tgz#e4e328950595ae45ded314e9329f0adb755033b7" + integrity sha512-S9kpDJq74gkceynfRqXCaED119k2jF2us4U9xZnTLX0GEduWQY29MNTpAMAf/ucEailW9SPLjGLzbfPCONz3Lw== + dependencies: + bare-events "^2.0.0" + bare-stream "^2.0.0" + +bare-process@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/bare-process/-/bare-process-4.2.1.tgz#64dc819a9c3f5ec3d168ffc078cc517900ace7a7" + integrity sha512-wcmyQWTHxd2xRgeKUSY46ofmuEAJ9CLo/6swJTHOZFPYpBShMWNPVI2Ba8o0n/X/YE4as99M28x37saWZ1L1vQ== + dependencies: + bare-env "^3.0.0" + bare-events "^2.3.1" + bare-hrtime "^2.0.0" + bare-os "^3.5.0" + bare-pipe "^4.0.0" + bare-signals "^4.0.0" + bare-tty "^5.0.0" + +bare-rpc@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bare-rpc/-/bare-rpc-0.2.6.tgz#f2f53d0b687822d9969f93ceff4673a6df2e0b6c" + integrity sha512-NdSoIaqvofcS1OljLcetM7X+jAwqCPekIa/oTFmRBUKjcjWz4pMta0rEWi6k53CeoYqszsdhxKhsoPneWvGY+A== + dependencies: + b4a "^1.6.6" + bare-stream "^2.1.3" + compact-encoding "^2.15.0" + safety-catch "^1.0.2" + +bare-semver@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bare-semver/-/bare-semver-1.0.1.tgz#4737d660785f07cb723cefa6b022089e59120a61" + integrity sha512-UtggzHLiTrmFOC/ogQ+Hy7VfoKoIwrP1UFcYtTxoCUdLtsIErT8+SWtOC2DH/snT9h+xDrcBEPcwKei1mzemgg== + +bare-signals@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bare-signals/-/bare-signals-4.0.3.tgz#02ce3b28d9fb1e60feedb0d443c3e3031c3adb60" + integrity sha512-xIy8ILawd3xepmpsYYMzPgMWsZm471Onw6jcqoitczuH6kNqN0NVkFKcEztpdq82MMfcsyo7xqLQIgmeoB05rg== + dependencies: + bare-events "^2.5.3" + bare-os "^3.3.1" + +bare-stream@^2.0.0, bare-stream@^2.1.3, bare-stream@^2.6.4: + version "2.6.5" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.6.5.tgz#bba8e879674c4c27f7e27805df005c15d7a2ca07" + integrity sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA== + dependencies: + streamx "^2.21.0" + +bare-subprocess@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bare-subprocess/-/bare-subprocess-5.1.0.tgz#c8bffe75ee60eb5d06fd9e300272f82da25aa855" + integrity sha512-uUQdB2Ks+eUXPwYDGKQ2ZX1hV8AoS1fs5ep33GkMIAv21f4zy/soV9IejjlbSqJbnmDLC44RyitRzSAbIed1ng== + dependencies: + bare-env "^3.0.0" + bare-events "^2.5.4" + bare-os "^3.0.1" + bare-pipe "^4.0.0" + +bare-tty@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/bare-tty/-/bare-tty-5.0.2.tgz#04b972c4f96c0818ca3335a6350195bafbbf5b85" + integrity sha512-xOHwI7zZl2Opm7Rul5O+okE32j7O14feJhgovJX2EghtQ2IWVfiC1oH0DmFruMvKthvhZY/Lpg8n5SVBaZhV1A== + dependencies: + bare-events "^2.2.0" + bare-signals "^4.0.0" + bare-stream "^2.0.0" + +bare-url@^2.0.9, bare-url@^2.1.0: + version "2.1.6" + resolved "https://registry.yarnpkg.com/bare-url/-/bare-url-2.1.6.tgz#86937c11c29b094c821459d3041b7fa67322266e" + integrity sha512-FgjDeR+/yDH34By4I0qB5NxAoWv7dOTYcOXwn73kr+c93HyC2lU6tnjifqUe33LKMJcDyCYPQjEAqgOQiXkE2Q== + dependencies: + bare-path "^3.0.0" + base-64@0.1.0, base-64@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" @@ -4588,6 +4742,13 @@ bitcoinjs-message@2.2.0: secp256k1 "^3.0.1" varuint-bitcoin "^1.0.1" +bits-to-bytes@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz#b6b0b547ff5747b0609a42e31a0b57212f09f4e7" + integrity sha512-OJoHTpFXS9bXHBCekGTByf3MqM8CGblBDIduKQeeVVeiU9dDWywSSirXIBYGgg3d1zbVuvnMa1vD4r6PA0kOKg== + dependencies: + b4a "^1.5.0" + bl@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" @@ -4605,6 +4766,20 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +blind-relay@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/blind-relay/-/blind-relay-1.4.0.tgz#1c01263e9f9297c2f898668fcc1693fd816a8beb" + integrity sha512-6xt7fDfCs6eGmNNym6I9N42jmjcMQn2qwwOVnkP9ZnrkXFk6c4/tdO1xqRmDEzKzV8gigd+DVdCUG/RUYnen7Q== + dependencies: + b4a "^1.6.4" + bare-events "^2.2.0" + bits-to-bytes "^1.3.0" + compact-encoding "^2.12.0" + compact-encoding-bitfield "^1.0.0" + protomux "^3.5.1" + sodium-universal "^5.0.0" + streamx "^2.15.1" + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.2" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.2.tgz#3d8fed6796c24e177737f7cc5172ee04ef39ec99" @@ -4615,6 +4790,14 @@ bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== +bogon@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/bogon/-/bogon-1.1.0.tgz#14234260353d8f806c3a307202f66698f635d18b" + integrity sha512-a6SnToksXHuUlgeMvI/txWmTcKz7c7iBa8f0HbXL4toN1Uza/CTQ4F7n9jSDX49TCpxv3KUP100q4sZfwLyLiw== + dependencies: + compact-encoding "^2.11.0" + compact-encoding-net "^1.2.0" + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -5191,6 +5374,27 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +compact-encoding-bitfield@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/compact-encoding-bitfield/-/compact-encoding-bitfield-1.0.0.tgz#39f923263dffb68b266b6cb9dc0bae2d42cf5c4a" + integrity sha512-3nMVKUg+PF72UHfainmCL8uKvyWfxsjqOtUY+HiMPGLPCTjnwzoKfFAMo1Ad7nwTPdjBqtGK5b3BOFTFW4EBTg== + dependencies: + compact-encoding "^2.4.1" + +compact-encoding-net@^1.0.1, compact-encoding-net@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/compact-encoding-net/-/compact-encoding-net-1.2.0.tgz#19d2efd55df10f2ee793576fc4483bf6d2218743" + integrity sha512-LVXpNpF7PGQeHRVVLGgYWzuVoYAaDZvKUsUxRioGfkotzvOh4AzoQF1HBH3zMNaSnx7gJXuUr3hkjnijaH/Eng== + dependencies: + compact-encoding "^2.4.1" + +compact-encoding@^2.11.0, compact-encoding@^2.12.0, compact-encoding@^2.15.0, compact-encoding@^2.4.1, compact-encoding@^2.5.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/compact-encoding/-/compact-encoding-2.16.1.tgz#b05ecf04fbe198d9d5ea69cfc0a235639214594a" + integrity sha512-vP39X4nwtesmZucaAxDg4wnudOoaJTSR+fikzi8VLVxbwLmcWXf3t0LxY0n2H1AMpdoQZ08lmUf4GY3XiDPnMQ== + dependencies: + b4a "^1.3.0" + compressible@~2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -5640,6 +5844,23 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dht-rpc@^6.15.1: + version "6.18.1" + resolved "https://registry.yarnpkg.com/dht-rpc/-/dht-rpc-6.18.1.tgz#6e4109989d47fea487f0be8efa2980557019fde7" + integrity sha512-uTcHj8dERVjmoUsSxqAulQ+qo11Qy4pMK419tyF2cFpyf56Y09u1v9g8yWlay0KjUY5bvdybBQ/V8YWN/wyK5Q== + dependencies: + b4a "^1.6.1" + bare-events "^2.2.0" + compact-encoding "^2.11.0" + compact-encoding-net "^1.2.0" + fast-fifo "^1.1.0" + kademlia-routing-table "^1.0.1" + nat-sampler "^1.0.1" + sodium-universal "^5.0.0" + streamx "^2.13.2" + time-ordered-set "^2.0.0" + udx-native "^1.5.3" + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -6465,6 +6686,11 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== +fast-fifo@^1.1.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" @@ -7112,6 +7338,60 @@ husky@8.0.3: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184" integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== +hypercore-crypto@^3.3.0, hypercore-crypto@^3.3.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz#582bc3d4c4e3e6f8adf6b86222c8db73006aadfa" + integrity sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA== + dependencies: + b4a "^1.6.6" + compact-encoding "^2.15.0" + sodium-universal "^5.0.0" + +hypercore-id-encoding@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz#b3822eb97bee89ac134ffea2b8b67230ca166f55" + integrity sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ== + dependencies: + b4a "^1.5.3" + z32 "^1.0.0" + +hyperdht@^6.11.0: + version "6.20.5" + resolved "https://registry.yarnpkg.com/hyperdht/-/hyperdht-6.20.5.tgz#087e93a28e67cadde8413a010a99a5941d8d48ab" + integrity sha512-eDAwTmAtE9rjMivgqYtqHalTdBVhhCMBVHlCWRVhEcWtchpDonsd2dmX26lJ0raoF+l9djkXvPcN1/kb9/kykw== + dependencies: + "@hyperswarm/secret-stream" "^6.6.2" + b4a "^1.3.1" + bare-events "^2.2.0" + blind-relay "^1.3.0" + bogon "^1.0.0" + compact-encoding "^2.4.1" + compact-encoding-net "^1.0.1" + dht-rpc "^6.15.1" + hypercore-crypto "^3.3.0" + hypercore-id-encoding "^1.2.0" + noise-curve-ed "^2.0.0" + noise-handshake "^4.0.0" + record-cache "^1.1.1" + safety-catch "^1.0.1" + signal-promise "^1.0.3" + sodium-universal "^5.0.1" + streamx "^2.16.1" + unslab "^1.3.0" + xache "^1.1.0" + +hyperswarm@4.11.7: + version "4.11.7" + resolved "https://registry.yarnpkg.com/hyperswarm/-/hyperswarm-4.11.7.tgz#2c35f6050bc87e413f0fcb754ddab2de5f9df535" + integrity sha512-/cOHoiuho81u8OdJhJkAg3Byc2X/47doS4pO6L8EMnhPJnYDHtLYa/SdVsbGNjzjso0Q20qAe7WTwbqdtt/diA== + dependencies: + b4a "^1.3.1" + bare-events "^2.2.0" + hyperdht "^6.11.0" + safety-catch "^1.0.2" + shuffled-priority-queue "^2.1.0" + unslab "^1.3.0" + hyphenate-style-name@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz#1797bf50369588b47b72ca6d5e65374607cf4436" @@ -8128,6 +8408,13 @@ jsonfile@^4.0.0: object.assign "^4.1.4" object.values "^1.1.6" +kademlia-routing-table@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/kademlia-routing-table/-/kademlia-routing-table-1.0.6.tgz#6d76fe4415d8a51d3181b20882bc0eb2fe859ee1" + integrity sha512-Ve6jwIlUCYvUzBnXnzVRHDZCFgXURW9gmF3r7n05kZs/2rNbLHXwGdcq0qIaSwdmJCvtosgR4JensnVU65hzNQ== + dependencies: + bare-events "^2.2.0" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -9021,6 +9308,11 @@ nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== +nanoassert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09" + integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA== + nanoid@^3.1.23: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" @@ -9031,6 +9323,11 @@ napi-build-utils@^2.0.0: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== +nat-sampler@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nat-sampler/-/nat-sampler-1.0.1.tgz#2b68338ea6d4c139450cd971fd00a4ac1b33d923" + integrity sha512-yQvyNN7xbqR8crTKk3U8gRgpcV1Az+vfCEijiHu9oHHsnIl8n3x+yXNHl42M6L3czGynAVoOT9TqBfS87gDdcw== + native-base@3.4.28: version "3.4.28" resolved "https://registry.yarnpkg.com/native-base/-/native-base-3.4.28.tgz#81b174c8fab1a38fc4560abc12b63dc836c6a14a" @@ -9183,6 +9480,24 @@ node-stream-zip@^1.9.1: resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea" integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== +noise-curve-ed@^2.0.0, noise-curve-ed@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz#38ab0748439baa0f21d49c78d7b8be3d5bb94f42" + integrity sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg== + dependencies: + b4a "^1.1.0" + nanoassert "^2.0.0" + sodium-universal "^5.0.0" + +noise-handshake@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/noise-handshake/-/noise-handshake-4.1.0.tgz#7cc2f3c1ddaf7b06b2db5dc7dbbbe30613e4e85c" + integrity sha512-ZHt2+mOXTvjtaWS2h/JPvQjmknfKrEld2xdSsRYWXnYiJmK/N+dtxrDVSt1cr9wGAlhH7Ek43lIZNsL5bVeX9A== + dependencies: + b4a "^1.1.0" + nanoassert "^2.0.0" + sodium-universal "^5.0.0" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -9464,6 +9779,11 @@ pako@2.1.0: resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== +paparam@^1.5.0: + version "1.8.5" + resolved "https://registry.yarnpkg.com/paparam/-/paparam-1.8.5.tgz#6c7f0b7adb9a9426ca6fad294649ab4506413f3f" + integrity sha512-LLsGJwf3bJuf32jAfQYIMbt7oXuY2TXG3aWVHGXrE8o9cwGYIj+r/4J/VBn5Sgo+mA5lXzNnfqPqfBGPBIvjvQ== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9778,6 +10098,17 @@ prop-types@*, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.0, prop-t object-assign "^4.1.1" react-is "^16.13.1" +protomux@^3.5.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/protomux/-/protomux-3.10.1.tgz#a971f0d5ce090f8a30c86e303e165c3fadaacce5" + integrity sha512-jgBqx8ZyaBWea/DFG4eOu1scOaeBwcnagiRC1XFVrjeGt7oAb0Pk5udPpBUpJ4DJBRjra50jD6YcZiQQTRqaaA== + dependencies: + b4a "^1.3.1" + compact-encoding "^2.5.1" + queue-tick "^1.0.0" + safety-catch "^1.0.1" + unslab "^1.3.0" + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -9803,11 +10134,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.2.4.tgz#54008ac972aec74175def9cba6df7fa9d3918740" - integrity sha512-h/vscxLPvI2l7k/0dFUKZ5I5TgMCJ/Pl+J6rw77PDuQM6UApf/GaRVkjv/YSm2k+fbp7Yw8dxsoe29DolT7h7w== - punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -9854,6 +10180,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue-tick@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + queue@6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" @@ -9953,6 +10284,15 @@ react-native-background-timer@2.4.1: resolved "https://registry.yarnpkg.com/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz#a3bc1cafa8c1e3aeefd0611de120298b67978a0f" integrity sha512-TE4Kiy7jUyv+hugxDxitzu38sW1NqjCk4uE5IgU2WevLv7sZacaBc6PZKOShNRPGirLl1NWkaG3LDEkdb9Um5g== +react-native-bare-kit@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/react-native-bare-kit/-/react-native-bare-kit-0.5.6.tgz#53cf1713bd872d5b93d8e84dee3cb98caa3e0f62" + integrity sha512-iVg3I7gN1iVvlc26RGzaPyTpEZic+PRzJKhIo5XXLj5X2DpxFQTDjBaEG/+x+MJ0c/VPYldxRp8KDVpaDzQHLg== + dependencies: + b4a "^1.6.6" + bare-link "^1.0.1" + streamx "^2.21.0" + react-native-biometrics@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/react-native-biometrics/-/react-native-biometrics-2.2.0.tgz#6fc3e801b83389ae1f6c9c41f7b37b50e7014c4e" @@ -10237,14 +10577,6 @@ react-native-tcp-socket@5.6.2: buffer "^5.4.3" eventemitter3 "^4.0.7" -react-native-tor@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/react-native-tor/-/react-native-tor-0.1.8.tgz#7e888aecaeb69aae5da2348639e761b6ee97c1a2" - integrity sha512-rRArwpqTQoUrQ3WQxc1WLkk1Eg/yjiwU775AxnSt6E7Nfy1V9H6+R9reMD5PJ/39qcuDYv3F7i3MR8Rjy7lOZA== - dependencies: - "@types/async" "^3.2.6" - async "^3.2.0" - react-native@0.74.7: version "0.74.7" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.74.7.tgz#68aef9bbfac7549eabfa3fe68ef4987489131188" @@ -10441,6 +10773,13 @@ recast@^0.21.0: source-map "~0.6.1" tslib "^2.0.1" +record-cache@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.2.0.tgz#e601bc4f164d58330cc00055e27aa4682291c882" + integrity sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw== + dependencies: + b4a "^1.3.1" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -10559,6 +10898,22 @@ regjsparser@^0.12.0: dependencies: jsesc "~3.0.2" +require-addon@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/require-addon/-/require-addon-1.1.0.tgz#0a1ef0ba98b186a3aa304a1abda5208902e63e17" + integrity sha512-KbXAD5q2+v1GJnkzd8zzbOxchTkStSyJZ9QwoCq3QwEXAaIlG3wDYRZGzVD357jmwaGY7hr5VaoEAL0BkF0Kvg== + dependencies: + bare-addon-resolve "^1.3.0" + bare-url "^2.1.0" + +require-asset@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/require-asset/-/require-asset-1.1.0.tgz#cf320497fd0baa8452778100dda42771e7ef25ee" + integrity sha512-X4MXkttYxIEAWr74QsHnGAhUStqCmKPi86OMzh9fnGyPPjduD8qSHEw5N2iApFrs82DBuQ+yesDxajDl/yxT3Q== + dependencies: + bare-module-resolve "^1.6.2" + bare-url "^2.1.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10743,6 +11098,11 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +safety-catch@^1.0.1, safety-catch@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/safety-catch/-/safety-catch-1.0.2.tgz#d64cbd57fd601da91c356b6ab8902f3e449a7a4b" + integrity sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA== + sax@>=0.6.0: version "1.4.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" @@ -10948,6 +11308,13 @@ shell-quote@^1.6.1, shell-quote@^1.7.3: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.2.tgz#d2d83e057959d53ec261311e9e9b8f51dcb2934a" integrity sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA== +shuffled-priority-queue@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/shuffled-priority-queue/-/shuffled-priority-queue-2.1.0.tgz#432bf14dd90f7c4dd1705752d81aadf454fd3af6" + integrity sha512-xhdh7fHyMsr0m/w2kDfRJuBFRS96b9l8ZPNWGaQ+PMvnUnZ/Eh+gJJ9NsHBd7P9k0399WYlCLzsy18EaMfyadA== + dependencies: + unordered-set "^2.0.1" + side-channel-list@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" @@ -10993,6 +11360,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-promise@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/signal-promise/-/signal-promise-1.0.3.tgz#3a6ae4591b7e5b926c06a85a92ceedacbbee858f" + integrity sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -11084,6 +11456,29 @@ socket.io-parser@~4.2.1: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" +sodium-native@^5.0.1: + version "5.0.6" + resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-5.0.6.tgz#f2b47e259d91835998f8d54a723d6a784361edde" + integrity sha512-lKSVfjJ867gb3LrnOZ0nuVxZ19//k/YhhYKVWQg4fklZ85YKtDHTxmEuWuk/s62IoiDbKC/DV50pcSd9tgaVew== + dependencies: + require-addon "^1.1.0" + which-runtime "^1.2.1" + +sodium-secretstream@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz#256c6e7c79907abce73038de187128c985aeed35" + integrity sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q== + dependencies: + b4a "^1.1.1" + sodium-universal "^5.0.0" + +sodium-universal@^5.0.0, sodium-universal@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/sodium-universal/-/sodium-universal-5.0.1.tgz#b06c0a52256f19d2bf071ea031476f4306831dfd" + integrity sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q== + dependencies: + sodium-native "^5.0.1" + source-map-js@^1.0.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -11189,6 +11584,16 @@ streamifier@^0.1.1: resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" integrity sha512-zDgl+muIlWzXNsXeyUfOk9dChMjlpkq0DRsxujtYPgyJ676yQ8jEm6zzaaWHFDg5BNcLuif0eD2MTyJdZqXpdg== +streamx@^2.13.2, streamx@^2.14.0, streamx@^2.15.1, streamx@^2.16.1, streamx@^2.21.0, streamx@^2.22.0: + version "2.22.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.1.tgz#c97cbb0ce18da4f4db5a971dc9ab68ff5dc7f5a5" + integrity sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA== + dependencies: + fast-fifo "^1.3.2" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -11507,6 +11912,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" + text-encoding@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.7.0.tgz#f895e836e45990624086601798ea98e8f36ee643" @@ -11535,6 +11947,16 @@ through2@^2.0.1: readable-stream "~2.3.6" xtend "~4.0.1" +time-ordered-set@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/time-ordered-set/-/time-ordered-set-2.0.1.tgz#66327419aa983a6e8c8f9e1b6832f721462c95b7" + integrity sha512-VJEKmgSN2UiOLB8BpN8Sh2b9LGMHTP5OPrQRpnKjvOheOyzk0mufbjzjKTIG2gO4A+Y+vDJ+0TcLbpUmMLsg8A== + +timeout-refresh@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/timeout-refresh/-/timeout-refresh-2.0.1.tgz#f8ec7cf1f9d93b2635b7d4388cb820c5f6c16f98" + integrity sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw== + tinycolor2@^1.4.2: version "1.6.0" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" @@ -11744,6 +12166,16 @@ typescript@5.0.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== +udx-native@^1.5.3: + version "1.18.2" + resolved "https://registry.yarnpkg.com/udx-native/-/udx-native-1.18.2.tgz#759267ce9f89be0d4b1b818f5dd137c894220ca0" + integrity sha512-cOPNygeROwr07xl85oiRQHiHtvRkRVUHFxMJ5q3r5WCPrNMI64ss1bmiS1KelT3bAIrAmkOw263kuQqU78a7pQ== + dependencies: + b4a "^1.5.0" + bare-events "^2.2.0" + require-addon "^1.1.0" + streamx "^2.22.0" + unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" @@ -11797,11 +12229,23 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unordered-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unordered-set/-/unordered-set-2.0.1.tgz#4cd0fe27b8814bcf5d6073e5f0966ec7a50841e6" + integrity sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg== + unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +unslab@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/unslab/-/unslab-1.3.0.tgz#6b5c21c873f2adc34ac10475438344f51f681988" + integrity sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ== + dependencies: + b4a "^1.6.6" + update-browserslist-db@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" @@ -11817,13 +12261,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url@0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.1.tgz#d8eba8f267cec7645ddd93d2cdcf2320c876d25b" - integrity sha512-ADxffdyUpVmS7pcoveHZ5vm+vvVUJRrSx1hAN/9H/ZEaQ7LDMs3tqOYIWDlxEa2JHD6c3ghbLZ934FdbnW6lpQ== - dependencies: - punycode "1.2.4" - use-latest-callback@^0.2.1: version "0.2.3" resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.3.tgz#2d644d3063040b9bc2d4c55bb525a13ae3de9e16" @@ -11860,6 +12297,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== + uuid@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" @@ -11978,6 +12420,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-runtime@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which-runtime/-/which-runtime-1.3.0.tgz#d6623cc60b1ab7b423a31acfee594cf5947b68f4" + integrity sha512-mQrEBUe15PdEuJvrBlvy3tika6sxQrkblI7JQ9kXeg8Lcby9FwmKvAYrCT3wLh91k6ltost7AVM7qYhjC8N0Zg== + which-typed-array@^1.1.16, which-typed-array@^1.1.18, which-typed-array@^1.1.2: version "1.1.19" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" @@ -12088,6 +12535,11 @@ ws@~8.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== +xache@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/xache/-/xache-1.2.1.tgz#0c2041ba13d8c60d2e10214a0ce99894315f9349" + integrity sha512-igRS6jPreJ54ABdzhh4mCDXcz+XMaWO2q1ABRV2yWYuk29jlp8VT7UBdCqNkX7rpYBbXsebVVKkwIuYZjyZNqA== + xcode@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" @@ -12221,3 +12673,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +z32@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/z32/-/z32-1.1.0.tgz#f308de8e0cb877671368de47b9cb4c9d5a6f507e" + integrity sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ== + dependencies: + b4a "^1.5.3"