From 716a6b5ad007c54ffe75a2ef2040c42f03dd2732 Mon Sep 17 00:00:00 2001 From: Parsh Date: Sun, 13 Jul 2025 11:46:12 +0200 Subject: [PATCH 01/80] Init: HolePunch P2P --- ios/Podfile.lock | 193 +++++---- ios/hexa_keeper.xcodeproj/project.pbxproj | 138 +++--- package.json | 5 + src/services/p2p/rpc-commands.mjs | 10 + src/services/p2p/worklet.mjs | 136 ++++++ src/utils/service-utilities/config.ts | 2 + yarn.lock | 503 +++++++++++++++++++++- 7 files changed, 813 insertions(+), 174 deletions(-) create mode 100644 src/services/p2p/rpc-commands.mjs create mode 100644 src/services/p2p/worklet.mjs diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 10dd61a2d..55e39506c 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+17f072)": + - 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 @@ -1533,6 +1552,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`) @@ -1552,7 +1572,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`) @@ -1688,6 +1707,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: @@ -1726,8 +1747,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: @@ -1816,7 +1835,7 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - BVLinearGradient: cb006ba232a1f3e4f341bb62c42d1098c284da70 + BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 FBLazyVector: 04dc972982abebd96d823752c3a426bbe6ac397f @@ -1831,105 +1850,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: 9f4bb916525897c56308fe5c30d289feca247bf7 + 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: 65c89d27fa3c71ecae1f89a4c5f428ed9315d9b5 - 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: 5a48ea3632b315e4db75ee713613214c4cb9c220 + 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 31f1c9d73..68c856208 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=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; @@ -867,7 +867,7 @@ }; 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; @@ -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; @@ -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; diff --git a/package.json b/package.json index 11dca3d1a..87e0216a1 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", @@ -41,6 +42,8 @@ "@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 +62,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 +78,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", 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..d4132d107 --- /dev/null +++ b/src/services/p2p/worklet.mjs @@ -0,0 +1,136 @@ +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'; +import config from 'src/utils/service-utilities/config'; +global.Buffer = Buffer; + +const { IPC } = BareKit; + +let keyPair; +const connections = new Map(); + +const RELAY_PUB_KEY = config.RELAY_PEER_PUB_KEY; + +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/utils/service-utilities/config.ts b/src/utils/service-utilities/config.ts index 1aba4ba1c..5a5a46303 100644 --- a/src/utils/service-utilities/config.ts +++ b/src/utils/service-utilities/config.ts @@ -109,6 +109,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; constructor() { diff --git a/yarn.lock b/yarn.lock index 83643dea1..52835e3eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1462,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" @@ -3558,11 +3573,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" @@ -4249,11 +4259,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" @@ -4294,6 +4299,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" @@ -4429,6 +4439,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" @@ -4575,6 +4730,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" @@ -4592,6 +4754,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" @@ -4602,6 +4778,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" @@ -5178,6 +5362,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" @@ -5627,6 +5832,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" @@ -6452,6 +6674,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" @@ -7098,6 +7325,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" @@ -8114,6 +8395,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" @@ -9007,6 +9295,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" @@ -9017,6 +9310,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" @@ -9169,6 +9467,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" @@ -9450,6 +9766,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" @@ -9764,6 +10085,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" @@ -9840,6 +10172,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" @@ -9939,6 +10276,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" @@ -10223,14 +10569,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" @@ -10427,6 +10765,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" @@ -10545,6 +10890,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" @@ -10729,6 +11090,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" @@ -10934,6 +11300,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" @@ -10979,6 +11352,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" @@ -11070,6 +11448,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" @@ -11175,6 +11576,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" @@ -11493,6 +11904,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" @@ -11521,6 +11939,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" @@ -11730,6 +12158,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" @@ -11783,11 +12221,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" @@ -11964,6 +12414,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" @@ -12074,6 +12529,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" @@ -12207,3 +12667,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" From 599cbb88cf900f6a0254e8210612f6b33b2cd512 Mon Sep 17 00:00:00 2001 From: Parsh Date: Sun, 13 Jul 2025 12:02:24 +0200 Subject: [PATCH 02/80] fixes: bundle gen --- src/services/p2p/app.bundle.mjs | 1 + src/services/p2p/config.mjs | 15 +++++++++++++++ src/services/p2p/worklet.mjs | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/services/p2p/app.bundle.mjs create mode 100644 src/services/p2p/config.mjs diff --git a/src/services/p2p/app.bundle.mjs b/src/services/p2p/app.bundle.mjs new file mode 100644 index 000000000..6992e70dd --- /dev/null +++ b/src/services/p2p/app.bundle.mjs @@ -0,0 +1 @@ +export default "48661\n{\"version\":0,\"id\":\"f3e2489ed193e602b883fde45f9cdbf94ed6ec53c12c7edfdf5e3bcc52b98a88\",\"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/config.mjs\":{\"#package\":\"/package.json\"},\"/src/services/p2p/rpc-commands.mjs\":{\"#package\":\"/package.json\"},\"/src/services/p2p/worklet.mjs\":{\"#package\":\"/package.json\",\"./config.mjs\":\"/src/services/p2p/config.mjs\",\"./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/config.mjs\":{\"offset\":758500,\"length\":452,\"mode\":420},\"/src/services/p2p/rpc-commands.mjs\":{\"offset\":758952,\"length\":291,\"mode\":420},\"/src/services/p2p/worklet.mjs\":{\"offset\":759243,\"length\":3263,\"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.0\",\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.28.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}\n// For bare runtime, we'll use environment variables or defaults\nconst getConfig = (key) => {\n // Try to get from Bare.env if available, otherwise use defaults\n if (typeof Bare !== 'undefined' && Bare.env && Bare.env[key]) {\n return Bare.env[key].trim();\n }\n};\n\n// P2P specific config\nexport const RELAY_PEER_PUB_KEY = getConfig('RELAY_PEER_PUB_KEY') || '';\n\n// Export a default object for compatibility\nexport default {\n RELAY_PEER_PUB_KEY,\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';\nimport * as config from './config.mjs';\nglobal.Buffer = Buffer;\n\nconst { IPC } = BareKit;\n\nlet keyPair;\nconst connections = new Map();\n\nconst RELAY_PUB_KEY = config.RELAY_PEER_PUB_KEY;\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/config.mjs b/src/services/p2p/config.mjs new file mode 100644 index 000000000..f4fea7989 --- /dev/null +++ b/src/services/p2p/config.mjs @@ -0,0 +1,15 @@ +// For bare runtime, we'll use environment variables or defaults +const getConfig = (key) => { + // Try to get from Bare.env if available, otherwise use defaults + if (typeof Bare !== 'undefined' && Bare.env && Bare.env[key]) { + return Bare.env[key].trim(); + } +}; + +// P2P specific config +export const RELAY_PEER_PUB_KEY = getConfig('RELAY_PEER_PUB_KEY') || ''; + +// Export a default object for compatibility +export default { + RELAY_PEER_PUB_KEY, +}; diff --git a/src/services/p2p/worklet.mjs b/src/services/p2p/worklet.mjs index d4132d107..71b024234 100644 --- a/src/services/p2p/worklet.mjs +++ b/src/services/p2p/worklet.mjs @@ -14,7 +14,7 @@ import RPC from 'bare-rpc'; import b4a from 'b4a'; import { Buffer } from 'buffer'; -import config from 'src/utils/service-utilities/config'; +import * as config from './config.mjs'; global.Buffer = Buffer; const { IPC } = BareKit; From 08a3d7cd530e7ba8e1cbd576664b403485137a61 Mon Sep 17 00:00:00 2001 From: Parsh Date: Sun, 13 Jul 2025 15:06:17 +0200 Subject: [PATCH 03/80] adds: interfaces and schemas (contacts and communities) --- src/models/interfaces/KeeperApp.ts | 1 + src/services/p2p/interface.ts | 51 +++++++++++++++++++++++++++ src/storage/realm/enum.ts | 3 ++ src/storage/realm/realm.ts | 2 +- src/storage/realm/schema/app.ts | 1 + src/storage/realm/schema/community.ts | 41 +++++++++++++++++++++ src/storage/realm/schema/index.ts | 4 +++ 7 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/services/p2p/interface.ts create mode 100644 src/storage/realm/schema/community.ts diff --git a/src/models/interfaces/KeeperApp.ts b/src/models/interfaces/KeeperApp.ts index 73dcc1165..5d4a91f78 100644 --- a/src/models/interfaces/KeeperApp.ts +++ b/src/models/interfaces/KeeperApp.ts @@ -20,4 +20,5 @@ export interface KeeperApp { backup: AppBackup; subscription: SubScription; enableAnalytics: boolean; + contactsKey?: { [key: string]: string }; } diff --git a/src/services/p2p/interface.ts b/src/services/p2p/interface.ts new file mode 100644 index 000000000..8b81fc2e4 --- /dev/null +++ b/src/services/p2p/interface.ts @@ -0,0 +1,51 @@ +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; + // messages: Message[]; + // members: 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/storage/realm/enum.ts b/src/storage/realm/enum.ts index 95c744db5..f12fed745 100644 --- a/src/storage/realm/enum.ts +++ b/src/storage/realm/enum.ts @@ -44,4 +44,7 @@ export enum RealmSchema { USDTWalletPresentationData = 'USDTWalletPresentationData', USDTWalletDerivationDetails = 'USDTWalletDerivationDetails', USDTWalletAccountStatus = 'USDTWalletAccountStatus', + Community = 'Community', + Message = 'Message', + Contact = 'Contact', } diff --git a/src/storage/realm/realm.ts b/src/storage/realm/realm.ts index d1e0e9126..e9d9db1d2 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 = 107; /** * initializes/opens realm w/ appropriate configuration diff --git a/src/storage/realm/schema/app.ts b/src/storage/realm/schema/app.ts index cb3e9e27f..ff634e395 100644 --- a/src/storage/realm/schema/app.ts +++ b/src/storage/realm/schema/app.ts @@ -15,6 +15,7 @@ export const KeeperAppSchema: ObjectSchema = { subscription: RealmSchema.StoreSubscription, backup: RealmSchema.Backup, enableAnalytics: { type: 'bool', default: false }, + contactsKey: '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..77afb7ef4 --- /dev/null +++ b/src/storage/realm/schema/community.ts @@ -0,0 +1,41 @@ +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?', + }, +}; diff --git a/src/storage/realm/schema/index.ts b/src/storage/realm/schema/index.ts index 7e9737d32..8eb61b8c2 100644 --- a/src/storage/realm/schema/index.ts +++ b/src/storage/realm/schema/index.ts @@ -50,6 +50,7 @@ import { USDTWalletDerivationDetailsSchema, USDTWalletAccountStatusSchema, } from './usdtWallet'; +import { CommunitySchema, ContactSchema, MessageSchema } from './community'; export default [ KeeperAppSchema, @@ -100,4 +101,7 @@ export default [ USDTWalletDerivationDetailsSchema, USDTWalletPresentationDataSchema, USDTWalletAccountStatusSchema, + MessageSchema, + ContactSchema, + CommunitySchema, ]; From 5a56e48b95d93955878712653ce5ed5e4d4a26d1 Mon Sep 17 00:00:00 2001 From: Parsh Date: Sun, 13 Jul 2025 16:13:03 +0200 Subject: [PATCH 04/80] adds: peer manager --- src/services/p2p/ChatPeerManager.ts | 231 ++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/services/p2p/ChatPeerManager.ts diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts new file mode 100644 index 000000000..91717615d --- /dev/null +++ b/src/services/p2p/ChatPeerManager.ts @@ -0,0 +1,231 @@ +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, Message } from './interface'; +import { RealmSchema } from 'src/storage/realm/enum'; +import dbManager from 'src/storage/realm/dbManager'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; + +export default class ChatPeerManager { + static instance: ChatPeerManager; + + 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; + } + + async init(seed: string): Promise { + await this.worklet.start('/app.bundle', bundle, [seed]); + + this.rpc = new RPC(this.IPC, async (req) => { + const data = b4a.toString(req.data); + // console.log(`${Platform.OS} Received:`, req.command, 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) { + if (this.onConnectionCallback) { + this.onConnectionCallback(JSON.parse(data)); + } + } + }); + return true; + } + + async getKeys() { + 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() { + 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) { + 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) { + 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://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 + 1); + if (response.messages.length > 0) { + const communities = dbManager.getCollection(RealmSchema.Community); + for (const msg of response.messages) { + const message: Message = JSON.parse(msg.message); + const communityId = [this.app.contactsKey.publicKey, message.sender].sort().join('-'); + if (!communities.find((c) => c.id === communityId)) { + // const contact = await Relay.getWalletProfiles([message.sender]); + // if (contact.results.length > 0) { + // dbManager.createObject(RealmSchema.Contact, contact.results[0]); + // dbManager.createObject(RealmSchema.Community, { + // id: communityId, + // communityId: communityId, + // name: contact.results[0].name, + // type: CommunityType.Peer, + // createdAt: msg.timestamp, + // updatedAt: msg.timestamp, + // with: message.sender, + // }); + // } + + dbManager.createObject(RealmSchema.Community, { + id: communityId, + communityId: communityId, + // name: contact.results[0].name, + type: CommunityType.Peer, + createdAt: msg.timestamp, + updatedAt: msg.timestamp, + with: message.sender, + }); + } + dbManager.createObject(RealmSchema.Message, { + id: message.id, + communityId: communityId, + type: message.type, + text: message.text, + createdAt: msg.timestamp, + sender: message.sender, + block: msg.blockNumber, + unread: true, + fileUrl: message?.fileUrl, + request: (message as any)?.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); + const community = communities.find((c) => c.id === message.communityId); + if (!community) { + // const contact = await Relay.getWalletProfiles([message.sender]); + // if (contact.results.length > 0) { + // dbManager.createObject(RealmSchema.Contact, contact.results[0]); + // dbManager.createObject(RealmSchema.Community, { + // id: message.communityId, + // name: contact.results[0].name, + // type: CommunityType.Peer, + // createdAt: data.timestamp, + // updatedAt: data.timestamp, + // with: message.sender, + // }); + // } + + dbManager.createObject(RealmSchema.Community, { + id: message.communityId, + // name: contact.results[0].name, + type: CommunityType.Peer, + createdAt: data.timestamp, + updatedAt: data.timestamp, + with: message.sender, + }); + } + + 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, + request: message?.request, + }); + } catch (error) { + console.error('Error storing messages:', error); + } + }; + + // async syncContacts() { + // try { + // const contacts = dbManager.getCollection(RealmSchema.Contact); + // const response = await Relay.getWalletProfiles(contacts.map((c) => c.contactKey)); + // if (response.results.length > 0) { + // dbManager.createObjectBulk( + // RealmSchema.Contact, + // response.results, + // Realm.UpdateMode.Modified + // ); + // } + // } catch (error) { + // console.error('Error syncing contacts:', error); + // } + // } +} From 1f63c30fa01f6a9e860e89967d099661170a2737 Mon Sep 17 00:00:00 2001 From: Parsh Date: Tue, 15 Jul 2025 10:49:29 +0200 Subject: [PATCH 05/80] update: bare-kit lock --- ios/Podfile.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d5668203a..422df68bd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1026,7 +1026,7 @@ PODS: - React-debug - react-native-background-timer (2.4.1): - React-Core - - "react-native-bare-kit (0.5.6+17f072)": + - "react-native-bare-kit (0.5.6+dea2b7)": - DoubleConversion - glog - hermes-engine @@ -1885,7 +1885,7 @@ SPEC CHECKSUMS: React-logger: 87cddd161bd9784d60fc5528f21c57ca07d2962a React-Mapbuffer: b6e208217a18044f0f1a183be8f6756ad67b42d7 react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe - react-native-bare-kit: 9f4bb916525897c56308fe5c30d289feca247bf7 + react-native-bare-kit: e5778d30c237c784b138ffc119620e8efbe36f25 react-native-biometrics: 3b95f2eb074d537d3a8b05d853c17778f6685c4a react-native-blob-util: 2d36383bb52c15c5451be81cb7ddf22bc34a12a6 react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f @@ -1924,26 +1924,26 @@ SPEC CHECKSUMS: 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 From 0749abbab2734cd772abb599780579edb91a905d Mon Sep 17 00:00:00 2001 From: Parsh Date: Wed, 16 Jul 2025 11:11:25 +0200 Subject: [PATCH 06/80] adds: wallet profile mock --- src/services/p2p/ChatPeerManager.ts | 122 +++++++++++++++++++--------- 1 file changed, 82 insertions(+), 40 deletions(-) diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts index 91717615d..3a443b9ea 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -12,7 +12,7 @@ import { RPC_KEY, SEND_MESSAGE, } from './rpc-commands.mjs'; -import { CommunityType, Message } from './interface'; +import { CommunityType, Contact, Message } from './interface'; import { RealmSchema } from 'src/storage/realm/enum'; import dbManager from 'src/storage/realm/dbManager'; import { KeeperApp } from 'src/models/interfaces/KeeperApp'; @@ -45,7 +45,6 @@ export default class ChatPeerManager { this.rpc = new RPC(this.IPC, async (req) => { const data = b4a.toString(req.data); - // console.log(`${Platform.OS} Received:`, req.command, data); if (req.command === RPC_KEY) { } else if (req.command === ON_MESSAGE) { @@ -124,18 +123,20 @@ export default class ChatPeerManager { const communityId = [this.app.contactsKey.publicKey, message.sender].sort().join('-'); if (!communities.find((c) => c.id === communityId)) { // const contact = await Relay.getWalletProfiles([message.sender]); - // if (contact.results.length > 0) { - // dbManager.createObject(RealmSchema.Contact, contact.results[0]); - // dbManager.createObject(RealmSchema.Community, { - // id: communityId, - // communityId: communityId, - // name: contact.results[0].name, - // type: CommunityType.Peer, - // createdAt: msg.timestamp, - // updatedAt: msg.timestamp, - // with: message.sender, - // }); - // } + const contact = await this.mockGetWalletProfiles([message.sender]); + + if (contact.results.length > 0) { + dbManager.createObject(RealmSchema.Contact, contact.results[0]); + dbManager.createObject(RealmSchema.Community, { + id: communityId, + communityId: communityId, + name: contact.results[0].name, + type: CommunityType.Peer, + createdAt: msg.timestamp, + updatedAt: msg.timestamp, + with: message.sender, + }); + } dbManager.createObject(RealmSchema.Community, { id: communityId, @@ -174,17 +175,19 @@ export default class ChatPeerManager { const community = communities.find((c) => c.id === message.communityId); if (!community) { // const contact = await Relay.getWalletProfiles([message.sender]); - // if (contact.results.length > 0) { - // dbManager.createObject(RealmSchema.Contact, contact.results[0]); - // dbManager.createObject(RealmSchema.Community, { - // id: message.communityId, - // name: contact.results[0].name, - // type: CommunityType.Peer, - // createdAt: data.timestamp, - // updatedAt: data.timestamp, - // with: message.sender, - // }); - // } + const contact = await this.mockGetWalletProfiles([message.sender]); + + if (contact.results.length > 0) { + dbManager.createObject(RealmSchema.Contact, contact.results[0]); + dbManager.createObject(RealmSchema.Community, { + id: message.communityId, + name: contact.results[0].name, + type: CommunityType.Peer, + createdAt: data.timestamp, + updatedAt: data.timestamp, + with: message.sender, + }); + } dbManager.createObject(RealmSchema.Community, { id: message.communityId, @@ -213,19 +216,58 @@ export default class ChatPeerManager { } }; - // async syncContacts() { - // try { - // const contacts = dbManager.getCollection(RealmSchema.Contact); - // const response = await Relay.getWalletProfiles(contacts.map((c) => c.contactKey)); - // if (response.results.length > 0) { - // dbManager.createObjectBulk( - // RealmSchema.Contact, - // response.results, - // Realm.UpdateMode.Modified - // ); - // } - // } catch (error) { - // console.error('Error syncing contacts:', error); - // } - // } + // Mock method for testing - returns random contact objects + mockGetWalletProfiles(contactKeys: string[]) { + const mockNames = [ + 'Alice Johnson', + 'Bob Smith', + 'Charlie Brown', + 'Diana Prince', + 'Eve Davis', + 'Frank Miller', + 'Grace Lee', + 'Henry Wilson', + 'Ivy Chen', + 'Jack Turner', + ]; + + const mockImageUrls = [ + 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alice', + 'https://api.dicebear.com/7.x/avataaars/svg?seed=Bob', + 'https://api.dicebear.com/7.x/avataaars/svg?seed=Charlie', + 'https://api.dicebear.com/7.x/avataaars/svg?seed=Diana', + 'https://api.dicebear.com/7.x/avataaars/svg?seed=Eve', + ]; + + const results = contactKeys.map((contactKey) => ({ + appID: `app_${Math.random().toString(36).substr(2, 9)}`, + contactKey, + name: mockNames[Math.floor(Math.random() * mockNames.length)], + imageUrl: mockImageUrls[Math.floor(Math.random() * mockImageUrls.length)], + })); + + return { + results, + success: true, + message: 'Mock wallet profiles retrieved successfully', + }; + } + + async syncContacts() { + try { + const contacts: Contact[] = dbManager.getCollection(RealmSchema.Contact) as any; + // const response = await Relay.getWalletProfiles(contacts.map((c) => c.contactKey)); + const response = await this.mockGetWalletProfiles(contacts.map((c) => c.contactKey)); + + if (response.results.length > 0) { + dbManager.createObjectBulk( + RealmSchema.Contact, + response.results, + Realm.UpdateMode.Modified + ); + } + } catch (error) { + console.error('Error syncing contacts:', error); + } + } } From 46fef7e22fd89ddbf7472e459879f08ae8326fda Mon Sep 17 00:00:00 2001 From: Parsh Date: Wed, 16 Jul 2025 13:48:54 +0200 Subject: [PATCH 07/80] fixes: redundant community creation --- src/services/p2p/ChatPeerManager.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts index 3a443b9ea..377598697 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -137,16 +137,6 @@ export default class ChatPeerManager { with: message.sender, }); } - - dbManager.createObject(RealmSchema.Community, { - id: communityId, - communityId: communityId, - // name: contact.results[0].name, - type: CommunityType.Peer, - createdAt: msg.timestamp, - updatedAt: msg.timestamp, - with: message.sender, - }); } dbManager.createObject(RealmSchema.Message, { id: message.id, @@ -188,15 +178,6 @@ export default class ChatPeerManager { with: message.sender, }); } - - dbManager.createObject(RealmSchema.Community, { - id: message.communityId, - // name: contact.results[0].name, - type: CommunityType.Peer, - createdAt: data.timestamp, - updatedAt: data.timestamp, - with: message.sender, - }); } dbManager.createObject(RealmSchema.Message, { From c830ef0744770076ed39749dff3b2f58addd4728 Mon Sep 17 00:00:00 2001 From: Parsh Date: Wed, 16 Jul 2025 13:55:42 +0200 Subject: [PATCH 08/80] init: peer chat manager upon login --- src/store/sagas/login.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/store/sagas/login.ts b/src/store/sagas/login.ts index dfd5a901e..1d0e683be 100644 --- a/src/store/sagas/login.ts +++ b/src/store/sagas/login.ts @@ -69,6 +69,8 @@ import { } from '../reducers/account'; import { REALM_FILE } from 'src/storage/realm/realm'; import { loadConciergeUserOnLogin, saveBackupMethodByAppId } from '../sagaActions/account'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; +import idx from 'idx'; export const stringToArrayBuffer = (byteString: string): Uint8Array => { if (byteString) { @@ -189,7 +191,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 @@ -197,6 +199,15 @@ function* credentialsAuthWorker({ payload }) { yield put(setTempDetails({ hash, realmId: REALM_FILE, accountIdentifier: '' })); } + const contactsSecretKey = idx(keeperApp, (app) => app.contactsKey.secretKey); + if (contactsSecretKey) { + // initiate the contacts manager if the contacts feature is active + const cm = ChatPeerManager.getInstance(); + cm.init(keeperApp.primarySeed).then((success) => + console.log(`Initialized contacts manager: ${success}`) + ); + } + const newVersion = DeviceInfo.getVersion(); const versionCollection = yield call(dbManager.getCollection, RealmSchema.VersionHistory); const lastElement = versionCollection[versionCollection.length - 1]; From a6954e868c420512c45bbfb1fc9376d46b96e495 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 16 Jul 2025 17:26:16 +0500 Subject: [PATCH 09/80] initial setup --- src/assets/images/Contact-footer-dark.svg | 3 +++ src/assets/images/Contact-footer-light.svg | 3 +++ src/assets/images/contact-footer-default.svg | 3 +++ src/assets/images/contact-header-dark.svg | 8 ++++++++ src/assets/images/contact-header-light.svg | 8 ++++++++ src/components/MenuFooter.tsx | 10 +++++----- src/components/ThemedSvg.tsx/ThemedIcons.js | 16 ++++++++++++++++ src/context/Localization/language/en.json | 3 +++ src/context/Localization/language/es.json | 3 +++ src/screens/Home/HomeScreen.tsx | 15 ++++++++++----- .../components/Contact/component/Contact.tsx | 13 +++++++++++++ 11 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 src/assets/images/Contact-footer-dark.svg create mode 100644 src/assets/images/Contact-footer-light.svg create mode 100644 src/assets/images/contact-footer-default.svg create mode 100644 src/assets/images/contact-header-dark.svg create mode 100644 src/assets/images/contact-header-light.svg create mode 100644 src/screens/Home/components/Contact/component/Contact.tsx 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/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/components/MenuFooter.tsx b/src/components/MenuFooter.tsx index 9eec7f817..8f096c8a4 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,12 +11,13 @@ 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'; 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 menuOptions = [ @@ -37,9 +37,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 4f2804c01..8d4744124 100644 --- a/src/components/ThemedSvg.tsx/ThemedIcons.js +++ b/src/components/ThemedSvg.tsx/ThemedIcons.js @@ -316,8 +316,24 @@ 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'; const themeIcons = { + contact_footer: { + DARK: ContactFooterDark, + LIGHT: ContactFooterLight, + PRIVATE: ContactFooterDark, + PRIVATE_LIGHT: ContactFooterLight, + }, + contact_header: { + DARK: ContactHeaderDark, + LIGHT: ContactHeaderLight, + PRIVATE: ContactHeaderDark, + PRIVATE_LIGHT: ContactHeaderLight, + }, acquire_send_arrow: { DARK: BuyAcquireArrowWhite, LIGHT: BuyAcquireArrow, diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index ab0e42500..c20f1d819 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1979,5 +1979,8 @@ "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" } } diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index ab0e42500..c20f1d819 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1979,5 +1979,8 @@ "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" } } diff --git a/src/screens/Home/HomeScreen.tsx b/src/screens/Home/HomeScreen.tsx index 7884c7a7d..ebaa6f18b 100644 --- a/src/screens/Home/HomeScreen.tsx +++ b/src/screens/Home/HomeScreen.tsx @@ -18,11 +18,11 @@ import HomeWallet from './components/Wallet/HomeWallet'; import ManageKeys from './components/Keys/ManageKeys'; import KeeperSettings from './components/Settings/keeperSettings'; import { useNavigation } from '@react-navigation/native'; -import TechnicalSupport from '../KeeperConcierge/TechnicalSupport'; 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 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={ { + return ( + + contact + + ); +}; + +export default Contact; From 8623eb5f18c38750d0c7a3c80b591055a2f69255 Mon Sep 17 00:00:00 2001 From: Parsh Date: Wed, 16 Jul 2025 18:00:15 +0200 Subject: [PATCH 10/80] adds: chatPeer hook --- src/hooks/useChatPeer.ts | 376 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 src/hooks/useChatPeer.ts diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts new file mode 100644 index 000000000..de58bc045 --- /dev/null +++ b/src/hooks/useChatPeer.ts @@ -0,0 +1,376 @@ +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 idx from 'idx'; + +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; + getPeerMessages: (pubKey: string, lastBlock: number) => Promise; + + // Contact management + syncContacts: () => 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); + 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); + }); + } + + const secretKey = idx(keeperApp, (app) => app.contactsKey.secretKey); + // Get keys from ChatPeerManager and update KeeperApp if contactsKey is missing + if (success && !secretKey) { + try { + const keys = await chatManager.getKeys(); + if (keys && keys.publicKey && keys.secretKey) { + // Update the KeeperApp with the contacts key in Realm database + await dbManager.updateObjectById(RealmSchema.KeeperApp, keeperApp.id, { + contactsKey: { + publicKey: keys.publicKey, + secretKey: keys.secretKey, + }, + }); + console.log('KeeperApp updated with contacts key'); + } + } catch (keyError) { + console.warn('Failed to get or update contacts key:', keyError); + captureError(keyError); + return false; + } + } + + return success; + } catch (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); + return await chatManager.sendMessage(pubKey, message); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to send message'); + captureError(err); + throw err; + } + }, []); + + // Join peer + const joinPeer = useCallback(async (pubKey: string): Promise => { + try { + setError(null); + return await chatManager.joinPeers(pubKey); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to join peer'); + captureError(err); + throw err; + } + }, []); + + // Load pending messages + 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); + } + }, []); + + // Get peers + const getPeers = useCallback(async (): Promise => { + try { + setError(null); + 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; + } + }, []); + + // 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; + } + }, []); + + // Sync contacts + const syncContacts = useCallback(async (): Promise => { + try { + setError(null); + await chatManager.syncContacts(); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to sync contacts'); + captureError(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, + getPeerMessages, + + // Contact management + syncContacts, + + // Message operations (lazy loaded) + getMessagesByCommunity, + getAllMessages, + getUnreadMessages, + getUnreadCount, + markMessageAsRead, + markCommunityAsRead, + + // Community helpers + getPeerCommunities, + getCommunityById, + + // Contact helpers + getContactByKey, + + // Listeners + setMessageListener, + setConnectionListener, + }; +}; From 78352defc521f8dc3087c16381a37daa1ef0bd45 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 17 Jul 2025 12:22:34 +0500 Subject: [PATCH 11/80] hme screen --- src/assets/images/Pin.svg | 3 + src/assets/images/contact-add-icon.svg | 5 + src/assets/images/contact-edit.svg | 3 + .../images/contact-placeholder-image.png | Bin 0 -> 1181 bytes src/assets/images/share-green.svg | 3 + src/assets/images/share-white.svg | 3 + src/components/ThemedSvg.tsx/ThemedIcons.js | 8 + .../components/Contact/component/ChatList.tsx | 184 ++++++++++++++++++ .../components/Contact/component/Contact.tsx | 97 ++++++++- .../Contact/component/ContactHeader.tsx | 112 +++++++++++ 10 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 src/assets/images/Pin.svg create mode 100644 src/assets/images/contact-add-icon.svg create mode 100644 src/assets/images/contact-edit.svg create mode 100644 src/assets/images/contact-placeholder-image.png create mode 100644 src/assets/images/share-green.svg create mode 100644 src/assets/images/share-white.svg create mode 100644 src/screens/Home/components/Contact/component/ChatList.tsx create mode 100644 src/screens/Home/components/Contact/component/ContactHeader.tsx 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/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-placeholder-image.png b/src/assets/images/contact-placeholder-image.png new file mode 100644 index 0000000000000000000000000000000000000000..c95d2e8c5faa21058773ef661d663da817a6a74a GIT binary patch literal 1181 zcmV;O1Y-M%P)R>R)VadtOqRC_uuoTed6WUB!t=1`EfXRq4k|F|KNTypwMlS>;J%!}}k`?rJ z(nU}v)+ZB78c1{ubP`QSaA;_#1B($bu2>=n_=5*}dwa*?8O0@t$_=MVHBAC0(+DbQ z)ohPdDZ6wW$u+?dK%Sdz$Wx4LGi&sDaa808@>t`s+M)=`Zf%7l29cz(BkND7Uj_#U zJF$2U$A|ismKN{C#KfFZaX1H&JdeL(5!QdJNX~M-?%L*dMDnGoR`v{KJHO=<#o-J|KHlIu;F6|^lkm*Y(#!XJofXA~9#e5OuW`|bm z&fXq;Ui?ZbCv{AqlVKV@?8&a=6;yF0j*_fEUh5R7r{O;Z611mbpe6;uD9#q-L0wI? zRJztTU=!WEi>{@ZmzkLfXL7SK!#R(2`(T^51It381HY7R;n8v*bk0Lctr~9LfU-*^ zU<5P;W@c^DywwAmf0%9+E7{Dbo=%h?w6{JoOpZ`iS_18D52f$Wi79$W1X0QnJv|;j zxObS8vVi*DzuT;L>l=``(L>Jv)FKS#cSvE3fcIiTZyBgbxdLSX^?b zuZjtyv6RzAkbtMYP6*wu^6L6?h52IX#S)G(nB1JxQT>8JlZaa%=h@x{m*9@t>QFTi z^8mWQj2naXUIEk1LqC4((6Wi#A$-1HpgCyh3fLO!K9Gr^0);3J&}cM8lNB4qU281m vm*}w|{|(V3A6q$PK`CS)6hiFD{tfXL!X&Z(TptC-00000NkvXXu0mjfUMwKC literal 0 HcmV?d00001 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/components/ThemedSvg.tsx/ThemedIcons.js b/src/components/ThemedSvg.tsx/ThemedIcons.js index 8d4744124..ebaf12c4b 100644 --- a/src/components/ThemedSvg.tsx/ThemedIcons.js +++ b/src/components/ThemedSvg.tsx/ThemedIcons.js @@ -320,8 +320,16 @@ 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'; const themeIcons = { + share_icons: { + DARK: ShareDark, + LIGHT: ShareLight, + PRIVATE: ShareDark, + PRIVATE_LIGHT: ShareLight, + }, contact_footer: { DARK: ContactFooterDark, LIGHT: ContactFooterLight, 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..32d1b1019 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import { FlatList, StyleSheet, TouchableOpacity } from 'react-native'; +import { Box, Image, useColorMode } from 'native-base'; +import Text from 'src/components/KeeperText'; +import { hp, wp } from 'src/constants/responsive'; +import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png'; +import { useNavigation } from '@react-navigation/native'; +import Fonts from 'src/constants/Fonts'; + +// dummy chat data +const chatData = [ + { + id: '1', + name: 'John Doe', + lastMessage: 'Hey! Are you available tomorrow?', + image: '', + date: '2023-10-01T1:00:00', + message_count: 2, + }, + { + id: '2', + name: 'Jane Smith', + lastMessage: 'Let’s catch up soon!', + image: '', + date: '2023-10-01T12:45:00', + message_count: 6, + }, + { + id: '3', + name: 'Bob Johnson', + lastMessage: 'I sent the documents.', + image: '', + date: '2023-10-01T13:40:00', + message_count: 0, + }, +]; + +const chatDatas = []; + +const PlaceHolderChat = () => { + const { colorMode } = useColorMode(); + return ( + + + No Contacts Yet + + + Please add your trusted contacts to send/receive sats, advice and the latest bitcoin + updates. + + + ); +}; + +const ChatItem = ({ item, userProfileImage }) => { + 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', { + receiverProfileImage: item.image, + receiverProfileName: item.name, + userProfileImage, + }); + }; + + return ( + + + + profile + + + {item.name} + + + {item.lastMessage} + + + + + {formatTime(item.date)} + {item.message_count > 0 && ( + + + {item.message_count} + + + )} + + + + ); +}; + +const ChatList = ({ userProfileImage }) => { + const { colorMode } = useColorMode(); + + const renderItem = ({ item }) => ; + + 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), + }, + 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/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 341a01891..f35eb8568 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,13 +1,104 @@ -import { Box } from 'native-base'; +import { Box, ScrollView, useColorMode } from 'native-base'; import React from 'react'; +import { 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 Buttons from 'src/components/Buttons'; +import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; const Contact = () => { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + return ( - - contact + + + + + Recent Chats + + + + + {}}> + + } + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + + Keeper Support + + + We're here to help. + + + + + + 10:45 AM + + + + + + + + + {}} + fullWidth + LeftIcon={ContactAddicon} + /> + ); }; 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: { + marginVertical: wp(20), + }, +}); 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..15b6a9372 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -0,0 +1,112 @@ +import { Box, Image, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import Text from 'src/components/KeeperText'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; +import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; +import EditIcon from 'src/assets/images/contact-edit.svg'; +import { wp } from 'src/constants/responsive'; + +type Props = {}; + +const ContactHeader = (props: Props) => { + const { colorMode } = useColorMode(); + const userProfileImage = ''; + const userProfileName = ''; + return ( + + + + {userProfileImage ? ( + profileImage + ) : ( + placeHolder + )} + + + {userProfileName || 'Name'} + + + For easy identification  + + + + {}} style={styles.edit_icon}> + + + + {}}> + + + + Share + + + + + ); +}; + +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: wp(248), + }, + 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), + }, +}); From 6636a369eac495f93ac69cd61d2ab124cbf9c2c7 Mon Sep 17 00:00:00 2001 From: Parsh Date: Thu, 17 Jul 2025 10:22:41 +0200 Subject: [PATCH 12/80] fixes: worklet's config --- src/services/p2p/app.bundle.mjs | 2 +- src/services/p2p/config.mjs | 15 --------------- src/services/p2p/worklet.mjs | 3 +-- 3 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 src/services/p2p/config.mjs diff --git a/src/services/p2p/app.bundle.mjs b/src/services/p2p/app.bundle.mjs index 6992e70dd..4c9b11503 100644 --- a/src/services/p2p/app.bundle.mjs +++ b/src/services/p2p/app.bundle.mjs @@ -1 +1 @@ -export default "48661\n{\"version\":0,\"id\":\"f3e2489ed193e602b883fde45f9cdbf94ed6ec53c12c7edfdf5e3bcc52b98a88\",\"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/config.mjs\":{\"#package\":\"/package.json\"},\"/src/services/p2p/rpc-commands.mjs\":{\"#package\":\"/package.json\"},\"/src/services/p2p/worklet.mjs\":{\"#package\":\"/package.json\",\"./config.mjs\":\"/src/services/p2p/config.mjs\",\"./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/config.mjs\":{\"offset\":758500,\"length\":452,\"mode\":420},\"/src/services/p2p/rpc-commands.mjs\":{\"offset\":758952,\"length\":291,\"mode\":420},\"/src/services/p2p/worklet.mjs\":{\"offset\":759243,\"length\":3263,\"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.0\",\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.28.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}\n// For bare runtime, we'll use environment variables or defaults\nconst getConfig = (key) => {\n // Try to get from Bare.env if available, otherwise use defaults\n if (typeof Bare !== 'undefined' && Bare.env && Bare.env[key]) {\n return Bare.env[key].trim();\n }\n};\n\n// P2P specific config\nexport const RELAY_PEER_PUB_KEY = getConfig('RELAY_PEER_PUB_KEY') || '';\n\n// Export a default object for compatibility\nexport default {\n RELAY_PEER_PUB_KEY,\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';\nimport * as config from './config.mjs';\nglobal.Buffer = Buffer;\n\nconst { IPC } = BareKit;\n\nlet keyPair;\nconst connections = new Map();\n\nconst RELAY_PUB_KEY = config.RELAY_PEER_PUB_KEY;\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" +export default "48482\n{\"version\":0,\"id\":\"16e18e632992d00e9b48842a0e4ca0cecb86d95534fb041cfc4d974925ef027d\",\"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\":3264,\"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.1\",\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 = '58dac4e5cfe213a0755719abda3ab6d131d02eee0bc3bb2066ded29630885681';\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/config.mjs b/src/services/p2p/config.mjs deleted file mode 100644 index f4fea7989..000000000 --- a/src/services/p2p/config.mjs +++ /dev/null @@ -1,15 +0,0 @@ -// For bare runtime, we'll use environment variables or defaults -const getConfig = (key) => { - // Try to get from Bare.env if available, otherwise use defaults - if (typeof Bare !== 'undefined' && Bare.env && Bare.env[key]) { - return Bare.env[key].trim(); - } -}; - -// P2P specific config -export const RELAY_PEER_PUB_KEY = getConfig('RELAY_PEER_PUB_KEY') || ''; - -// Export a default object for compatibility -export default { - RELAY_PEER_PUB_KEY, -}; diff --git a/src/services/p2p/worklet.mjs b/src/services/p2p/worklet.mjs index 71b024234..ebcdbfd84 100644 --- a/src/services/p2p/worklet.mjs +++ b/src/services/p2p/worklet.mjs @@ -14,7 +14,6 @@ import RPC from 'bare-rpc'; import b4a from 'b4a'; import { Buffer } from 'buffer'; -import * as config from './config.mjs'; global.Buffer = Buffer; const { IPC } = BareKit; @@ -22,7 +21,7 @@ const { IPC } = BareKit; let keyPair; const connections = new Map(); -const RELAY_PUB_KEY = config.RELAY_PEER_PUB_KEY; +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 { From 232beda23aa91de2e605185c802ac6f962d39574 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 17 Jul 2025 14:29:13 +0500 Subject: [PATCH 13/80] chat section --- src/assets/images/add-image-icon.svg | 5 + src/assets/images/change-profile-icon.svg | 12 + src/assets/images/placeholder-whote-image.png | Bin 0 -> 4881 bytes src/assets/images/profile-placeHolder.png | Bin 0 -> 4485 bytes src/navigation/Navigator.tsx | 2 + src/navigation/privateTheme.js | 8 + src/navigation/themes.js | 8 + .../components/Contact/ChatRoomScreen.tsx | 57 +++ .../components/Contact/component/ChatList.tsx | 7 +- .../components/Contact/component/ChatRoom.tsx | 379 ++++++++++++++++++ .../Contact/component/ChatRoomHeader.tsx | 111 +++++ .../components/Contact/component/Contact.tsx | 33 +- .../Contact/component/ContactHeader.tsx | 26 +- .../Contact/component/EditModalContent.tsx | 50 +++ .../Contact/component/ProfileContent.tsx | 137 +++++++ src/theme/Colors.ts | 2 + 16 files changed, 823 insertions(+), 14 deletions(-) create mode 100644 src/assets/images/add-image-icon.svg create mode 100644 src/assets/images/change-profile-icon.svg create mode 100644 src/assets/images/placeholder-whote-image.png create mode 100644 src/assets/images/profile-placeHolder.png create mode 100644 src/screens/Home/components/Contact/ChatRoomScreen.tsx create mode 100644 src/screens/Home/components/Contact/component/ChatRoom.tsx create mode 100644 src/screens/Home/components/Contact/component/ChatRoomHeader.tsx create mode 100644 src/screens/Home/components/Contact/component/EditModalContent.tsx create mode 100644 src/screens/Home/components/Contact/component/ProfileContent.tsx 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/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/placeholder-whote-image.png b/src/assets/images/placeholder-whote-image.png new file mode 100644 index 0000000000000000000000000000000000000000..67a10fe67b9819e7b7a0da3113734af9b0b8fe1f GIT binary patch literal 4881 zcmV+s6YlJZP)7MRcdIrfz0&K9rA`aLNV8kkeK*kVZaf}d7V&H&d#~@?kI0kYOV#kOW zoI`-)L+}YvfCE?#7>ErPW*LxK%wl%1C4{gf$e@|-neOW8WxDI#+-g~}mXT)b>V7rT z-#If~)oazOzuvp=zW44WOi{9%Hz!rR<%x#`gC1cQ6GZOaVBTn^EA>@p3VblHFtE;?0#4 z>x2i$cV`IvlGglOAQ*Z=Q}C5&H2NNVjxUAK+uIv!i-tZ)iq6zppC(Tof=0r;o`@cC z6n+|ubv`EBW%vx=2*JJh?eWkF(0rkzd4UvmJIrKQYK{Ae^?lK{?mOWVj2A-5P90j& zdMS~*NOot!U-U{5-)<4F49Ami!eERQLNPbEkhQ*4GoM0)0Q^lw+K)Ag>HCqko%=k92GTGg{foJ*pKpp_beM67E<4ZUbD3qR6P^n}+CIu> z(<{VEzE5_$&}2+VdCi1&ZZG80SECMi+BDd9?>-g>?~%=8(PR=(56XqV^3ss>ObFR* z{~_WI{F(g3foL*m#F%`W*!f%}(Y+QP;7QX^80b4O09w-&LG6$x_8?7aO)--`4IWT) zA>{4UQn7d+MbjRrJ&b)c@!k1MU(JoknhK$4_g^drd$(A<4e?J*6XGCsXFfBq95tk7 zHiC%Y63ui2OowQri1k&uz19-APlmU%?$gXVA!8lg41KUg*8U8hXS|?^mq+7WH=+jA zLWY4rL+SYkD zCds5v&NW_`_$~$#FbgdLcmx%-BGTUe)I_I`iIEm8=K7D(T3s&RcQRmHEg|QrPHKsF z^k5Q9N(k9(-`s#$vaS&Wb6`2CH$ZxMq_wL@1i+{fc3-%U-&05i~b7uxJ07s zx0oOk76NmAb|G8rk#@?QE+WGg0bJD*@A?lBzz{ruxi?nGT3;4}?OGT$V8-hms?3ka zXKuj+n6MD?+5X4n<0rxpsZUT*Q zGNJZPM6kjn5x93lfIU@HVep*WZLofh2_*{k5 z8Sy&S1S65pq&_VN^EAxqV)j!|9WkEy4C87VxWf0CB7!Q)x~b73aEZ1hpJmM}U=Cee z=3B?}fiVjqpK;yC{7D!CukGy{_-7b`h~Yc(@H-d-FDTlBe?8WBkEtqiyNY=`{{n$# zzz{TJQEOXrH4Gv*_`xYk1&lc+@rb4=h{L1NaWT{?FbH>Dsk_K{Pp|J$2>HR(*&-#=<{Z8s zYE7<#;k34PzAbm*W*7w@BwpdAdwO+`LSTg}U>MT&z9R)})SWgGa*QQ8+Kn&_=|${u zIo*gtDCV{-k}BB5_ynFz<@N%GHm~Ml99A4moifr(BML$5z_(x^;>+lRZ?GmbFTiW0 zroVKg=XVoA$xh8<@NqB@soHG#1O+pXRhrj@B|E*_46EIQAlB}A(kz%6vq-aVrparr zn7Z6Q;07n#^~v1|Rs(+tLt#Q3K7mNJ!cfGRz0eI;xZ`s>2_ctBp9`Zy*s*WI>#^CP z0B2TXR_?$~LQo2eVK7p-z6rENh6e=`XW^H3+}|z)@j8X%-{)a4Ql;(3TecWmXsz6Xwk5Y+C|DnKd){d7~!y?gcTv4`I$#XP#As5DA2R z75A8kzb(eCan2<-aF{%N8$P0tDOJ0X)xs3}VAQEhCLv1xcyXw|ZlUMKN?~Z=9Nz^K zSJkXH+iV1VBzy*Ix^7@#gE7fF(&==YjMf=8t#1r4j&UEh)(B7rtydT;G&3A%`z{P2 z8sIDCn0#Ry?1-EH%6+72WDw=N4WA<8{(l}zbpJ0Jh`8pLi6`+kza|)|pwz9{;yLg! zQk&gVU3=Id3U=QDMYze&S!O~i{3#`k?ML8mg3_g2ozH%7H0n$_H+Yg}x=ZRMPf`ok z3&)`Yl?M4fV+zBaof~)}KbZOg>OeKVbS}MY*l~Vh*b1a&bB2MG; z<*C`2!bthNPPt5KK)OGsrq``GZnz!!w^3?~-x2FR)>~&AlR}(Hb<67q%iclNi8|8; z8gOfbA*m6fXfl0a^rVa~auj~O(X-VhHfX8#1AXyOr!_XFZm8>o8aeT<9(Ix-w=YcYJqGg$Wg zY`7Y+HdOV&5ZonrX6AbuBc#;3A_Q#qn+~MZvvMt7V&cawMb8a8&U&DfipOUa@#dRl z%$rw_GYV!hnQ(+loel-(iaL%Y<^@pd)3WU^Cmr*88Ee?GNRjmr42P?imY=hf`cJMD zSGRQRYfi$99pGGUMxG0c!Ri9J!Hyyy)D=W}fzniY0j9%vS#+@I=c^x^ABlI~VyXyS zsI~QjSiJj|XuNa2d(86p3Jiyw;HIixhccCN($GdKlZf+4ign2`xV7Fev ztgaw4M|tgNBAKX;HOMfuL3swYK`E{E(nzBFI=_m*h3Lb@XrlXCkp7YkbsOls;}_lk zRUt_G`!vjA9cguBYkSutm?Fdy-48?p`Xs3pCY+nm3$&)^-N&jBEQM!b2qMbo94a5% z(%$|KrWh3IUK_EnP^yWSVF-aB^|+5!A%rMwM6wCFTV9K5b?QWuSo#5qCO5aVs?W%| zz77KzD)OuD# zyAWp1oH-=Jk#$!mv+jw@Y&s(a5<`9H z6}R$E(Oq_`5!|IgSWgA zX-kgiLEg*SM|BfCgf-{UWG9sJ*r7+bdQUIyQ3%mEek}bO&wT%+s40Y95YMi6Wg-Ji z(Rgyzo?hOg5QvglsjgPQGq?*)c7j2km5F7kf7;vYqt3C-+vyiYO*IeE>rP3J#5+4p z&ir0a+z2hjTq?s%HP1yMgL-eZ#FO*(_MTDWBRFs_g9p~E_$m^)TXGkAc->U=b*A_I}0l!e$GD`TCV503V}F)hEyr&4n`fS09; zsdcDXMgYe~qe;^y&(@WKJ#eCoKt6^VFP)R-<^!sfj)*2_Z5r*pV+x%UO(r*4N?(cE zLnz~pe7=7crYVI&dOs1tO4L{cE~M};V-Z31y%-01yZYJkn+o?k_O~_|cM_an| zgcl-^CZU(hbk=x2GcF;tbacGS;EUh|LssE)*l~L9Pb( ztT~#X{?@%#q*nrLb$OIibEa8${72341E?navAT4 zTtHhhofB&t-FrC7_e`GRQ2Gq*a9!sgx{*l45p!)U(RF8|1+i+?D(j5TopQdeRK6z;ITup^!Xe=BCvr)lEt;zPb9iL325S(b(`q!URx{|#LLA!XfjD? zjYGrpy%It77f=WCgZ(SS#Qi(j^xCm9<&f^-HIZ1y6<&9d>((yjGJVS|WnCxM_teTf z>lxg~ur7)vI_^gusH=fg7#KK!Eq+K&@(?uH3-mZSbisIsk|*_xXaM<4|HV?z{ex6@ z{?D{tL!?)b7N^{mvG&fJQD+)50VLYibyKlYIzpO>m1yGjUuBq`AD{{jZ-fY38s2vv z%4|7}pnf3EnrYcw$j$JQQr4v{@s5TDJvGLOQpoiG1(Ui8)5h;WZ;Ov*MN1-iD-6Im zZ(4qE%Tg%IooaO$rXY&w6=?^47;WviTWq8(48d5pYA%ynB!%`ZajYLF2N{5maYGR) z&@W}6==w-&@?jW=@&0w&PR+9%eLjPhigRTSe1r^oZPXRMEf7#Y4jUdz(pTBZWMuPP zMSSKTrhkQCAaY!TB7h!csUHp6!%tKLnfd@KgFa`O4HLwa41YZ@600000NkvXXu0mjf D^vpMd literal 0 HcmV?d00001 diff --git a/src/assets/images/profile-placeHolder.png b/src/assets/images/profile-placeHolder.png new file mode 100644 index 0000000000000000000000000000000000000000..de14cd7218955b62762cd392b9efcdc973e87e46 GIT binary patch literal 4485 zcmV;05qj>4P)Bzl2PJLJ(sxSOKjdTCAT&6@sMSz$p+g8sI4TD> zjG|(3M-xO`Kt<4sAVCzN>{Lj=Y|!5~PI5xXGMT*PzM1)*^SE~wGWXs4f8X7|8z&J; zUS8fI9XfP4B`z-Rw|n>Q?a5mQbM+EXmFogtCWyDG;ab4GJe--C`IZ=nQ9|(h{at)M-w2|aWZEAkGFe8o z_8<++VlfC~gg|%irug{yyNLFY=%9>i0_})r!~k3u0#(4-JpGfoPL$|mIgQ`jva+(? z5EpSx2ves{?TH1Lf&ebzWe16Js4iw8hvBud7#7y3G%Bmd3V{cVZ%DkojdnEu^C&d2C9NXQTA+Z z>MV&7HsoI^`8d+fhNNvmm@;L`u?(-yCE8nJSi!1 zMJ&J)A)wBy$++jmO!iW+%QbxzdcoMSW4DCzp9%{Ldn1ma{NHr`@9)IWDSOg)4FgZJ zq@A?;<`^g%$|#tMpo^Hr9*sI(Ud&<(f*+Wjo&BwZN!UV| zK7D$R>gwuJF{q130qHXI37tE4E*v>>WQEwk%$YMgRa8`rAv%fQGikz17tjvCGHjAC z2wMoW3rfg{L1GA`Wj#;pb?r5gp;A;-bSnM&*NJu$LqG@%Hg4QFXu^aEd)oM4!>jKw zk+~*`fD9<3A$Y!1B2XCNTa55@55*5+2$addw6wGbB@EhZ5wwRu@Qf*DQ7wUrz^zrn zScbmT&kIS1F#~7_^fK~ePFwy@n}iTtPw)}Z!^8~Au?EAOv;<9&pPzpQf#!=TUCb@y z%c*Ty&(Nl-fj>T5%n^a6C(msWC=BX+LXa!wkQ!V76fz+Tfp$R_{ppj$99DMh*zrCw z1R9R_6RZ$(7)+Us4fV?*nKJDvR#jDf#uJz(hCo^_!Wz6S2I2SnFJP+Jq%>{AGMo^~ zI);!e0wQ?8BoV-erinnw$;o+#eELAlAw^e;hw{Z%g`h*o+}w>~78Au#cmj<|n{F4- zy(6uCuT>$?HG5PHLdOhJQ&WxHKd6wlcgXyh-%wwv7Ams%78-Now+@IBBN|5aCA zRV#*~X~P^BiCLUTS>Dvr7g`hoA~2>^UI&e_cg1iRVAA3D8Zii+J=IOd&3)di5Kz!n zV&0m=_3PHH`%Vl8Q@n~Q>?bjcK2-A~n)|R>A)uhU#XMFsp<>jN))e#}DuYhAjDjkw z+3j>q3W0%$iwKPKPry@V%TjJTAg#t(F7-J<*?p{kPu=tNnzKn3#B@80xc#MwpkD$s4)dpNA;{ zG3?i9+%IFv>>I>TxQS%(4xPnZZc%rI4d2*D2+XTrD+ar;LkM1SuV->vyQ|O2hHva6 z1Ty+kF&M0sXB=&jmqw?R>S0X7*Y!duE-p^w!^_2B$lP9Ze@r&H0T}DhygXVAhV-4S zE3)fe*9&3u=FR64_{3}u!z!3I_pu+{x^>g-awhHdXo`c&%ggH*8r2KIbZZbKUKgs0 z;b6Hg6mM`vU8G(J3_qSH9st2+JoVI59mP->he<*Re-RI$tGw#s0U?n19^w(F79_ss z?%lhu5JU0#e3v6uV;1pf!7=ASm8=U+)&&@;#?@hDicKWAoU z{##r`om=rR%@mIwFtTgcu5R%R(DW1!qucQ4^_(Ftq0TdcUS&Od%1ozDole%O;VAJo zKHXiEmzURH9HyY4;Jl#QAiIthbOek+?|4jWB%GL{9?|C0Y9B5Rpxs1um`w)0NZh0y zU|~Vt^@t<$o+W;*L*Yp#w^l5+mBFR3TJJjA*Blq4Ids_VLGX zp0%;6A%;?}y@sD9MpQ$Gp-P3|8Rru*Y-7DHrr0hX2SwNFp&dlE;TzVGJ#GKVINjE! zV=y{E<@G-J8r>`ZlLyBV^klXrq+7k0Evl`pE#vjpE?v6(!i;0}_U+r97$WrXyGcw) zr4aTdEvJzdJpf%-OL=XOu1qb$YViO%{7YspBKm|*9xzWBoozM*5yUfveLfcBpJJe6 zvWC>u)bt6+l^MuwWc0ye9@`k_$W2O0%Cl;LiPPvl&E30qXREtHLuYw+jvF_wOJL=# zPRYuzb(=}(LXxY~)6?gPhY1RJQht8EPJ2%^ZEXYT`CP-g{evHgK?Hpio`zCAErNz{ zFRvkl^SHOL&t|j((zIB^Iw5=_=CG)|y!i;Cd!P;%7r*@?Q4n)gxa&iKbjtyPsxOx}{CTd(}W~OKNV73*lCdl(_ahLb% zA`P8Qye#f9FC!!4If*i`47zc4zPJPyslLqXg-}sZ@dXNX!z}Bu_qt~w8dr|)r8*oh zF5J7$I5I$2Qun%E2sF?tFwbtNvlMG!M8nAqs3PqE$j!R34SEW*lU)*{ zF}4oqQKkCuOw%iktheSs`41{q;LYSgzPds>Ij_ z>TlcfgtGj5OP{ys;T#1$Nz48Vv4>vjqDEo_(BppyY+1&J2$oZp&$jfPR$bi(Cq*o8 z&7%G;TeogKM`8p>O-&s_V9S9dwY9akwf3D>g}_kcd=xcTYydHgk{AIP-nS($$m1DI zY`xLi_d+`S0;`}4D7K09TsL*<)QEKL*A}Ktn>L67yHYHMzqwI<^$Xc5Aqj!L=URe$ z#CmjJ#WQ5Y;S!}378dr>r&jFQKlzVf*|kcWemp-v|9yhNVlh-b-_R&Mhe@w35=H2F zN>x==A8`MD#bWrIU(i52In?jB>0BiK_)P?s7nZuZqx$(IFE8&ni4qD53i>m@Fy9Ul zke|AgpiNaxo6Qkc;WC0(#2$24Q2M4#o1UgSwMQZl?J{rKvSk#7HOZzeaGGy7G+uAe z8e!E-)akk0h;65tQBAx`D|-Cs(WAeY2*U68_fAMim_qbou?fUAkt*RXX(M3?LC-5d z47w@iEU}Lb2;!lXl$3n0?0{~Ll9G~y6)RTc(4W6E=;qfJ_}jYvGpsgTSZ62lgL}Dj zgGQOyM|au{eg>W9Av({ULRUvoQPF_q%a@mMFOS+O0x07*MoO5ptIP7l6HgpdSy}m^ z*gtQet7>Kw-rv-s3KOAQcsv$h0)?+G{_P%0--HMT>ZTb9lXmxAgM%TuW5$V-S#@^q zC9Wqif!ggdYA;047s6Sj>&D;!krQWF??ePwhcbHD5|#{N&=nd+VjNw6NMVI}>Kcc89za1{L0k_lKSCTvPo+l$mgKpm!70K#OIw6Pba>sz7zE%mm`Vy(2-9) zOq=jPaUbomU28>k&=6aEztLtGO8xy5(L=>>w0#jgxy1{9@V1cN;fSDAEXKB*XWpPM zPm#7{drJ%}D8q_ff(5h1p^0s++n^%|9px&O80|tgrEm?fO9X-65C@>$kc)M=-GPCX zh(qTN7(~0!;+dj6z!i_vwV_b@(CAP(bNmkBZGLEHE1xV*&BY4+D?LuG}! z#?We9?>p<50~zorSF~0aOK1z+N?X8Hr>DjYqA+fMJ=S3Yf{2!JhgG_zP!Ve}aX;i9vq+*>|#A+vIFa(0Lz7(1*=m2nZhi#v!83cn#< zKd&%=?wv#sCvcsOIP}=P6M6d}u?HRWSxMn9>LtG} zE;*X(2wv-ETdh($^SYNF;>nd1T#rzx=VT+Kbs7duycW0~=l2s9Xf^TAm{r{>5y<}m XS(Mka=sr^000000NkvXXu0mjf=Z}2! literal 0 HcmV?d00001 diff --git a/src/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index bf8c97a74..77933f683 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -156,6 +156,7 @@ 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 ChatRoomScreen from 'src/screens/Home/components/Contact/ChatRoomScreen'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -276,6 +277,7 @@ function AppStack() { + diff --git a/src/navigation/privateTheme.js b/src/navigation/privateTheme.js index 9c1f0a8c4..be5afd48f 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.stoneGrey, + coolGrey: Colors.bodyText, + subchatText: Colors.primaryCream, + SlateWhite: Colors.primaryCream, }, }, config: { diff --git a/src/navigation/themes.js b/src/navigation/themes.js index 755cc360c..8915490fe 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.stoneGrey, + coolGrey: Colors.bodyText, + subchatText: Colors.primaryCream, + SlateWhite: Colors.primaryCream, }, }, config: { diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx new file mode 100644 index 000000000..aaa4ce1a2 --- /dev/null +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import { Box, useColorMode } from 'native-base'; +import { StyleSheet } from 'react-native'; +import { RouteProp, useRoute } from '@react-navigation/native'; +import KeeperModal from 'src/components/KeeperModal'; +import ChatRoomHeader from './component/ChatRoomHeader'; +import EditModalContent from './component/EditModalContent'; +import ChatRoom from './component/ChatRoom'; + +type ChatRoomParams = { + ChatRoomScreen: { + receiverProfileImage: string; + receiverProfileName: string; + userProfileImage: string; + }; +}; + +const ChatRoomScreen = () => { + const { colorMode } = useColorMode(); + const route = useRoute>(); + const { receiverProfileImage, receiverProfileName, userProfileImage } = route.params; + const [editReceiverProfileName, setEditReceiverProfileName] = useState(receiverProfileName); + const [openEditModal, setOpenEditModal] = useState(false); + + return ( + + + + setOpenEditModal(false)} + title="Edit Contact Name" + 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/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index 32d1b1019..4ddbf9ac2 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -13,8 +13,9 @@ const chatData = [ id: '1', name: 'John Doe', lastMessage: 'Hey! Are you available tomorrow?', - image: '', - date: '2023-10-01T1:00:00', + image: + 'file:///Users/mac/Library/Developer/CoreSimulator/Devices/2C667219-79A1-4604-9AC8-183891E43BA9/data/Containers/Data/Application/9389AA84-812B-4523-BA70-4581CEE2193E/tmp/BC7D0137-3ABA-42DF-9EC4-F0F6556B70DD.jpg', + date: '2025-07-17T12:00:00', message_count: 2, }, { @@ -30,7 +31,7 @@ const chatData = [ name: 'Bob Johnson', lastMessage: 'I sent the documents.', image: '', - date: '2023-10-01T13:40:00', + date: '2025-07-16T12:00:00', message_count: 0, }, ]; 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..8efd6b1fb --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -0,0 +1,379 @@ +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 PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; +import PlusIcon from 'src/assets/images/plus-green-icon.svg'; +import PlusWhiteIcon from 'src/assets/images/add-plus-white.svg'; + +// dummy data +const initialMessages = [ + { + id: 1, + text: 'Hey, did you end up watching "The Silent City"?', + sender: 'other', + date: '2025-05-12T10:12:00', + }, + { + id: 2, + text: 'Yeah I did! Just finished it last night.', + sender: 'me', + date: '2025-05-12T10:13:00', + }, + { + id: 3, + text: 'Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.', + sender: 'other', + date: '2025-05-12T10:14:30', + }, + { + id: 10, + text: 'Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.', + sender: 'other', + date: '2025-05-12T10:14:30', + }, + { + id: 4, + text: 'Right? When she found out her brother was behind it all...', + sender: 'me', + date: '2025-05-12T10:15:20', + }, + { + id: 5, + text: 'Exactly! And the cinematography was so good. Every shot looked like a painting.', + sender: 'other', + date: '2025-05-12T10:17:10', + }, + { + id: 6, + text: 'I really liked the soundtrack too. Gave me chills.', + sender: 'me', + date: '2025-05-12T10:19:45', + }, + + { + id: 7, + text: 'I’ve been thinking about that ending all day ', + sender: 'other', + date: '2025-05-13T09:05:00', + }, + { + id: 8, + text: 'Same! I kind of want to rewatch it already.', + sender: 'me', + date: '2025-05-13T09:06:10', + }, + { + id: 9, + text: 'Let’s do a watch party this weekend? I’ll bring popcorn ', + sender: 'other', + date: '2025-05-13T09:07:55', + }, + { id: 10, text: 'Deal. Saturday night?', sender: 'me', date: '2025-05-13T09:08:20' }, + { id: 11, text: 'Perfect. Can’t wait!', sender: 'other', date: '2025-05-13T09:08:45' }, +]; + +const groupMessagesByDate = (msgs) => { + const groups = {}; + msgs.forEach((msg) => { + const date = moment(msg.date); + 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 }) => { + const [messages, setMessages] = useState(initialMessages); + const [inputValue, setInputValue] = useState(''); + const groupedMessages = groupMessagesByDate(messages); + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + const scrollRef = useRef(null); + + const handleSend = () => { + if (!inputValue.trim()) return; + const newMessage = { + id: Date.now(), + text: inputValue, + sender: 'me', + date: new Date().toISOString(), + }; + setMessages((prev) => [...prev, newMessage]); + setInputValue(''); + setTimeout(() => { + scrollRef?.current?.scrollToEnd({ animated: true }); + }, 0); + }; + useEffect(() => { + setTimeout(() => { + scrollRef?.current?.scrollToEnd({ animated: true }); + }, 100); + }, [messages]); + + return ( + + + + + {Object.entries(groupedMessages).map(([dateLabel, msgs]) => ( + + {/* Date Label */} + + + + {dateLabel} + + + + {/* Messages */} + {msgs.map((msg, index) => { + const thisMoment = moment(msg.date); + const nextMsg = msgs[index + 1]; + const nextMoment = nextMsg ? moment(nextMsg.date) : null; + + const isLastInMinuteAndSenderGroup = + !nextMoment || + !thisMoment.isSame(nextMoment, 'minute') || + msg.sender !== nextMsg.sender; + + const prevMsg = msgs[index - 1]; + const prevMoment = prevMsg ? moment(prevMsg.date) : null; + + const isFirstInGroup = + !prevMoment || + !thisMoment.isSame(prevMoment, 'minute') || + msg.sender !== prevMsg.sender; + + return ( + + {msg.sender === 'other' && + isFirstInGroup && + (receiverProfileImage ? ( + reciverprofileImage + ) : ( + placeHolder + ))} + + + + + {msg.text} + + + + {isLastInMinuteAndSenderGroup && ( + + {thisMoment.format('hh:mm A')} + + )} + + + {msg.sender === 'me' && + isLastInMinuteAndSenderGroup && + (userProfileImage ? ( + userprofileImage + ) : ( + placeHolder + ))} + + ); + })} + + ))} + + + + + + {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..e7e291570 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx @@ -0,0 +1,111 @@ +import { useNavigation } from '@react-navigation/native'; +import { Box, Image, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import { useSelector } from 'react-redux'; +import BackBlackButton from 'src/assets/images/header-arrow-icon.svg'; +import BackWhiteButton from 'src/assets/images/leftarrowCampainlight.svg'; +import PrivateBackButton from 'src/assets/privateImages/gold-back-arrow.svg'; +import Text from 'src/components/KeeperText'; +import { hp, wp } from 'src/constants/responsive'; +// import EditIcon from 'src/assets/images/edit-pencil-icon.svg'; +import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png'; +import { StatusBar } from 'react-native'; + +const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEditModal }) => { + const navigation = useNavigation(); + const { colorMode } = useColorMode(); + const themeMode = useSelector((state: any) => state?.settings?.themeMode); + const privateTheme = themeMode === 'PRIVATE'; + + return ( + + + + + + {privateTheme ? ( + + ) : colorMode === 'light' ? ( + + ) : ( + + )} + + + {receiverProfileImage ? ( + profileImage + ) : ( + placeHolder + )} + + + + + {receiverProfileName} + + + setOpenEditModal(true)}> + {/* */} + + + + + ); +}; + +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', + gap: wp(10), + 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 index f35eb8568..45a9f0632 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,5 +1,5 @@ import { Box, ScrollView, useColorMode } from 'native-base'; -import React from 'react'; +import React, { useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import Text from 'src/components/KeeperText'; import { wp } from 'src/constants/responsive'; @@ -11,14 +11,23 @@ import PinIcon from 'src/assets/images/Pin.svg'; import ChatList from './ChatList'; import Buttons from 'src/components/Buttons'; import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; +import KeeperModal from 'src/components/KeeperModal'; +import ProfileContent from './ProfileContent'; const Contact = () => { const { colorMode } = useColorMode(); const isDarkMode = colorMode === 'dark'; + const [createProfile, setCreateProfile] = useState(false); + const [userProfileImage, setUserProfileImage] = useState(null); + const [userProfileName, setUserProfileName] = useState(''); return ( - + Recent Chats @@ -50,7 +59,7 @@ const Contact = () => { - + { LeftIcon={ContactAddicon} /> + setCreateProfile(false)} + title="Edit Your Profile" + subTitle="Add a name/nym and an optional photo." + textColor={`${colorMode}.textGreen`} + subTitleColor={`${colorMode}.modalSubtitleBlack`} + modalBackground={`${colorMode}.modalWhiteBackground`} + Content={() => ( + + )} + /> ); }; diff --git a/src/screens/Home/components/Contact/component/ContactHeader.tsx b/src/screens/Home/components/Contact/component/ContactHeader.tsx index 15b6a9372..ef88a1a57 100644 --- a/src/screens/Home/components/Contact/component/ContactHeader.tsx +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -7,12 +7,15 @@ import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; import EditIcon from 'src/assets/images/contact-edit.svg'; import { wp } from 'src/constants/responsive'; -type Props = {}; +type Props = { + userProfileImage: string | null; + userProfileName: string; + setCreateProfile: any; +}; -const ContactHeader = (props: Props) => { +const ContactHeader = ({ userProfileImage, userProfileName, setCreateProfile }: Props) => { const { colorMode } = useColorMode(); - const userProfileImage = ''; - const userProfileName = ''; + return ( { {userProfileName || 'Name'} - - For easy identification  - + {!userProfileName && ( + + For easy identification  + + )} - {}} style={styles.edit_icon}> + { + setCreateProfile(true); + }} + style={styles.edit_icon} + > diff --git a/src/screens/Home/components/Contact/component/EditModalContent.tsx b/src/screens/Home/components/Contact/component/EditModalContent.tsx new file mode 100644 index 000000000..3ae34c10b --- /dev/null +++ b/src/screens/Home/components/Contact/component/EditModalContent.tsx @@ -0,0 +1,50 @@ +import { Box, useColorMode } from 'native-base'; +import React, { useEffect, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import Buttons from 'src/components/Buttons'; +import KeeperTextInput from 'src/components/KeeperTextInput'; + +const EditModalContent = ({ + editReceiverProfileName, + setEditReceiverProfileName, + setOpenEditModal, +}) => { + const { colorMode } = useColorMode(); + const [editName, setEditName] = useState(''); + + useEffect(() => { + setEditName(editReceiverProfileName); + }, [editReceiverProfileName]); + + const handleEditName = () => { + setEditReceiverProfileName(editName); + setEditName(''); + setOpenEditModal(false); + }; + return ( + + + { + handleEditName(); + }} + /> + + ); +}; + +export default EditModalContent; +const styles = StyleSheet.create({ + container: { + gap: 10, + }, +}); 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..eaef010e2 --- /dev/null +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -0,0 +1,137 @@ +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-whote-image.png'; +import { useEffect, useState } from 'react'; +import AddImageIcon from 'src/assets/images/add-image-icon.svg'; +import { launchImageLibrary } from 'react-native-image-picker'; +import Buttons from 'src/components/Buttons'; +import ChangeProfileImageIcon from 'src/assets/images/change-profile-icon.svg'; + +const ProfileContent = ({ + setUserProfileImage, + setUserProfileName, + setCreateProfile, + userProfileImage, + userProfileName, +}) => { + const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; + const [profileImage, setProfileImage] = useState(null); + const [profileName, setProfileName] = useState(''); + + 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 = () => { + setUserProfileImage(profileImage); + setUserProfileName(profileName); + setCreateProfile(false); + }; + + return ( + <> + + + {profileImage ? ( + profileImage + ) : ( + placeHolder + )} + + {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/theme/Colors.ts b/src/theme/Colors.ts index 47b742177..02826c186 100644 --- a/src/theme/Colors.ts +++ b/src/theme/Colors.ts @@ -153,5 +153,7 @@ 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)', }; export default Colors; From c0901639eeb85b6a4e1f06d201faea3c6cc263c7 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 17 Jul 2025 15:49:31 +0500 Subject: [PATCH 14/80] concierge --- src/assets/images/send-white-icon.svg | 10 +++ src/navigation/Navigator.tsx | 2 + src/screens/Concierge/KeeperSupport.tsx | 74 +++++++++++++++++++ .../components/Contact/component/ChatRoom.tsx | 3 +- .../components/Contact/component/Contact.tsx | 9 ++- .../components/TicketHistory.tsx | 3 +- 6 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 src/assets/images/send-white-icon.svg create mode 100644 src/screens/Concierge/KeeperSupport.tsx 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/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index 77933f683..d00abf309 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -157,6 +157,7 @@ import Usdtsetting from 'src/screens/USDT/Usdtsetting'; import { CloudBackupPassword } from 'src/screens/CloudBackup/CloudBackupPassword'; import { ImportedWalletSetup } from 'src/screens/Vault/ImportedWalletSetup'; import ChatRoomScreen from 'src/screens/Home/components/Contact/ChatRoomScreen'; +import KeeperSupport from 'src/screens/Concierge/KeeperSupport'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -278,6 +279,7 @@ function AppStack() { + diff --git a/src/screens/Concierge/KeeperSupport.tsx b/src/screens/Concierge/KeeperSupport.tsx new file mode 100644 index 000000000..35b69caea --- /dev/null +++ b/src/screens/Concierge/KeeperSupport.tsx @@ -0,0 +1,74 @@ +import { useNavigation } from '@react-navigation/native'; +import { Box, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import Text from 'src/components/KeeperText'; +import ScreenWrapper from 'src/components/ScreenWrapper'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; +import { hp, wp } from 'src/constants/responsive'; +import ConciergeIcon from 'src/assets/images/faqWhiteIcon.svg'; +import Fonts from 'src/constants/Fonts'; +import TechnicalSupport from '../KeeperConcierge/TechnicalSupport'; +import { StatusBar } from 'react-native'; + +const KeeperSupport = () => { + const { colorMode } = useColorMode(); + const navigation = useNavigation(); + + return ( + + + + {/* header */} + + { + navigation.goBack(); + }} + style={styles.backButton} + > + + + } + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + Keeper Support + + + + {/* adjust the route accordingly */} + + + + ); +}; + +export default KeeperSupport; +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: wp(20), + }, + header: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: hp(10), + paddingBottom: hp(24), + borderBottomWidth: 1, + }, + headerText: { + fontSize: 18, + fontFamily: Fonts.LoraMedium, + marginLeft: wp(13), + }, + backButton: { + height: hp(40), + width: wp(40), + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index 8efd6b1fb..970e29762 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -16,6 +16,7 @@ import { hp, wp } from 'src/constants/responsive'; import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; 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'; // dummy data const initialMessages = [ @@ -284,7 +285,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage }) => { /> - {/* */} + diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 45a9f0632..6788c09f6 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -13,6 +13,7 @@ import Buttons from 'src/components/Buttons'; import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; import KeeperModal from 'src/components/KeeperModal'; import ProfileContent from './ProfileContent'; +import { useNavigation } from '@react-navigation/native'; const Contact = () => { const { colorMode } = useColorMode(); @@ -20,6 +21,7 @@ const Contact = () => { const [createProfile, setCreateProfile] = useState(false); const [userProfileImage, setUserProfileImage] = useState(null); const [userProfileName, setUserProfileName] = useState(''); + const navigation = useNavigation(); return ( @@ -35,7 +37,12 @@ const Contact = () => { - {}}> + { + navigation.navigate('keeperSupport'); + }} + > { {tickets.length ? : } {!onboardCallScheduled && ( - + { const styles = StyleSheet.create({ container: { - paddingBottom: hp(10), flex: 1, }, historyTitle: { From c612f65b2ff6a5ef3b3b68caa61080ebb8a42c49 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 17 Jul 2025 18:38:16 +0500 Subject: [PATCH 15/80] add and share contact --- src/assets/images/exportFile.svg | 3 + src/navigation/Navigator.tsx | 2 + src/screens/Concierge/KeeperSupport.tsx | 48 +---- .../Home/components/Contact/ContactShowQr.tsx | 42 ++++ .../components/Contact/component/Contact.tsx | 29 ++- .../Contact/component/ContactHeader.tsx | 19 +- .../Contact/component/ContactModalData.tsx | 188 ++++++++++++++++++ .../KeeperConcierge/TechnicalSupport.tsx | 42 +++- .../components/ConciergeScreenWrapper.tsx | 6 +- 9 files changed, 324 insertions(+), 55 deletions(-) create mode 100644 src/assets/images/exportFile.svg create mode 100644 src/screens/Home/components/Contact/ContactShowQr.tsx create mode 100644 src/screens/Home/components/Contact/component/ContactModalData.tsx 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/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index d00abf309..021d29672 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -158,6 +158,7 @@ import { CloudBackupPassword } from 'src/screens/CloudBackup/CloudBackupPassword import { ImportedWalletSetup } from 'src/screens/Vault/ImportedWalletSetup'; 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'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -280,6 +281,7 @@ function AppStack() { + diff --git a/src/screens/Concierge/KeeperSupport.tsx b/src/screens/Concierge/KeeperSupport.tsx index 35b69caea..6b5d50ab9 100644 --- a/src/screens/Concierge/KeeperSupport.tsx +++ b/src/screens/Concierge/KeeperSupport.tsx @@ -1,16 +1,10 @@ import { useNavigation } from '@react-navigation/native'; import { Box, useColorMode } from 'native-base'; import React from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; -import CircleIconWrapper from 'src/components/CircleIconWrapper'; -import Text from 'src/components/KeeperText'; +import { StatusBar, StyleSheet } from 'react-native'; import ScreenWrapper from 'src/components/ScreenWrapper'; -import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; -import { hp, wp } from 'src/constants/responsive'; -import ConciergeIcon from 'src/assets/images/faqWhiteIcon.svg'; -import Fonts from 'src/constants/Fonts'; +import { wp } from 'src/constants/responsive'; import TechnicalSupport from '../KeeperConcierge/TechnicalSupport'; -import { StatusBar } from 'react-native'; const KeeperSupport = () => { const { colorMode } = useColorMode(); @@ -19,26 +13,6 @@ const KeeperSupport = () => { return ( - - {/* header */} - - { - navigation.goBack(); - }} - style={styles.backButton} - > - - - } - backgroundColor={`${colorMode}.pantoneGreen`} - /> - - Keeper Support - - {/* adjust the route accordingly */} @@ -53,22 +27,4 @@ const styles = StyleSheet.create({ flex: 1, paddingTop: wp(20), }, - header: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: hp(10), - paddingBottom: hp(24), - borderBottomWidth: 1, - }, - headerText: { - fontSize: 18, - fontFamily: Fonts.LoraMedium, - marginLeft: wp(13), - }, - backButton: { - height: hp(40), - width: wp(40), - justifyContent: 'center', - alignItems: 'center', - }, }); diff --git a/src/screens/Home/components/Contact/ContactShowQr.tsx b/src/screens/Home/components/Contact/ContactShowQr.tsx new file mode 100644 index 000000000..867574781 --- /dev/null +++ b/src/screens/Home/components/Contact/ContactShowQr.tsx @@ -0,0 +1,42 @@ +import { Box, useColorMode } from 'native-base'; +import { authenticator } from 'otplib'; +import React 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'; + +const ContactShareQr = () => { + const validationKey = 'Bitcoin Keeper'; //dummy key, replace with actual key + const { colorMode } = useColorMode(); + 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/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 6788c09f6..00283d594 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -14,6 +14,7 @@ import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; import KeeperModal from 'src/components/KeeperModal'; import ProfileContent from './ProfileContent'; import { useNavigation } from '@react-navigation/native'; +import ContactModalData from './ContactModalData'; const Contact = () => { const { colorMode } = useColorMode(); @@ -22,6 +23,8 @@ const Contact = () => { const [userProfileImage, setUserProfileImage] = useState(null); const [userProfileName, setUserProfileName] = useState(''); const navigation = useNavigation(); + const [contactmodalVisible, setContactModalVisible] = useState(false); + const [shareContact, setShareContact] = useState(false); return ( @@ -29,6 +32,8 @@ const Contact = () => { userProfileImage={userProfileImage} userProfileName={userProfileName} setCreateProfile={setCreateProfile} + setContactModalVisible={setContactModalVisible} + setShareContact={setShareContact} /> @@ -71,7 +76,10 @@ const Contact = () => { {}} + primaryCallback={() => { + setContactModalVisible(true); + setShareContact(false); + }} fullWidth LeftIcon={ContactAddicon} /> @@ -94,6 +102,22 @@ const Contact = () => { /> )} /> + setContactModalVisible(false)} + title={shareContact ? 'Share Contact Info' : 'Add a New Contact'} + subTitle="Choose how to add the contact" + textColor={`${colorMode}.textGreen`} + subTitleColor={`${colorMode}.modalSubtitleBlack`} + modalBackground={`${colorMode}.modalWhiteBackground`} + Content={() => ( + + )} + /> ); }; @@ -133,6 +157,7 @@ const styles = StyleSheet.create({ gap: wp(6), }, bottomButton: { - marginVertical: wp(20), + marginTop: wp(10), + marginBottom: wp(15), }, }); diff --git a/src/screens/Home/components/Contact/component/ContactHeader.tsx b/src/screens/Home/components/Contact/component/ContactHeader.tsx index ef88a1a57..f31440d0b 100644 --- a/src/screens/Home/components/Contact/component/ContactHeader.tsx +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -10,10 +10,18 @@ import { wp } from 'src/constants/responsive'; type Props = { userProfileImage: string | null; userProfileName: string; - setCreateProfile: any; + setCreateProfile: (visible: boolean) => void; + setContactModalVisible: (visible: boolean) => void; + setShareContact: (share: boolean) => void; }; -const ContactHeader = ({ userProfileImage, userProfileName, setCreateProfile }: Props) => { +const ContactHeader = ({ + userProfileImage, + userProfileName, + setCreateProfile, + setContactModalVisible, + setShareContact, +}: Props) => { const { colorMode } = useColorMode(); return ( @@ -54,7 +62,12 @@ const ContactHeader = ({ userProfileImage, userProfileName, setCreateProfile }: - {}}> + { + setContactModalVisible(true); + setShareContact(true); + }} + > { + 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: 1, + label: 'Show QR', + icon: , + onPress: () => { + setContactModalVisible(false); + navigation.navigate('ContactShareQr'); + }, + }, + { + id: 2, + label: `${isIos ? 'Airdrop / ' : ''}File `, + icon: , + onPress: () => { + setContactModalVisible(false); + shareWithAirdrop(); + }, + }, + + { + 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/KeeperConcierge/TechnicalSupport.tsx b/src/screens/KeeperConcierge/TechnicalSupport.tsx index 15f1b7aca..61b2c53c3 100644 --- a/src/screens/KeeperConcierge/TechnicalSupport.tsx +++ b/src/screens/KeeperConcierge/TechnicalSupport.tsx @@ -2,7 +2,7 @@ import { Box, useColorMode } from 'native-base'; import React, { useContext, useEffect, useState } from 'react'; import ConciergeScreenWrapper from './components/ConciergeScreenWrapper'; 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'; @@ -33,6 +33,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'; type ScreenProps = NativeStackScreenProps; const TechnicalSupport = ({ route }: ScreenProps) => { @@ -143,6 +147,24 @@ const TechnicalSupport = ({ route }: ScreenProps) => { loading={loading || conciergeLoading} wrapperStyle={{ paddingTop: hp(0) }} > + + { + navigation.goBack(); + }} + style={styles.backButton} + > + + + } + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + Keeper Support + + {isKeeperPrivate ? ( accountManagerDetails ? ( @@ -195,6 +217,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 && ( - + )} From 1cdbbf35e21423a89926b7909ba95093a02fbc82 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 18 Jul 2025 12:44:32 +0500 Subject: [PATCH 16/80] dark and private mode --- src/assets/images/add-profile-dark.svg | 5 ++ src/assets/images/edit-profile-dark.svg | 12 ++++ src/assets/images/placeholder-image-dark.png | Bin 0 -> 3529 bytes .../Contact-footor-gold-icon.svg | 9 +++ .../privateImages/acquire-header-white.svg | 3 + src/assets/privateImages/add-profile-gold.svg | 11 ++++ .../contact-header-gold-icon.svg | 34 ++++++++++ .../privateImages/contact-header-white.svg | 8 +++ .../privateImages/edit-profile-gold.svg | 16 +++++ src/assets/privateImages/share-gold-icon.svg | 9 +++ src/components/ThemedSvg.tsx/ThemedIcons.js | 35 ++++++++-- src/navigation/privateTheme.js | 2 +- src/navigation/themes.js | 2 +- src/screens/Concierge/KeeperSupport.tsx | 2 +- .../components/Contact/ChatRoomScreen.tsx | 21 +++--- .../components/Contact/component/ChatList.tsx | 2 +- .../components/Contact/component/ChatRoom.tsx | 2 - .../Contact/component/ChatRoomHeader.tsx | 61 ++++++++---------- .../Contact/component/ContactModalData.tsx | 2 +- .../Contact/component/EditModalContent.tsx | 50 -------------- .../Contact/component/ProfileContent.tsx | 9 ++- src/theme/Colors.ts | 1 + 22 files changed, 186 insertions(+), 110 deletions(-) create mode 100644 src/assets/images/add-profile-dark.svg create mode 100644 src/assets/images/edit-profile-dark.svg create mode 100644 src/assets/images/placeholder-image-dark.png create mode 100644 src/assets/privateImages/Contact-footor-gold-icon.svg create mode 100644 src/assets/privateImages/acquire-header-white.svg create mode 100644 src/assets/privateImages/add-profile-gold.svg create mode 100644 src/assets/privateImages/contact-header-gold-icon.svg create mode 100644 src/assets/privateImages/contact-header-white.svg create mode 100644 src/assets/privateImages/edit-profile-gold.svg create mode 100644 src/assets/privateImages/share-gold-icon.svg delete mode 100644 src/screens/Home/components/Contact/component/EditModalContent.tsx 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/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/placeholder-image-dark.png b/src/assets/images/placeholder-image-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..448ebcf952d3e901dd40754ca2167c0d162063e1 GIT binary patch literal 3529 zcmV;)4L0(LP)LjaSL&KDkLHz1}}s}XoQwG_zQmg1^5f_7d-VwFG|~p2yZ&P&`PLz z5TZ#rq)1%ql%iU#-})Zs^qEd)I%m$D%id?sCpn!?r=8Bs{_eHbUVHDg$5B9OY;08e z`}@m(|NgCv8#it-Us3+Cg8O(J_xby1nm<*I$5nn@!+pMeHGcf~1nwU`d^kY?WE=$m zH2T26K#UveZ@wGjMpQC1vLo>G$NBjoh}%O$Ly03tj;JI#QVM|^KKkp|uX1jL{e1TT zUkdq)YKWtG!h{L&ii!%2Btse@n2paL_A$PDz|9`-08K26W_BahBniR`;qv9nG4y=? zV2=yl_@m(049n2O*9FmI3t@!-0dT{g<|{_QAi=`8AgYxRP6+k&_2~Jh$$!7^7KkU# ziY5qQgirtiNJS_?_%4J1x`12}LJ+Y z1l;CB1kmIWo>+vsx;n%HCn>}Z=PN5KPm#;GDg+}SY~*jtDI_0UcNMO=1~zNuEP@sa zMc@SJP%ZdO$t7G70&)X<0}&_`;uM%#Z(%p<%5d9pAs{GZGu9Fg4P61BX=80|t>f{Q zqe4J_fRiHC6yhqJd^*Qg_cS?(W5FUsz*gWqg?I+X4QHH6J~<);xP#CV_{MP_US6a; z$y*4t8aC9IBSeoC6HX)nX7J`v6 zR47Cg^eiqG`H~H63Bee;gc(EeDDUs@w>7NHIYuy+l>I{?!ouxys;a6gr=fgKLNMb3 z3SENeXgreJkjo@hPObrqN+=Y83`C8#vW#UR7@5Nug-8L~DwjD4ORgbw3DRQOMOd;3 zY-S2+zd?|&>>{#SgfN9HKgQ~&_*lP_)gthdghNHiVYi;O4aXFMv5Hoq5ZO_(t%x2| z2*zH-kN|+T94q~tQ3#YNI{!f<@brLhG|pJX-5q~n@@QA_ANbm@qX2-0fAHXeertnhXJ;qjDl02fckkY45d^wOnE3jT-8uvHkfNoJG~WS&?uMSy_N&Hr)r>Q#+zrK_cA!a_KfI+PqC z1YD76*Vh+iF_6TTAqGScB)!pOfDpVa2^m=vvF^NRA10As9Pk zq!WT0xdOYix3|lfhE~koQMwQ~lPKk8m3QynktlR@bVxZL!J~&5ZgnBZm_&kGzzm`& za9TSmqkG_Vvu*@Ti|{u|h9ASlY;<*XNoN%}PS}oHGgy?)HHavL?(S|GlS>M|L{PF# z#{5AJ>k2%hx3^cuI3cRfI2*^{Nk@NwKZ%Bj;?j{Mm^W{p zDDFXBxpJjS@*=ko;2zelT}ytmapOjnB;V^GwGhCjO`CijO5%U>YfwmXq!$8+#bV?a zd-m*!Qvi022SNyl6}D{ILS6%F6ombd076hH0IXlX9=@8qf@2fa%m3#xY9K6v33{j( zjRO&E-MUqw08zx#&?+)Z6c8W=;Li~63K|&J9X)zf-}eCPU{??jz=jPQ6c(03!9_*I6qN!D zECViVLh0z}(APR(kT5Jn z?qKfRxsI(>!1JSw@5`4j^-%|&fAQkQvJW*?4iuN*o8c0?Y$BSC@~l!}HV}$tBuzmr zB>~YUPMnxo2o)6-I5|`ptAK`gtK_~{JbLtq8X6je5;1r7CbV_(&(f9YZz2IOMIeJK z$Ofjo;nqZ{=FSPkQJp>0nNGe2jiv}hESQxK2p$g}Jm_~4YB-6MOxlGA=@jE7K@dA^k!rt!Mg&+v6oCoen~wpcindMnK*ps>>>4hR$Kc!rnFB+s zU?}Xz`^;nwKt>^O*6F;LR>ALc7rX|R&*MNiqcs2-g@ECOmsSD3v(WG|H!_wr0AsFN z<>>MZd9d6aE z^1QXv6tW0RTpmRM!TC*Nqic;gpLAMKBs{(sc@) z2%pRE=OhG}87mQ}6`Xw2K>?k^iH^o@{FGIfV3PCh*Q%I95*0ju-&XJgS^kLoX=EiY;u?6Ne5RQm0Lu zb|BZkbxH*K>mUcQD~6t)9`(kJ8@N@dfLOS2Vbk8ddlj;A60egY0FD<}v?~TiOC%EN zy?ggy859aI6DLkg?Ao{}>iCedBX#oi)wWUj!Cbn(c7G-N->*QVJ zYz7AhF+_}$3%FW)S3i6@5^144Og#kA+S(d@{ra^+@{zXy4D@7CK6!#{xL$<0v9U4A zIdtT{k5L|c`SJxZ1U-B94EYCz1O_nxoBy6QYu4bDU4`-hdr|rMPTI{FTE(+!+<63% zsi>TWjq>cJCYY7bd;hw1>!Ne#&Q~9sDssJ5-fxh`DP1q!I%2{4qAs1eE9G|<4YF>yOOBGD}J=H zvNCP{{Q2nd8V~&-1ce;NjiI3xd=UaLtOG_B`2fN;$J zPd>wfIBr-6g$fhZumaWOBf=crVkDtZ-cO+dz{m?+VE1y~?Gb^c*FypUcfmKX5EyDI z6hK51fQe8-NG5{|?{oMF*jsY&Xl^S*m z;e}vgxQ*EO@#Bw)Toe>w1i+2oENbshhcrS+#uNoxiv9ed$NVlEY%Jn!V&8uo^$TTb z{A8pQLMoW);Gjj#=Xn{x z$sfaVSXua@;)`( + + + + + + + + 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/ThemedSvg.tsx/ThemedIcons.js b/src/components/ThemedSvg.tsx/ThemedIcons.js index ebaf12c4b..994efa005 100644 --- a/src/components/ThemedSvg.tsx/ThemedIcons.js +++ b/src/components/ThemedSvg.tsx/ThemedIcons.js @@ -322,25 +322,48 @@ 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'; 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: ShareLight, + PRIVATE_LIGHT: ShareGold, }, contact_footer: { DARK: ContactFooterDark, LIGHT: ContactFooterLight, - PRIVATE: ContactFooterDark, - PRIVATE_LIGHT: ContactFooterLight, + PRIVATE: ContactFooterGold, + PRIVATE_LIGHT: ContactFooterGold, }, contact_header: { DARK: ContactHeaderDark, LIGHT: ContactHeaderLight, - PRIVATE: ContactHeaderDark, - PRIVATE_LIGHT: ContactHeaderLight, + PRIVATE: ContactHeaderWhite, + PRIVATE_LIGHT: ContactHeaderGold, }, acquire_send_arrow: { DARK: BuyAcquireArrowWhite, @@ -1110,7 +1133,7 @@ const themeIcons = { header_buy_btc: { DARK: BtcLogo, LIGHT: BtcLogo, - PRIVATE: BtcLogoGold, + PRIVATE: AquireHeadrWhite, PRIVATE_LIGHT: BtcLogoGold, }, footer_buy_btc: { diff --git a/src/navigation/privateTheme.js b/src/navigation/privateTheme.js index be5afd48f..eecab78fe 100644 --- a/src/navigation/privateTheme.js +++ b/src/navigation/privateTheme.js @@ -302,7 +302,7 @@ export const privateTheme = extendTheme({ activationFeeText: Colors.bodyText, greybeige: Colors.secondaryBlack, AcquireText: Colors.bodyText, - stoneGrey: Colors.stoneGrey, + stoneGrey: Colors.lightstone, coolGrey: Colors.bodyText, subchatText: Colors.primaryCream, SlateWhite: Colors.primaryCream, diff --git a/src/navigation/themes.js b/src/navigation/themes.js index 8915490fe..bd04ecfe8 100644 --- a/src/navigation/themes.js +++ b/src/navigation/themes.js @@ -298,7 +298,7 @@ export const customTheme = extendTheme({ activationFeeText: Colors.bodyText, greybeige: Colors.secondaryBlack, AcquireText: Colors.bodyText, - stoneGrey: Colors.stoneGrey, + stoneGrey: Colors.lightstone, coolGrey: Colors.bodyText, subchatText: Colors.primaryCream, SlateWhite: Colors.primaryCream, diff --git a/src/screens/Concierge/KeeperSupport.tsx b/src/screens/Concierge/KeeperSupport.tsx index 6b5d50ab9..7325d0da9 100644 --- a/src/screens/Concierge/KeeperSupport.tsx +++ b/src/screens/Concierge/KeeperSupport.tsx @@ -25,6 +25,6 @@ export default KeeperSupport; const styles = StyleSheet.create({ container: { flex: 1, - paddingTop: wp(20), + paddingTop: wp(10), }, }); diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx index aaa4ce1a2..432121ab1 100644 --- a/src/screens/Home/components/Contact/ChatRoomScreen.tsx +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -4,8 +4,8 @@ import { StyleSheet } from 'react-native'; import { RouteProp, useRoute } from '@react-navigation/native'; import KeeperModal from 'src/components/KeeperModal'; import ChatRoomHeader from './component/ChatRoomHeader'; -import EditModalContent from './component/EditModalContent'; import ChatRoom from './component/ChatRoom'; +import ProfileContent from './component/ProfileContent'; type ChatRoomParams = { ChatRoomScreen: { @@ -21,15 +21,16 @@ const ChatRoomScreen = () => { const { receiverProfileImage, receiverProfileName, userProfileImage } = route.params; const [editReceiverProfileName, setEditReceiverProfileName] = useState(receiverProfileName); const [openEditModal, setOpenEditModal] = useState(false); + const [editUserProfileImage, setEditUserProfileImage] = useState(receiverProfileImage); return ( - + setOpenEditModal(false)} @@ -37,11 +38,15 @@ const ChatRoomScreen = () => { textColor={`${colorMode}.textGreen`} modalBackground={`${colorMode}.modalWhiteBackground`} Content={() => ( - + <> + + )} /> diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index 4ddbf9ac2..8194cab65 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -14,7 +14,7 @@ const chatData = [ name: 'John Doe', lastMessage: 'Hey! Are you available tomorrow?', image: - 'file:///Users/mac/Library/Developer/CoreSimulator/Devices/2C667219-79A1-4604-9AC8-183891E43BA9/data/Containers/Data/Application/9389AA84-812B-4523-BA70-4581CEE2193E/tmp/BC7D0137-3ABA-42DF-9EC4-F0F6556B70DD.jpg', + 'file:///Users/mac/Library/Developer/CoreSimulator/Devices/2C667219-79A1-4604-9AC8-183891E43BA9/data/Containers/Data/Application/FBE5364F-9652-4CBB-A5A0-649007152F46/tmp/A3814890-5695-4D50-A53B-420A052A904C.jpg', date: '2025-07-17T12:00:00', message_count: 2, }, diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index 970e29762..24897ac65 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -212,7 +212,6 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage }) => { }, styles.messageContent, ]} - borderColor={`${colorMode}.separator`} > {msg.text} @@ -361,7 +360,6 @@ const styles = StyleSheet.create({ paddingVertical: wp(10), paddingHorizontal: wp(14), borderRadius: 12, - borderWidth: 1, }, seperator: { height: 30, diff --git a/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx index e7e291570..58c582f0d 100644 --- a/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx @@ -2,21 +2,15 @@ import { useNavigation } from '@react-navigation/native'; import { Box, Image, useColorMode } from 'native-base'; import React from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; -import { useSelector } from 'react-redux'; -import BackBlackButton from 'src/assets/images/header-arrow-icon.svg'; -import BackWhiteButton from 'src/assets/images/leftarrowCampainlight.svg'; -import PrivateBackButton from 'src/assets/privateImages/gold-back-arrow.svg'; import Text from 'src/components/KeeperText'; import { hp, wp } from 'src/constants/responsive'; -// import EditIcon from 'src/assets/images/edit-pencil-icon.svg'; import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png'; import { StatusBar } from 'react-native'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEditModal }) => { const navigation = useNavigation(); const { colorMode } = useColorMode(); - const themeMode = useSelector((state: any) => state?.settings?.themeMode); - const privateTheme = themeMode === 'PRIVATE'; return ( @@ -28,34 +22,34 @@ const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEdit > - {privateTheme ? ( - - ) : colorMode === 'light' ? ( - - ) : ( - - )} + - - {receiverProfileImage ? ( - profileImage - ) : ( - placeHolder - )} - + setOpenEditModal(true)} + > + + {receiverProfileImage ? ( + profileImage + ) : ( + placeHolder + )} + - - - {receiverProfileName} - - - setOpenEditModal(true)}> - {/* */} + + + {receiverProfileName} + + @@ -92,7 +86,6 @@ const styles = StyleSheet.create({ }, profile_image_container: { flexDirection: 'row', - gap: wp(10), alignItems: 'center', justifyContent: 'center', }, diff --git a/src/screens/Home/components/Contact/component/ContactModalData.tsx b/src/screens/Home/components/Contact/component/ContactModalData.tsx index b77b93531..bce825d01 100644 --- a/src/screens/Home/components/Contact/component/ContactModalData.tsx +++ b/src/screens/Home/components/Contact/component/ContactModalData.tsx @@ -155,7 +155,7 @@ function ContactModalData({ isShareContact = false, setContactModalVisible, navi diff --git a/src/screens/Home/components/Contact/component/EditModalContent.tsx b/src/screens/Home/components/Contact/component/EditModalContent.tsx deleted file mode 100644 index 3ae34c10b..000000000 --- a/src/screens/Home/components/Contact/component/EditModalContent.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Box, useColorMode } from 'native-base'; -import React, { useEffect, useState } from 'react'; -import { StyleSheet } from 'react-native'; -import Buttons from 'src/components/Buttons'; -import KeeperTextInput from 'src/components/KeeperTextInput'; - -const EditModalContent = ({ - editReceiverProfileName, - setEditReceiverProfileName, - setOpenEditModal, -}) => { - const { colorMode } = useColorMode(); - const [editName, setEditName] = useState(''); - - useEffect(() => { - setEditName(editReceiverProfileName); - }, [editReceiverProfileName]); - - const handleEditName = () => { - setEditReceiverProfileName(editName); - setEditName(''); - setOpenEditModal(false); - }; - return ( - - - { - handleEditName(); - }} - /> - - ); -}; - -export default EditModalContent; -const styles = StyleSheet.create({ - container: { - gap: 10, - }, -}); diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx index eaef010e2..cbfb76a9c 100644 --- a/src/screens/Home/components/Contact/component/ProfileContent.tsx +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -3,12 +3,11 @@ 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-whote-image.png'; +import PlaceholderWhiteImage from 'src/assets/images/placeholder-image-dark.png'; import { useEffect, useState } from 'react'; -import AddImageIcon from 'src/assets/images/add-image-icon.svg'; import { launchImageLibrary } from 'react-native-image-picker'; import Buttons from 'src/components/Buttons'; -import ChangeProfileImageIcon from 'src/assets/images/change-profile-icon.svg'; +import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; const ProfileContent = ({ setUserProfileImage, @@ -78,9 +77,9 @@ const ProfileContent = ({ )} {profileImage ? ( - + ) : ( - + )} diff --git a/src/theme/Colors.ts b/src/theme/Colors.ts index 02826c186..d11d679ca 100644 --- a/src/theme/Colors.ts +++ b/src/theme/Colors.ts @@ -155,5 +155,6 @@ const Colors = { 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)', }; export default Colors; From f2ededc630e84bf72d1245adca89ace3ace0a3c7 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 18 Jul 2025 16:12:45 +0500 Subject: [PATCH 17/80] localization --- src/context/Localization/language/en.json | 18 ++++++++++++++- src/context/Localization/language/es.json | 18 ++++++++++++++- .../components/Contact/ChatRoomScreen.tsx | 7 ++++-- .../Home/components/Contact/ContactShowQr.tsx | 10 ++++---- .../components/Contact/component/ChatList.tsx | 10 ++++---- .../components/Contact/component/Contact.tsx | 23 +++++++++++-------- .../Contact/component/ContactHeader.tsx | 11 +++++---- .../Contact/component/ProfileContent.tsx | 9 +++++--- 8 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index c20f1d819..6810e7e43 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1981,6 +1981,22 @@ "importInfoDescNote": "Note: Standard TRC-20 wallets that require TRX to move funds are not supported." }, "contactText": { - "contacts": "Contacts" + "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, advice and the latest bitcoin updates." } } diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index c20f1d819..6810e7e43 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1981,6 +1981,22 @@ "importInfoDescNote": "Note: Standard TRC-20 wallets that require TRX to move funds are not supported." }, "contactText": { - "contacts": "Contacts" + "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, advice and the latest bitcoin updates." } } diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx index 432121ab1..00e535f6f 100644 --- a/src/screens/Home/components/Contact/ChatRoomScreen.tsx +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { Box, useColorMode } from 'native-base'; import { StyleSheet } from 'react-native'; import { RouteProp, useRoute } from '@react-navigation/native'; @@ -6,6 +6,7 @@ import KeeperModal from 'src/components/KeeperModal'; import ChatRoomHeader from './component/ChatRoomHeader'; import ChatRoom from './component/ChatRoom'; import ProfileContent from './component/ProfileContent'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; type ChatRoomParams = { ChatRoomScreen: { @@ -22,6 +23,8 @@ const ChatRoomScreen = () => { const [editReceiverProfileName, setEditReceiverProfileName] = useState(receiverProfileName); const [openEditModal, setOpenEditModal] = useState(false); const [editUserProfileImage, setEditUserProfileImage] = useState(receiverProfileImage); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; return ( @@ -34,7 +37,7 @@ const ChatRoomScreen = () => { setOpenEditModal(false)} - title="Edit Contact Name" + title={contactText?.editContactName} textColor={`${colorMode}.textGreen`} modalBackground={`${colorMode}.modalWhiteBackground`} Content={() => ( diff --git a/src/screens/Home/components/Contact/ContactShowQr.tsx b/src/screens/Home/components/Contact/ContactShowQr.tsx index 867574781..1d6ba2c0e 100644 --- a/src/screens/Home/components/Contact/ContactShowQr.tsx +++ b/src/screens/Home/components/Contact/ContactShowQr.tsx @@ -1,21 +1,21 @@ import { Box, useColorMode } from 'native-base'; import { authenticator } from 'otplib'; -import React from 'react'; +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'; const ContactShareQr = () => { const validationKey = 'Bitcoin Keeper'; //dummy key, replace with actual key const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { vault: vaultText, settings } = translations; return ( - + { const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; return ( - No Contacts Yet + {contactText.noContactYet} - Please add your trusted contacts to send/receive sats, advice and the latest bitcoin - updates. + {contactText.noContactDesc} ); diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 00283d594..6d3881e9d 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,5 +1,5 @@ import { Box, ScrollView, useColorMode } from 'native-base'; -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import Text from 'src/components/KeeperText'; import { wp } from 'src/constants/responsive'; @@ -15,6 +15,7 @@ import KeeperModal from 'src/components/KeeperModal'; import ProfileContent from './ProfileContent'; import { useNavigation } from '@react-navigation/native'; import ContactModalData from './ContactModalData'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; const Contact = () => { const { colorMode } = useColorMode(); @@ -25,7 +26,8 @@ const Contact = () => { const navigation = useNavigation(); const [contactmodalVisible, setContactModalVisible] = useState(false); const [shareContact, setShareContact] = useState(false); - + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; return ( { /> - Recent Chats + {contactText.recentChats} @@ -56,14 +58,15 @@ const Contact = () => { /> - Keeper Support + {contactText.keeperSupport} - We're here to help. + {contactText.hereToHelp} + {/* place a time here */} 10:45 AM @@ -75,7 +78,7 @@ const Contact = () => { { setContactModalVisible(true); setShareContact(false); @@ -87,8 +90,8 @@ const Contact = () => { setCreateProfile(false)} - title="Edit Your Profile" - subTitle="Add a name/nym and an optional photo." + title={contactText.editProfile} + subTitle={contactText.editProfileDesc} textColor={`${colorMode}.textGreen`} subTitleColor={`${colorMode}.modalSubtitleBlack`} modalBackground={`${colorMode}.modalWhiteBackground`} @@ -105,8 +108,8 @@ const Contact = () => { setContactModalVisible(false)} - title={shareContact ? 'Share Contact Info' : 'Add a New Contact'} - subTitle="Choose how to add the contact" + title={shareContact ? contactText.shareContact : contactText.addNewContact} + subTitle={contactText.chooseHowToAdd} textColor={`${colorMode}.textGreen`} subTitleColor={`${colorMode}.modalSubtitleBlack`} modalBackground={`${colorMode}.modalWhiteBackground`} diff --git a/src/screens/Home/components/Contact/component/ContactHeader.tsx b/src/screens/Home/components/Contact/component/ContactHeader.tsx index f31440d0b..1c0443a02 100644 --- a/src/screens/Home/components/Contact/component/ContactHeader.tsx +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -1,11 +1,12 @@ import { Box, Image, useColorMode } from 'native-base'; -import React from 'react'; +import React, { useContext } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import Text from 'src/components/KeeperText'; import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; import EditIcon from 'src/assets/images/contact-edit.svg'; import { wp } from 'src/constants/responsive'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; type Props = { userProfileImage: string | null; @@ -23,6 +24,8 @@ const ContactHeader = ({ setShareContact, }: Props) => { const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { contactText } = translations; return ( @@ -44,11 +47,11 @@ const ContactHeader = ({ )} - {userProfileName || 'Name'} + {userProfileName || contactText.name} {!userProfileName && ( - For easy identification  + {contactText.easyIdentification} )} @@ -75,7 +78,7 @@ const ContactHeader = ({ > - Share + {contactText.share} diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx index cbfb76a9c..393582c1d 100644 --- a/src/screens/Home/components/Contact/component/ProfileContent.tsx +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -4,10 +4,11 @@ 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 { useEffect, useState } from 'react'; +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'; const ProfileContent = ({ setUserProfileImage, @@ -20,6 +21,8 @@ const ProfileContent = ({ const isDarkMode = colorMode === 'dark'; const [profileImage, setProfileImage] = useState(null); const [profileName, setProfileName] = useState(''); + const { translations } = useContext(LocalizationContext); + const { contactText, common } = translations; useEffect(() => { if (userProfileImage) { @@ -86,14 +89,14 @@ const ProfileContent = ({ setProfileName(text)} backgroundColor={`${colorMode}.primaryBackground`} /> Date: Fri, 18 Jul 2025 17:15:14 +0500 Subject: [PATCH 18/80] remove image --- src/screens/Home/components/Contact/component/ChatList.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index b3b1a3d1d..ee0b6ed36 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -14,8 +14,7 @@ const chatData = [ id: '1', name: 'John Doe', lastMessage: 'Hey! Are you available tomorrow?', - image: - 'file:///Users/mac/Library/Developer/CoreSimulator/Devices/2C667219-79A1-4604-9AC8-183891E43BA9/data/Containers/Data/Application/FBE5364F-9652-4CBB-A5A0-649007152F46/tmp/A3814890-5695-4D50-A53B-420A052A904C.jpg', + image: '', date: '2025-07-17T12:00:00', message_count: 2, }, From 80325b2dd323c2dbeca78313b2b69ab1f84e9a3c Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 18 Jul 2025 17:21:21 +0500 Subject: [PATCH 19/80] fix loader --- src/screens/KeeperConcierge/TechnicalSupport.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/screens/KeeperConcierge/TechnicalSupport.tsx b/src/screens/KeeperConcierge/TechnicalSupport.tsx index 61b2c53c3..04070b7bc 100644 --- a/src/screens/KeeperConcierge/TechnicalSupport.tsx +++ b/src/screens/KeeperConcierge/TechnicalSupport.tsx @@ -37,6 +37,7 @@ 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; const TechnicalSupport = ({ route }: ScreenProps) => { @@ -142,11 +143,8 @@ const TechnicalSupport = ({ route }: ScreenProps) => { }; return ( - + <> + { @@ -189,7 +187,7 @@ const TechnicalSupport = ({ route }: ScreenProps) => { textColor={`${colorMode}.modalWhiteContent`} Content={() => OnboardCallContent({ submitOnboardEmail })} /> - + ); }; From c655d15d227180b00a390dbe182d4ca1722b4e3d Mon Sep 17 00:00:00 2001 From: Parsh Date: Mon, 21 Jul 2025 11:56:56 +0200 Subject: [PATCH 20/80] adds: key fetcher --- src/hooks/useChatPeer.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index de58bc045..45aff4788 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -32,6 +32,9 @@ export interface UseChatPeerReturn { // Data fetching getPeers: () => Promise; + getKeys: () => { + [key: string]: string; + }; getPeerMessages: (pubKey: string, lastBlock: number) => Promise; // Contact management @@ -117,7 +120,7 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn secretKey: keys.secretKey, }, }); - console.log('KeeperApp updated with contacts key'); + console.log('Database updated with peer keys'); } } catch (keyError) { console.warn('Failed to get or update contacts key:', keyError); @@ -185,6 +188,23 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn } }, []); + // Get keys + 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 { @@ -349,6 +369,7 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn // Data fetching getPeers, + getKeys, getPeerMessages, // Contact management From 9c8466f3420e5ac91472c5d772c5c845ae716348 Mon Sep 17 00:00:00 2001 From: Parsh Date: Mon, 21 Jul 2025 11:57:27 +0200 Subject: [PATCH 21/80] integrates: peer initialization --- .../components/Contact/component/Contact.tsx | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 6d3881e9d..2c72ab81c 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,5 +1,5 @@ import { Box, ScrollView, useColorMode } from 'native-base'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import Text from 'src/components/KeeperText'; import { wp } from 'src/constants/responsive'; @@ -16,6 +16,10 @@ import ProfileContent from './ProfileContent'; import { useNavigation } from '@react-navigation/native'; import ContactModalData from './ContactModalData'; import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { useChatPeer } from 'src/hooks/useChatPeer'; +import useToastMessage from 'src/hooks/useToastMessage'; +import ToastErrorIcon from 'src/assets/images/toast_error.svg'; +import idx from 'idx'; const Contact = () => { const { colorMode } = useColorMode(); @@ -26,8 +30,33 @@ const Contact = () => { const navigation = useNavigation(); const [contactmodalVisible, setContactModalVisible] = useState(false); const [shareContact, setShareContact] = useState(false); + const [shareContactData, setShareContactData] = useState(''); const { translations } = useContext(LocalizationContext); const { contactText } = translations; + + const { showToast } = useToastMessage(); + const chatPeer = useChatPeer(); + + const initializeChat = async () => { + try { + const chatPeerInitialized = await chatPeer.initChatPeer(); + if (!chatPeerInitialized) { + throw new Error(); + } + + const keys = await chatPeer.getKeys(); + setShareContactData(idx(keys, (_) => _.publicKey)); + } catch (error) { + console.error('Error initializing chat peer:', error); + showToast('Chat Peer initialization failed', ); + return; + } + }; + + useEffect(() => { + if (!chatPeer.isInitialized) initializeChat(); + }, [chatPeer.isInitialized]); + return ( { isShareContact={shareContact} setContactModalVisible={setContactModalVisible} navigation={navigation} + data={shareContactData} /> )} /> From e53190ae3d8e79e9919d590a5c724ff83fa55e6c Mon Sep 17 00:00:00 2001 From: Parsh Date: Mon, 21 Jul 2025 12:47:00 +0200 Subject: [PATCH 22/80] fixes: Contacts QR --- src/screens/Home/components/Contact/ContactShowQr.tsx | 7 ++++--- .../Home/components/Contact/component/ContactModalData.tsx | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/screens/Home/components/Contact/ContactShowQr.tsx b/src/screens/Home/components/Contact/ContactShowQr.tsx index 1d6ba2c0e..21ef77b92 100644 --- a/src/screens/Home/components/Contact/ContactShowQr.tsx +++ b/src/screens/Home/components/Contact/ContactShowQr.tsx @@ -8,18 +8,19 @@ import WalletHeader from 'src/components/WalletHeader'; import { windowWidth } from 'src/constants/responsive'; import { LocalizationContext } from 'src/context/Localization/LocContext'; -const ContactShareQr = () => { - const validationKey = 'Bitcoin Keeper'; //dummy key, replace with actual key +const ContactShareQr = ({ route }) => { const { colorMode } = useColorMode(); const { translations } = useContext(LocalizationContext); const { vault: vaultText, settings } = translations; + const { data } = route.params; + return ( { setNfcVisible(false); Vibration.cancel(); @@ -109,7 +107,7 @@ function ContactModalData({ isShareContact = false, setContactModalVisible, navi icon: , onPress: () => { setContactModalVisible(false); - navigation.navigate('ContactShareQr'); + navigation.navigate('ContactShareQr', { data }); }, }, { From bfdb51d8d12c220e8dd7a1aa6c412f6121a09a21 Mon Sep 17 00:00:00 2001 From: Parsh Date: Mon, 21 Jul 2025 15:08:22 +0200 Subject: [PATCH 23/80] integrates: Contact's chat list --- .../components/Contact/component/ChatList.tsx | 130 +++++++++++++----- 1 file changed, 96 insertions(+), 34 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index ee0b6ed36..d12b82b7e 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -1,42 +1,25 @@ -import React, { useContext } from 'react'; +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 { hp, wp } from 'src/constants/responsive'; +import { wp } from 'src/constants/responsive'; import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png'; import { useNavigation } from '@react-navigation/native'; import Fonts from 'src/constants/Fonts'; import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { useChatPeer } from 'src/hooks/useChatPeer'; +import { CommunityType } from 'src/services/p2p/interface'; -// dummy chat data -const chatData = [ - { - id: '1', - name: 'John Doe', - lastMessage: 'Hey! Are you available tomorrow?', - image: '', - date: '2025-07-17T12:00:00', - message_count: 2, - }, - { - id: '2', - name: 'Jane Smith', - lastMessage: 'Let’s catch up soon!', - image: '', - date: '2023-10-01T12:45:00', - message_count: 6, - }, - { - id: '3', - name: 'Bob Johnson', - lastMessage: 'I sent the documents.', - image: '', - date: '2025-07-16T12:00:00', - message_count: 0, - }, -]; - -const chatDatas = []; +interface ChatItemData { + id: string; + name: string; + lastMessage: string; + image: string; + date: string; + message_count: number; + communityId: string; + contactKey?: string; +} const PlaceHolderChat = () => { const { colorMode } = useColorMode(); @@ -54,7 +37,7 @@ const PlaceHolderChat = () => { ); }; -const ChatItem = ({ item, userProfileImage }) => { +const ChatItem = ({ item, userProfileImage }: { item: ChatItemData; userProfileImage: any }) => { const { colorMode } = useColorMode(); const navigation = useNavigation(); const formatTime = (dateString) => { @@ -67,6 +50,8 @@ const ChatItem = ({ item, userProfileImage }) => { receiverProfileImage: item.image, receiverProfileName: item.name, userProfileImage, + communityId: item.communityId, + contactKey: item.contactKey, }); }; @@ -109,10 +94,87 @@ const ChatItem = ({ item, userProfileImage }) => { ); }; -const ChatList = ({ userProfileImage }) => { +const ChatList = ({ userProfileImage }: { userProfileImage: any }) => { const { colorMode } = useColorMode(); + const { communities, 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 + ); - const renderItem = ({ item }) => ; + // 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 ( From 40faf290566f617dab1e334671792ad9b78465ad Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 12:37:38 +0530 Subject: [PATCH 24/80] change minSdk support to 28 to support bare-kit on android --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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' From 24f712652d13a220176785e619e4c29b6fc2967a Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 12:39:30 +0530 Subject: [PATCH 25/80] init chat peer when creating a new app and store contactKey in db --- src/services/backend/Relay.ts | 4 +++- src/store/sagas/storage.ts | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index a5da599dc..2f77559ed 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -257,7 +257,8 @@ export default class Relay { public static createNewApp = async ( publicId: string, appID: string, - fcmToken: string + fcmToken: string, + contactsKey?: string ): Promise<{ created: boolean; }> => { @@ -267,6 +268,7 @@ export default class Relay { appID, publicId, fcmToken, + contactsKey, }); } catch (err) { console.log('err', err); diff --git a/src/store/sagas/storage.ts b/src/store/sagas/storage.ts index 8522bf7d6..dc1c6562f 100644 --- a/src/store/sagas/storage.ts +++ b/src/store/sagas/storage.ts @@ -41,6 +41,7 @@ import { } from '../reducers/account'; import { loadConciergeTickets, loadConciergeUser } from '../reducers/concierge'; import LoginMethod from 'src/models/enums/LoginMethod'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; export function* setupKeeperAppWorker({ payload }) { try { @@ -70,7 +71,16 @@ export function* setupKeeperAppWorker({ payload }) { ); imageEncryptionKey = generateEncryptionKey(entropy.toString('hex')); } - const response = yield call(Relay.createNewApp, publicId, appID, fcmToken); + const cm = ChatPeerManager.getInstance(); + yield call([cm, cm.init], primarySeed.toString('hex')); + const contactsKey = yield call([cm, cm.getKeys]); + const response = yield call( + Relay.createNewApp, + publicId, + appID, + fcmToken, + contactsKey.publicKey + ); if (response && response.created) { const newAPP: KeeperApp = { @@ -90,6 +100,7 @@ export function* setupKeeperAppWorker({ payload }) { version: DeviceInfo.getVersion(), networkType: bitcoinNetworkType, enableAnalytics: false, + contactsKey, }; yield call(dbManager.createObject, RealmSchema.KeeperApp, newAPP); From 7dcc61683a886a8c3d672daca7c3f4d7904414b5 Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 12:41:39 +0530 Subject: [PATCH 26/80] feat: update app's contact name --- .../Contact/component/ProfileContent.tsx | 27 ++++++++-- src/services/backend/Relay.ts | 52 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx index 393582c1d..461105e82 100644 --- a/src/screens/Home/components/Contact/component/ProfileContent.tsx +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -9,6 +9,10 @@ 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 Relay from 'src/services/backend/Relay'; +import { RealmSchema } from 'src/storage/realm/enum'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import dbManager from 'src/storage/realm/dbManager'; const ProfileContent = ({ setUserProfileImage, @@ -23,6 +27,7 @@ const ProfileContent = ({ const [profileName, setProfileName] = useState(''); const { translations } = useContext(LocalizationContext); const { contactText, common } = translations; + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); useEffect(() => { if (userProfileImage) { @@ -50,10 +55,24 @@ const ProfileContent = ({ } ); }; - const handleConfirm = () => { - setUserProfileImage(profileImage); - setUserProfileName(profileName); - setCreateProfile(false); + + const handleConfirm = async () => { + try { + const response = await Relay.updateAppProfile(profileName, profileImage, app.id); + if (response.updated) { + dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { + appName: profileName, + }); + dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { + image: profileImage, + }); + setUserProfileImage(profileImage); + setUserProfileName(profileName); + setCreateProfile(false); + } + } catch (error) { + console.log('Error in handleConfirm', error); + } }; return ( diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index 2f77559ed..5e0c6166a 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -281,6 +281,58 @@ export default class Relay { }; }; + public static getAppProfiles = async ( + publicIds: string[] + ): Promise<{ + profiles: { + contactsKey: string; + name: string; + image: string; + }[]; + }> => { + let res; + try { + res = await RestClient.post(`${RELAY}getAppProfiles`, { + publicIds, + }); + } 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 { profiles } = res.data || res.json; + return { + profiles, + }; + }; + + public static updateAppProfile = async ( + appName: string, + image: any, + id: string + ): Promise => { + let res; + try { + const formData = new FormData(); + formData.append('appName', appName); + if (image) { + formData.append('image', image); + } + formData.append('id', id); + console.log(`${RELAY}updateAppProfile`); + res = await RestClient.post(`${RELAY}updateAppProfile`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + } catch (err) { + console.log('err', err); + if (err.response) throw new Error(err.response.data.err); + if (err.code) throw new Error(err.code); + } + return res.data || res.json; + }; + public static modifyLabels = async ( appId: string, addLabels: any[], From 00e04f61edc24d96708746536840b84ff738cf6e Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 12:42:40 +0530 Subject: [PATCH 27/80] use appName to display contact name and change contact qr to support contact deeplink --- .../Home/components/Contact/component/Contact.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 2c72ab81c..8a64fe5f8 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -19,7 +19,9 @@ import { LocalizationContext } from 'src/context/Localization/LocContext'; import { useChatPeer } from 'src/hooks/useChatPeer'; import useToastMessage from 'src/hooks/useToastMessage'; import ToastErrorIcon from 'src/assets/images/toast_error.svg'; -import idx from 'idx'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import { RealmSchema } from 'src/storage/realm/enum'; +import dbManager from 'src/storage/realm/dbManager'; const Contact = () => { const { colorMode } = useColorMode(); @@ -30,9 +32,9 @@ const Contact = () => { const navigation = useNavigation(); const [contactmodalVisible, setContactModalVisible] = useState(false); const [shareContact, setShareContact] = useState(false); - const [shareContactData, setShareContactData] = useState(''); const { translations } = useContext(LocalizationContext); const { contactText } = translations; + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); const { showToast } = useToastMessage(); const chatPeer = useChatPeer(); @@ -43,9 +45,6 @@ const Contact = () => { if (!chatPeerInitialized) { throw new Error(); } - - const keys = await chatPeer.getKeys(); - setShareContactData(idx(keys, (_) => _.publicKey)); } catch (error) { console.error('Error initializing chat peer:', error); showToast('Chat Peer initialization failed', ); @@ -61,7 +60,7 @@ const Contact = () => { { { - navigation.navigate('keeperSupport'); + navigation.navigate('ChatRoomScreen'); }} > @@ -147,7 +146,7 @@ const Contact = () => { isShareContact={shareContact} setContactModalVisible={setContactModalVisible} navigation={navigation} - data={shareContactData} + data={`keeper://contact/${app.contactsKey.publicKey}/`} /> )} /> From 9e9a9d70eafb7521cc856443390d222bfe74058c Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 12:45:33 +0530 Subject: [PATCH 28/80] refactor: update app profile to use fetch API and handle response data --- src/services/backend/Relay.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index 5e0c6166a..b70b5d1a0 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -311,7 +311,6 @@ export default class Relay { image: any, id: string ): Promise => { - let res; try { const formData = new FormData(); formData.append('appName', appName); @@ -319,18 +318,20 @@ export default class Relay { formData.append('image', image); } formData.append('id', id); - console.log(`${RELAY}updateAppProfile`); - res = await RestClient.post(`${RELAY}updateAppProfile`, formData, { + const res = await fetch(`${RELAY}updateAppProfile`, { + method: 'POST', + body: formData, headers: { 'Content-Type': 'multipart/form-data', }, }); + const data = await res.json(); + return data; } catch (err) { console.log('err', err); if (err.response) throw new Error(err.response.data.err); if (err.code) throw new Error(err.code); } - return res.data || res.json; }; public static modifyLabels = async ( From a2ae19ad2e84f23270915a95e3e08e804a939f10 Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 13:52:54 +0530 Subject: [PATCH 29/80] feat: enhance chat functionality with issue fixes and encryption support --- .../components/Contact/ChatRoomScreen.tsx | 32 ++- .../components/Contact/component/ChatList.tsx | 19 +- .../components/Contact/component/ChatRoom.tsx | 112 +++----- src/services/backend/Relay.ts | 8 +- src/services/p2p/ChatPeerManager.ts | 133 +++++---- src/services/p2p/interface.ts | 3 +- src/storage/realm/realm.ts | 2 +- src/storage/realm/schema/community.ts | 1 + .../ChatEncryptionManager.ts | 258 ++++++++++++++++++ 9 files changed, 411 insertions(+), 157 deletions(-) create mode 100644 src/utils/service-utilities/ChatEncryptionManager.ts diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx index 00e535f6f..c111d7085 100644 --- a/src/screens/Home/components/Contact/ChatRoomScreen.tsx +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -7,33 +7,47 @@ import ChatRoomHeader from './component/ChatRoomHeader'; import ChatRoom from './component/ChatRoom'; import ProfileContent from './component/ProfileContent'; import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { KeeperApp } from 'src/models/interfaces/KeeperApp'; +import { RealmSchema } from 'src/storage/realm/enum'; +import { useObject, useQuery } from '@realm/react'; +import { Community, Contact, Message } from 'src/services/p2p/interface'; type ChatRoomParams = { ChatRoomScreen: { - receiverProfileImage: string; - receiverProfileName: string; - userProfileImage: string; + communityId: string; }; }; const ChatRoomScreen = () => { const { colorMode } = useColorMode(); const route = useRoute>(); - const { receiverProfileImage, receiverProfileName, userProfileImage } = route.params; - const [editReceiverProfileName, setEditReceiverProfileName] = useState(receiverProfileName); + const { communityId } = route.params; const [openEditModal, setOpenEditModal] = useState(false); - const [editUserProfileImage, setEditUserProfileImage] = useState(receiverProfileImage); 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 contact = useQuery(RealmSchema.Contact).filtered( + 'contactKey = $0', + community.with + )[0]; return ( - + setOpenEditModal(false)} diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index d12b82b7e..0bd63f7b7 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -46,13 +46,7 @@ const ChatItem = ({ item, userProfileImage }: { item: ChatItemData; userProfileI }; const handlePress = () => { - navigation.navigate('ChatRoomScreen', { - receiverProfileImage: item.image, - receiverProfileName: item.name, - userProfileImage, - communityId: item.communityId, - contactKey: item.contactKey, - }); + navigation.navigate('ChatRoomScreen', { communityId: item.communityId }); }; return ( @@ -94,10 +88,15 @@ const ChatItem = ({ item, userProfileImage }: { item: ChatItemData; userProfileI ); }; -const ChatList = ({ userProfileImage }: { userProfileImage: any }) => { +const ChatList = ({ + userProfileImage, + communities, +}: { + userProfileImage: any; + communities: any; +}) => { const { colorMode } = useColorMode(); - const { communities, getAllMessages, getMessagesByCommunity, getUnreadCount, getContactByKey } = - useChatPeer(); + const { getAllMessages, getMessagesByCommunity, getUnreadCount, getContactByKey } = useChatPeer(); // Transform communities into chat list data const chatData = useMemo(() => { diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index 24897ac65..f26ecfd1f 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -17,73 +17,12 @@ import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; 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'; - -// dummy data -const initialMessages = [ - { - id: 1, - text: 'Hey, did you end up watching "The Silent City"?', - sender: 'other', - date: '2025-05-12T10:12:00', - }, - { - id: 2, - text: 'Yeah I did! Just finished it last night.', - sender: 'me', - date: '2025-05-12T10:13:00', - }, - { - id: 3, - text: 'Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.', - sender: 'other', - date: '2025-05-12T10:14:30', - }, - { - id: 10, - text: 'Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.Man, that twist at the end? I did NOT see that coming.', - sender: 'other', - date: '2025-05-12T10:14:30', - }, - { - id: 4, - text: 'Right? When she found out her brother was behind it all...', - sender: 'me', - date: '2025-05-12T10:15:20', - }, - { - id: 5, - text: 'Exactly! And the cinematography was so good. Every shot looked like a painting.', - sender: 'other', - date: '2025-05-12T10:17:10', - }, - { - id: 6, - text: 'I really liked the soundtrack too. Gave me chills.', - sender: 'me', - date: '2025-05-12T10:19:45', - }, - - { - id: 7, - text: 'I’ve been thinking about that ending all day ', - sender: 'other', - date: '2025-05-13T09:05:00', - }, - { - id: 8, - text: 'Same! I kind of want to rewatch it already.', - sender: 'me', - date: '2025-05-13T09:06:10', - }, - { - id: 9, - text: 'Let’s do a watch party this weekend? I’ll bring popcorn ', - sender: 'other', - date: '2025-05-13T09:07:55', - }, - { id: 10, text: 'Deal. Saturday night?', sender: 'me', date: '2025-05-13T09:08:20' }, - { id: 11, text: 'Perfect. Can’t wait!', sender: 'other', date: '2025-05-13T09:08:45' }, -]; +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 { useChatPeer } from 'src/hooks/useChatPeer'; +import { v4 as uuidv4 } from 'uuid'; const groupMessagesByDate = (msgs) => { const groups = {}; @@ -100,24 +39,41 @@ const groupMessagesByDate = (msgs) => { return groups; }; -const ChatRoom = ({ userProfileImage, receiverProfileImage }) => { - const [messages, setMessages] = useState(initialMessages); +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); + const chatPeer = useChatPeer(); const handleSend = () => { if (!inputValue.trim()) return; - const newMessage = { - id: Date.now(), - text: inputValue, - sender: 'me', - date: new Date().toISOString(), - }; - setMessages((prev) => [...prev, newMessage]); - setInputValue(''); + try { + const messageData = { + id: uuidv4(), + text: inputValue.trim(), + 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 + ); + chatPeer.sendMessage( + community.with, + JSON.stringify({ ...encryptedMessage, communityId: community.id }) + ); + setInputValue(''); + } catch (error) { + console.error('Error sending message:', error); + } setTimeout(() => { scrollRef?.current?.scrollToEnd({ animated: true }); }, 0); @@ -126,7 +82,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage }) => { setTimeout(() => { scrollRef?.current?.scrollToEnd({ animated: true }); }, 100); - }, [messages]); + }, [messages, scrollRef]); return ( { - await this.worklet.start('/app.bundle', bundle, [seed]); - - this.rpc = new RPC(this.IPC, async (req) => { - const data = b4a.toString(req.data); + try { + await this.worklet.start('/app.bundle', bundle, [seed]); - 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) { - if (this.onConnectionCallback) { - this.onConnectionCallback(JSON.parse(data)); + 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) { + if (this.onConnectionCallback) { + this.onConnectionCallback(JSON.parse(data)); + } } - } - }); - return true; + }); + return true; + } catch (error) { + console.error('Error initializing chat peer manager:', error); + return false; + } } async getKeys() { @@ -115,19 +121,30 @@ export default class ChatPeerManager { async loadPendingMessages(lastBlock = 0) { try { - const response = await this.getPeerMessages(this.app.contactsKey.publicKey, lastBlock + 1); + console.log('loadPendingMessages', lastBlock); + const response = await this.getPeerMessages( + this.app.contactsKey.publicKey, + lastBlock === 0 ? 0 : lastBlock - 1 + ); + console.log('response', response); if (response.messages.length > 0) { - const communities = dbManager.getCollection(RealmSchema.Community); for (const msg of response.messages) { const message: Message = JSON.parse(msg.message); - const communityId = [this.app.contactsKey.publicKey, message.sender].sort().join('-'); - if (!communities.find((c) => c.id === communityId)) { - // const contact = await Relay.getWalletProfiles([message.sender]); - const contact = await this.mockGetWalletProfiles([message.sender]); - + const communityId = message.communityId; + let community = dbManager.getObjectByPrimaryId(RealmSchema.Community, 'id', communityId); + if (!community && message.encryptedKeys) { + const contact = await Relay.getAppProfiles([message.sender]); if (contact.results.length > 0) { dbManager.createObject(RealmSchema.Contact, contact.results[0]); - dbManager.createObject(RealmSchema.Community, { + const sharedSecret = ChatEncryptionManager.performKeyExchange( + this.app.contactsKey, + message.sender + ); + const encryptedKeys = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); + community = { id: communityId, communityId: communityId, name: contact.results[0].name, @@ -135,20 +152,24 @@ export default class ChatPeerManager { createdAt: msg.timestamp, updatedAt: msg.timestamp, with: message.sender, - }); + key: encryptedKeys.aesKey, + }; + dbManager.createObject(RealmSchema.Community, community); } } + const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.key); + const messageData = JSON.parse(decryptedMessage); dbManager.createObject(RealmSchema.Message, { - id: message.id, - communityId: communityId, - type: message.type, - text: message.text, + id: messageData.id, + communityId: messageData.communityId, + type: messageData.type, + text: messageData.text, createdAt: msg.timestamp, - sender: message.sender, + sender: messageData.sender, block: msg.blockNumber, unread: true, - fileUrl: message?.fileUrl, - request: (message as any)?.request, + fileUrl: messageData?.fileUrl, + request: messageData?.request, }); } } @@ -162,35 +183,44 @@ export default class ChatPeerManager { const communities = dbManager.getCollection(RealmSchema.Community); const data = JSON.parse(payload.data); const message = JSON.parse(data.message); - const community = communities.find((c) => c.id === message.communityId); - if (!community) { - // const contact = await Relay.getWalletProfiles([message.sender]); - const contact = await this.mockGetWalletProfiles([message.sender]); - + let community: Community = communities.find((c) => c.id === message.communityId); + if (!community && message.encryptedKeys) { + const contact = await Relay.getAppProfiles([message.sender]); if (contact.results.length > 0) { + const sharedSecret = ChatEncryptionManager.performKeyExchange( + this.app.contactsKey, + message.sender + ); + const encryptedKeys = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); dbManager.createObject(RealmSchema.Contact, contact.results[0]); - dbManager.createObject(RealmSchema.Community, { + community = { id: message.communityId, name: contact.results[0].name, type: CommunityType.Peer, createdAt: data.timestamp, updatedAt: data.timestamp, with: message.sender, - }); + key: encryptedKeys.aesKey, + }; + dbManager.createObject(RealmSchema.Community, community); } } - + const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.key); + const messageData = JSON.parse(decryptedMessage); dbManager.createObject(RealmSchema.Message, { - id: message.id, - communityId: message.communityId, - type: message.type, - text: message.text, + id: messageData.id, + communityId: messageData.communityId, + type: messageData.type, + text: messageData.text, createdAt: data.timestamp, - sender: message.sender, + sender: messageData.sender, block: data.blockNumber, unread: true, - fileUrl: message?.fileUrl, - request: message?.request, + fileUrl: messageData?.fileUrl, + request: messageData?.request, }); } catch (error) { console.error('Error storing messages:', error); @@ -237,13 +267,12 @@ export default class ChatPeerManager { async syncContacts() { try { const contacts: Contact[] = dbManager.getCollection(RealmSchema.Contact) as any; - // const response = await Relay.getWalletProfiles(contacts.map((c) => c.contactKey)); - const response = await this.mockGetWalletProfiles(contacts.map((c) => c.contactKey)); + const response = await Relay.getAppProfiles(contacts.map((c) => c.contactKey)); - if (response.results.length > 0) { + if (response.profiles.length > 0) { dbManager.createObjectBulk( RealmSchema.Contact, - response.results, + response.profiles, Realm.UpdateMode.Modified ); } diff --git a/src/services/p2p/interface.ts b/src/services/p2p/interface.ts index 8b81fc2e4..f04a00be1 100644 --- a/src/services/p2p/interface.ts +++ b/src/services/p2p/interface.ts @@ -33,8 +33,7 @@ export interface Community { createdAt: number; updatedAt: number; with?: string; - // messages: Message[]; - // members: string[]; + key?: string; } export interface Contact { diff --git a/src/storage/realm/realm.ts b/src/storage/realm/realm.ts index e9d9db1d2..6d63a4af9 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 = 107; + public static schemaVersion = 108; /** * initializes/opens realm w/ appropriate configuration diff --git a/src/storage/realm/schema/community.ts b/src/storage/realm/schema/community.ts index 77afb7ef4..fc2148409 100644 --- a/src/storage/realm/schema/community.ts +++ b/src/storage/realm/schema/community.ts @@ -37,5 +37,6 @@ export const CommunitySchema: ObjectSchema = { createdAt: { type: 'int', default: Date.now() }, type: 'string', with: 'string?', + key: 'string?', }, }; diff --git a/src/utils/service-utilities/ChatEncryptionManager.ts b/src/utils/service-utilities/ChatEncryptionManager.ts new file mode 100644 index 000000000..c36412d2b --- /dev/null +++ b/src/utils/service-utilities/ChatEncryptionManager.ts @@ -0,0 +1,258 @@ +import CryptoJS from 'crypto-js'; +import { encode as base64Encode, decode as base64Decode } from '@stablelib/base64'; + +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'}` + ); + } + } + + /** + * 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(''); + } +} From bc1f23fe53578ea72bb379a338f22d217f62f99f Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 13:53:40 +0530 Subject: [PATCH 30/80] feat: init chat when detecting valid contact link --- .../components/Contact/component/Contact.tsx | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 8a64fe5f8..52a90b1be 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -22,6 +22,12 @@ 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 } from 'src/services/p2p/interface'; +import { hash256 } from 'src/utils/service-utilities/encryption'; +import Relay from 'src/services/backend/Relay'; +import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptionManager'; +import { v4 as uuidv4 } from 'uuid'; +import { useQuery } from '@realm/react'; const Contact = () => { const { colorMode } = useColorMode(); @@ -35,7 +41,7 @@ const Contact = () => { const { translations } = useContext(LocalizationContext); const { contactText } = translations; const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); - + const communities = useQuery(RealmSchema.Community); const { showToast } = useToastMessage(); const chatPeer = useChatPeer(); @@ -56,6 +62,65 @@ const Contact = () => { if (!chatPeer.isInitialized) initializeChat(); }, [chatPeer.isInitialized]); + useEffect(() => { + chatPeer.loadPendingMessages(); + }, []); + + const onQrScan = (data) => { + if (data.startsWith('keeper://')) { + const urlParts = data.split('/'); + const path = urlParts[2]; + if (path === 'contact') { + const publicKey = urlParts[3]; + initChat(publicKey); + } + } else { + showToast('Invalid QR code', ); + } + }; + + const initChat = async (publicKey: string) => { + try { + const profiles = await Relay.getAppProfiles([publicKey]); + if (profiles.length > 0) { + dbManager.createObject(RealmSchema.Contact, profiles[0]); + } + const communityId = hash256([app.contactsKey.publicKey, publicKey].sort().join('-')); + const sessionKeys = ChatEncryptionManager.generateSessionKeys(); + const encryptedKeys = ChatEncryptionManager.encryptKeys( + sessionKeys.aesKey, + ChatEncryptionManager.performKeyExchange(app.contactsKey, publicKey) + ); + const community = dbManager.getObjectByPrimaryId(RealmSchema.Community, 'id', communityId); + if (!community) { + dbManager.createObject(RealmSchema.Community, { + id: communityId, + communityId: communityId, + name: profiles[0].name, + createdAt: Date.now(), + type: CommunityType.Peer, + with: publicKey, + key: sessionKeys.aesKey, + }); + const message = { + id: uuidv4(), + communityId: communityId, + type: MessageType.Alert, + text: `Start of community`, + createdAt: Date.now(), + sender: app.contactsKey.publicKey, + unread: false, + encryptedKeys: encryptedKeys, + }; + chatPeer.sendMessage(publicKey, JSON.stringify(message)); + dbManager.createObject(RealmSchema.Message, message); + showToast('New Tribe Contact created', false); + } + } catch (error) { + console.error('Error initializing chat:', error); + } + }; + return ( { - + { setContactModalVisible={setContactModalVisible} navigation={navigation} data={`keeper://contact/${app.contactsKey.publicKey}/`} + onQrScan={onQrScan} /> )} /> From 6595d3157ec4f80e5884d7203a27dc50f11c2210 Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 13:54:11 +0530 Subject: [PATCH 31/80] add ReceiveAddress component for displaying contact link --- src/screens/Home/components/Contact/ContactShowQr.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/screens/Home/components/Contact/ContactShowQr.tsx b/src/screens/Home/components/Contact/ContactShowQr.tsx index 21ef77b92..5e5bd0978 100644 --- a/src/screens/Home/components/Contact/ContactShowQr.tsx +++ b/src/screens/Home/components/Contact/ContactShowQr.tsx @@ -1,5 +1,4 @@ import { Box, useColorMode } from 'native-base'; -import { authenticator } from 'otplib'; import React, { useContext } from 'react'; import { StyleSheet } from 'react-native'; import KeeperQRCode from 'src/components/KeeperQRCode'; @@ -7,6 +6,7 @@ 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(); @@ -26,6 +26,8 @@ const ContactShareQr = ({ route }) => { showLogo /> + + ); }; From 144bcc80c193db19a54496da4ec7108c6c7692b5 Mon Sep 17 00:00:00 2001 From: Shashank Date: Wed, 23 Jul 2025 13:56:04 +0530 Subject: [PATCH 32/80] added temporary options and CTAs --- .../Contact/component/ContactModalData.tsx | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ContactModalData.tsx b/src/screens/Home/components/Contact/component/ContactModalData.tsx index bcd470af8..429416a3c 100644 --- a/src/screens/Home/components/Contact/component/ContactModalData.tsx +++ b/src/screens/Home/components/Contact/component/ContactModalData.tsx @@ -18,8 +18,16 @@ 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 }) { +function ContactModalData({ + isShareContact = false, + setContactModalVisible, + navigation, + data, + onQrScan, +}) { const { colorMode } = useColorMode(); const [nfcVisible, setNfcVisible] = useState(false); const { showToast } = useToastMessage(); @@ -103,20 +111,45 @@ function ContactModalData({ isShareContact = false, setContactModalVisible, navi const ContactOptions = [ { id: 1, - label: 'Show QR', + 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, + }, + }) + ); + }, + }, { id: 2, label: `${isIos ? 'Airdrop / ' : ''}File `, icon: , - onPress: () => { + onPress: async () => { setContactModalVisible(false); - shareWithAirdrop(); + const clipboardData = await Clipboard.getString(); + if (clipboardData) { + onQrScan(clipboardData); + } else { + showToast('No data found in clipboard'); + } }, }, From 648594071d4be2f6367aa49e74f38c7dafa3f06e Mon Sep 17 00:00:00 2001 From: Shashank Date: Thu, 24 Jul 2025 11:44:55 +0530 Subject: [PATCH 33/80] update app bundle version and identifiers for P2P service --- src/services/p2p/app.bundle.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/p2p/app.bundle.mjs b/src/services/p2p/app.bundle.mjs index 4c9b11503..2eacc2a34 100644 --- a/src/services/p2p/app.bundle.mjs +++ b/src/services/p2p/app.bundle.mjs @@ -1 +1 @@ -export default "48482\n{\"version\":0,\"id\":\"16e18e632992d00e9b48842a0e4ca0cecb86d95534fb041cfc4d974925ef027d\",\"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\":3264,\"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.1\",\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 = '58dac4e5cfe213a0755719abda3ab6d131d02eee0bc3bb2066ded29630885681';\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" +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" From ad79a812e45af5597b7332f678d03ae05cdce59c Mon Sep 17 00:00:00 2001 From: Shashank Date: Thu, 24 Jul 2025 13:38:09 +0530 Subject: [PATCH 34/80] contacts and chat related fixes --- src/hooks/useChatPeer.ts | 2 +- .../components/Contact/component/Contact.tsx | 16 +++--- src/services/p2p/ChatPeerManager.ts | 50 ++++++++++++------- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index 45aff4788..6213f9ec6 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -97,7 +97,7 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn if (success && enableMessageListener) { chatManager.setOnMessageListener((data) => { - console.log('New message received:', data); + // console.log('New message received:', data); }); } diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 52a90b1be..51879e7bb 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -22,12 +22,13 @@ 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 } from 'src/services/p2p/interface'; +import { CommunityType, MessageType, Message } from 'src/services/p2p/interface'; import { hash256 } from 'src/utils/service-utilities/encryption'; import Relay from 'src/services/backend/Relay'; 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'; const Contact = () => { const { colorMode } = useColorMode(); @@ -41,15 +42,18 @@ const Contact = () => { const { translations } = useContext(LocalizationContext); const { contactText } = 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 chatPeer = useChatPeer(); const initializeChat = async () => { try { - const chatPeerInitialized = await chatPeer.initChatPeer(); - if (!chatPeerInitialized) { - throw new Error(); + if (!ChatPeerManager.isInitialized) { + const chatPeerInitialized = await chatPeer.initChatPeer(); + if (!chatPeerInitialized) { + throw new Error(); + } } } catch (error) { console.error('Error initializing chat peer:', error); @@ -63,7 +67,7 @@ const Contact = () => { }, [chatPeer.isInitialized]); useEffect(() => { - chatPeer.loadPendingMessages(); + chatPeer.loadPendingMessages(lastBlock); }, []); const onQrScan = (data) => { @@ -106,7 +110,7 @@ const Contact = () => { id: uuidv4(), communityId: communityId, type: MessageType.Alert, - text: `Start of community`, + text: `Start of conversation`, createdAt: Date.now(), sender: app.contactsKey.publicKey, unread: false, diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts index ae5c0073f..f91dba713 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -21,7 +21,7 @@ import Relay from '../backend/Relay'; export default class ChatPeerManager { static instance: ChatPeerManager; - + static isInitialized: boolean = false; worklet: Worklet; IPC: any; rpc: any; @@ -60,6 +60,7 @@ export default class ChatPeerManager { } } }); + ChatPeerManager.isInitialized = true; return true; } catch (error) { console.error('Error initializing chat peer manager:', error); @@ -110,7 +111,7 @@ export default class ChatPeerManager { async getPeerMessages(pubKey: string, lastBlock: number) { try { const response = await axios.get( - `https://api.bitcointribe.app/api/v1/chat/getmessages?publicKey=${pubKey}&from=${lastBlock}` + `https://dev-api.bitcointribe.app/api/v1/chat/getmessages?publicKey=${pubKey}&from=${lastBlock}` ); return response.data; } catch (error) { @@ -186,7 +187,7 @@ export default class ChatPeerManager { let community: Community = communities.find((c) => c.id === message.communityId); if (!community && message.encryptedKeys) { const contact = await Relay.getAppProfiles([message.sender]); - if (contact.results.length > 0) { + if (contact.length > 0) { const sharedSecret = ChatEncryptionManager.performKeyExchange( this.app.contactsKey, message.sender @@ -195,10 +196,10 @@ export default class ChatPeerManager { message.encryptedKeys, sharedSecret ); - dbManager.createObject(RealmSchema.Contact, contact.results[0]); + dbManager.createObject(RealmSchema.Contact, contact[0]); community = { id: message.communityId, - name: contact.results[0].name, + name: contact[0].name, type: CommunityType.Peer, createdAt: data.timestamp, updatedAt: data.timestamp, @@ -206,22 +207,33 @@ export default class ChatPeerManager { key: encryptedKeys.aesKey, }; dbManager.createObject(RealmSchema.Community, community); + 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.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, + }); } - const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.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, - request: messageData?.request, - }); } catch (error) { console.error('Error storing messages:', error); } From d433d8cb0a140d56f7ac3cd48884f465f16c4e28 Mon Sep 17 00:00:00 2001 From: Shashank Date: Thu, 24 Jul 2025 13:56:17 +0530 Subject: [PATCH 35/80] chore: update dependencies @stablelib/base64 and uuid --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f1ead71a..4ab6f819c 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@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", @@ -129,7 +130,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", From dac11be63c8bce9de90c862302d9ccae52865a44 Mon Sep 17 00:00:00 2001 From: Parsh Date: Thu, 24 Jul 2025 11:02:42 +0200 Subject: [PATCH 36/80] adds: support for asymmetric enc/dec via x25519 --- package.json | 2 +- src/services/p2p/enc-X25519.ts | 358 +++++++++++++++++++++++++++++++++ yarn.lock | 46 ++--- 3 files changed, 378 insertions(+), 28 deletions(-) create mode 100644 src/services/p2p/enc-X25519.ts diff --git a/package.json b/package.json index 4ab6f819c..3ade646f6 100644 --- a/package.json +++ b/package.json @@ -28,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", @@ -118,7 +119,6 @@ "react-native-share": "9.2.3", "react-native-svg": "13.13.0", "react-native-tcp-socket": "5.6.2", - "react-native-tor": "0.1.8", "react-redux": "7.2.8", "readable-stream": "1.0.33", "realm": "12.14.2", 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/yarn.lock b/yarn.lock index 479468944..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== @@ -1831,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" @@ -1841,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== @@ -3464,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" @@ -10135,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" @@ -12267,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" @@ -12310,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" From 04687cbb59f91962ee34ef54e6f14e9f31259018 Mon Sep 17 00:00:00 2001 From: Parsh Date: Thu, 24 Jul 2025 11:07:05 +0200 Subject: [PATCH 37/80] adds back: react-native-tor --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 3ade646f6..385e486b5 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "react-native-share": "9.2.3", "react-native-svg": "13.13.0", "react-native-tcp-socket": "5.6.2", + "react-native-tor": "0.1.8", "react-redux": "7.2.8", "readable-stream": "1.0.33", "realm": "12.14.2", From f57d7e5126c1b7cc1262f4271d680f37ff4ccdcc Mon Sep 17 00:00:00 2001 From: Parsh Date: Sat, 26 Jul 2025 18:11:22 +0200 Subject: [PATCH 38/80] migrates: peer init and key generation to contacts tab --- .../Home/components/Contact/component/Contact.tsx | 13 ++++--------- src/services/backend/Relay.ts | 4 +--- src/store/sagas/login.ts | 9 --------- src/store/sagas/storage.ts | 12 +----------- 4 files changed, 6 insertions(+), 32 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 51879e7bb..cd428e679 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -49,11 +49,9 @@ const Contact = () => { const initializeChat = async () => { try { - if (!ChatPeerManager.isInitialized) { - const chatPeerInitialized = await chatPeer.initChatPeer(); - if (!chatPeerInitialized) { - throw new Error(); - } + const chatPeerInitialized = await chatPeer.initChatPeer(); + if (!chatPeerInitialized) { + throw new Error(); } } catch (error) { console.error('Error initializing chat peer:', error); @@ -64,12 +62,9 @@ const Contact = () => { useEffect(() => { if (!chatPeer.isInitialized) initializeChat(); + else chatPeer.loadPendingMessages(lastBlock); }, [chatPeer.isInitialized]); - useEffect(() => { - chatPeer.loadPendingMessages(lastBlock); - }, []); - const onQrScan = (data) => { if (data.startsWith('keeper://')) { const urlParts = data.split('/'); diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index 31bea12c0..d40cbde40 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -258,8 +258,7 @@ export default class Relay { public static createNewApp = async ( publicId: string, appID: string, - fcmToken: string, - contactsKey?: string + fcmToken: string ): Promise<{ created: boolean; }> => { @@ -269,7 +268,6 @@ export default class Relay { appID, publicId, fcmToken, - contactsKey, }); } catch (err) { console.log('err', err); diff --git a/src/store/sagas/login.ts b/src/store/sagas/login.ts index 1d0e683be..36c52e806 100644 --- a/src/store/sagas/login.ts +++ b/src/store/sagas/login.ts @@ -199,15 +199,6 @@ function* credentialsAuthWorker({ payload }) { yield put(setTempDetails({ hash, realmId: REALM_FILE, accountIdentifier: '' })); } - const contactsSecretKey = idx(keeperApp, (app) => app.contactsKey.secretKey); - if (contactsSecretKey) { - // initiate the contacts manager if the contacts feature is active - const cm = ChatPeerManager.getInstance(); - cm.init(keeperApp.primarySeed).then((success) => - console.log(`Initialized contacts manager: ${success}`) - ); - } - const newVersion = DeviceInfo.getVersion(); const versionCollection = yield call(dbManager.getCollection, RealmSchema.VersionHistory); const lastElement = versionCollection[versionCollection.length - 1]; diff --git a/src/store/sagas/storage.ts b/src/store/sagas/storage.ts index dc1c6562f..71de5d99b 100644 --- a/src/store/sagas/storage.ts +++ b/src/store/sagas/storage.ts @@ -71,16 +71,7 @@ export function* setupKeeperAppWorker({ payload }) { ); imageEncryptionKey = generateEncryptionKey(entropy.toString('hex')); } - const cm = ChatPeerManager.getInstance(); - yield call([cm, cm.init], primarySeed.toString('hex')); - const contactsKey = yield call([cm, cm.getKeys]); - const response = yield call( - Relay.createNewApp, - publicId, - appID, - fcmToken, - contactsKey.publicKey - ); + const response = yield call(Relay.createNewApp, publicId, appID, fcmToken); if (response && response.created) { const newAPP: KeeperApp = { @@ -100,7 +91,6 @@ export function* setupKeeperAppWorker({ payload }) { version: DeviceInfo.getVersion(), networkType: bitcoinNetworkType, enableAnalytics: false, - contactsKey, }; yield call(dbManager.createObject, RealmSchema.KeeperApp, newAPP); From 0d02145d15f7fb22b9f0b5c27ed90f8751b0d2fb Mon Sep 17 00:00:00 2001 From: Parsh Date: Sat, 26 Jul 2025 18:24:11 +0200 Subject: [PATCH 39/80] fixes: keeper support crash --- src/screens/Home/components/Contact/component/Contact.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index cd428e679..9e86c2fad 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -139,7 +139,7 @@ const Contact = () => { { - navigation.navigate('ChatRoomScreen'); + navigation.navigate('keeperSupport'); }} > From d5c6c9accf12607027ef2df81c1cc84d1d52c873 Mon Sep 17 00:00:00 2001 From: Parsh Date: Sat, 26 Jul 2025 18:32:08 +0200 Subject: [PATCH 40/80] adds: todo for app update (alternate route) --- src/hooks/useChatPeer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index 6213f9ec6..a28881ce9 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -121,6 +121,7 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn }, }); console.log('Database updated with peer keys'); + // TODO: update the backend (using Relay.createNewApp alternate route) } } catch (keyError) { console.warn('Failed to get or update contacts key:', keyError); From 3754da3a34e99a0bd7e4d98d5a3dcf1b4ce410c6 Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 09:49:44 +0530 Subject: [PATCH 41/80] refactor: remove Relay dependency from ProfileContent and streamline profile update logic --- .../Contact/component/ProfileContent.tsx | 19 +++---- src/services/backend/Relay.ts | 51 ------------------- 2 files changed, 6 insertions(+), 64 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx index 461105e82..dde55aacc 100644 --- a/src/screens/Home/components/Contact/component/ProfileContent.tsx +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -9,7 +9,6 @@ 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 Relay from 'src/services/backend/Relay'; import { RealmSchema } from 'src/storage/realm/enum'; import { KeeperApp } from 'src/models/interfaces/KeeperApp'; import dbManager from 'src/storage/realm/dbManager'; @@ -58,18 +57,12 @@ const ProfileContent = ({ const handleConfirm = async () => { try { - const response = await Relay.updateAppProfile(profileName, profileImage, app.id); - if (response.updated) { - dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { - appName: profileName, - }); - dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { - image: profileImage, - }); - setUserProfileImage(profileImage); - setUserProfileName(profileName); - setCreateProfile(false); - } + dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { + appName: profileName, + }); + setUserProfileImage(profileImage); + setUserProfileName(profileName); + setCreateProfile(false); } catch (error) { console.log('Error in handleConfirm', error); } diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index d40cbde40..c39f60302 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -280,57 +280,6 @@ export default class Relay { }; }; - public static getAppProfiles = async ( - contactsKeys: string[] - ): Promise<{ - profiles: { - contactsKey: string; - name: string; - image: string; - }[]; - }> => { - let res; - try { - res = await RestClient.post(`${RELAY}getAppProfiles`, { - contactsKeys, - }); - } 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 { profiles } = res.data || res.json; - return profiles; - }; - - public static updateAppProfile = async ( - appName: string, - image: any, - id: string - ): Promise => { - try { - const formData = new FormData(); - formData.append('appName', appName); - if (image) { - formData.append('image', image); - } - formData.append('id', id); - const res = await fetch(`${RELAY}updateAppProfile`, { - method: 'POST', - body: formData, - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - const data = await res.json(); - return data; - } catch (err) { - console.log('err', err); - if (err.response) throw new Error(err.response.data.err); - if (err.code) throw new Error(err.code); - } - }; - public static modifyLabels = async ( appId: string, addLabels: any[], From ba4c92feb4b16ea716cce2ee68c2c5cabf3b9689 Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 09:50:22 +0530 Subject: [PATCH 42/80] refactor: update initChat function to include contact name, remove Relay dependency, and use share secrete gen --- .../components/Contact/component/Contact.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 9e86c2fad..7ae82641e 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -24,7 +24,6 @@ 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 Relay from 'src/services/backend/Relay'; import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptionManager'; import { v4 as uuidv4 } from 'uuid'; import { useQuery } from '@realm/react'; @@ -69,33 +68,31 @@ const Contact = () => { if (data.startsWith('keeper://')) { const urlParts = data.split('/'); const path = urlParts[2]; + const name = urlParts[4]; if (path === 'contact') { const publicKey = urlParts[3]; - initChat(publicKey); + initChat(publicKey, name); } } else { showToast('Invalid QR code', ); } }; - const initChat = async (publicKey: string) => { + const initChat = async (publicKey: string, name: string = 'Unknown Contact') => { try { - const profiles = await Relay.getAppProfiles([publicKey]); - if (profiles.length > 0) { - dbManager.createObject(RealmSchema.Contact, profiles[0]); - } const communityId = hash256([app.contactsKey.publicKey, publicKey].sort().join('-')); const sessionKeys = ChatEncryptionManager.generateSessionKeys(); - const encryptedKeys = ChatEncryptionManager.encryptKeys( - sessionKeys.aesKey, - ChatEncryptionManager.performKeyExchange(app.contactsKey, publicKey) + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + app.contactsKey.secretKey, + publicKey ); + 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: profiles[0].name, + name: name, createdAt: Date.now(), type: CommunityType.Peer, with: publicKey, @@ -108,12 +105,17 @@ const Contact = () => { text: `Start of conversation`, createdAt: Date.now(), sender: app.contactsKey.publicKey, + senderName: app.appName, unread: false, encryptedKeys: encryptedKeys, }; - chatPeer.sendMessage(publicKey, JSON.stringify(message)); + const encryptedMessage = ChatEncryptionManager.encryptMessage( + JSON.stringify({ ...message }), + sessionKeys.aesKey + ); + chatPeer.sendMessage(publicKey, JSON.stringify({ ...encryptedMessage, communityId })); dbManager.createObject(RealmSchema.Message, message); - showToast('New Tribe Contact created', false); + showToast('New Contact added', false); } } catch (error) { console.error('Error initializing chat:', error); From 01b6e406d6adbc0403048c93796a117e82fa08ed Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 09:50:42 +0530 Subject: [PATCH 43/80] feat: add x25519 deriveSharedSecret method for shared key generation --- src/utils/service-utilities/ChatEncryptionManager.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/service-utilities/ChatEncryptionManager.ts b/src/utils/service-utilities/ChatEncryptionManager.ts index c36412d2b..315392d5d 100644 --- a/src/utils/service-utilities/ChatEncryptionManager.ts +++ b/src/utils/service-utilities/ChatEncryptionManager.ts @@ -1,5 +1,6 @@ import CryptoJS from 'crypto-js'; import { encode as base64Encode, decode as base64Decode } from '@stablelib/base64'; +import { x25519, ed25519 } from '@noble/curves/ed25519'; export interface KeyPair { publicKey: string; // hex format @@ -100,6 +101,14 @@ export class ChatEncryptionManager { } } + static deriveSharedSecret( + privateKey: string | Uint8Array, + publicKey: string | Uint8Array + ): string { + const sharedSecret = x25519.getSharedSecret(privateKey, publicKey); + return this.uint8ArrayToHex(sharedSecret); + } + /** * Generate session keys for encryption * @returns Object containing hex-encoded AES key From b45d2cbcdc4c5f81ea169067ed7e7fa2054917c5 Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 09:51:26 +0530 Subject: [PATCH 44/80] refactor: remove syncContacts method from useChatPeer hook --- src/hooks/useChatPeer.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index a28881ce9..387f77838 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -37,9 +37,6 @@ export interface UseChatPeerReturn { }; getPeerMessages: (pubKey: string, lastBlock: number) => Promise; - // Contact management - syncContacts: () => Promise; - // Message operations (lazy loaded) getMessagesByCommunity: (communityId: string) => Message[]; getAllMessages: () => Message[]; @@ -218,17 +215,6 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn } }, []); - // Sync contacts - const syncContacts = useCallback(async (): Promise => { - try { - setError(null); - await chatManager.syncContacts(); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to sync contacts'); - captureError(err); - } - }, []); - // Lazy-loaded message operations const getAllMessages = useCallback((): Message[] => { try { @@ -373,9 +359,6 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn getKeys, getPeerMessages, - // Contact management - syncContacts, - // Message operations (lazy loaded) getMessagesByCommunity, getAllMessages, From c04dff66d4da4551efbe789cb88781bb2bc71c55 Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 09:51:57 +0530 Subject: [PATCH 45/80] removed unused imports --- src/store/sagas/login.ts | 2 -- src/store/sagas/storage.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/src/store/sagas/login.ts b/src/store/sagas/login.ts index 36c52e806..f5e441dff 100644 --- a/src/store/sagas/login.ts +++ b/src/store/sagas/login.ts @@ -69,8 +69,6 @@ import { } from '../reducers/account'; import { REALM_FILE } from 'src/storage/realm/realm'; import { loadConciergeUserOnLogin, saveBackupMethodByAppId } from '../sagaActions/account'; -import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; -import idx from 'idx'; export const stringToArrayBuffer = (byteString: string): Uint8Array => { if (byteString) { diff --git a/src/store/sagas/storage.ts b/src/store/sagas/storage.ts index 71de5d99b..8522bf7d6 100644 --- a/src/store/sagas/storage.ts +++ b/src/store/sagas/storage.ts @@ -41,7 +41,6 @@ import { } from '../reducers/account'; import { loadConciergeTickets, loadConciergeUser } from '../reducers/concierge'; import LoginMethod from 'src/models/enums/LoginMethod'; -import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; export function* setupKeeperAppWorker({ payload }) { try { From c404feb43b6e5c3f457febc3912d2087c1c00349 Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 10:53:28 +0530 Subject: [PATCH 46/80] refactor: streamline message handling and update contact key sharing logic --- src/hooks/useChatPeer.ts | 28 +-- .../components/Contact/ChatRoomScreen.tsx | 12 +- .../components/Contact/component/Contact.tsx | 8 +- .../Contact/component/ContactModalData.tsx | 2 +- src/services/backend/Relay.ts | 23 ++ src/services/p2p/ChatPeerManager.ts | 213 +++++++----------- .../ChatEncryptionManager.ts | 2 +- 7 files changed, 129 insertions(+), 159 deletions(-) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index 387f77838..bcc03e219 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -7,7 +7,7 @@ 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 idx from 'idx'; +import Relay from 'src/services/backend/Relay'; export interface UseChatPeerOptions { autoInit?: boolean; @@ -90,11 +90,12 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn 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); + console.log('New message received:', data); }); } @@ -104,31 +105,26 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn }); } - const secretKey = idx(keeperApp, (app) => app.contactsKey.secretKey); // Get keys from ChatPeerManager and update KeeperApp if contactsKey is missing - if (success && !secretKey) { - try { - const keys = await chatManager.getKeys(); - if (keys && keys.publicKey && keys.secretKey) { - // Update the KeeperApp with the contacts key in Realm database + 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, }, }); - console.log('Database updated with peer keys'); - // TODO: update the backend (using Relay.createNewApp alternate route) } - } catch (keyError) { - console.warn('Failed to get or update contacts key:', keyError); - captureError(keyError); - return false; } + } else { + return true; } - - return success; } catch (err) { + console.log('err', err); setError(err instanceof Error ? err.message : 'Failed to initialize chat peer'); captureError(err); return false; diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx index c111d7085..31a8f3b33 100644 --- a/src/screens/Home/components/Contact/ChatRoomScreen.tsx +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -30,21 +30,17 @@ const ChatRoomScreen = () => { const messages = useQuery(RealmSchema.Message) .filtered('communityId = $0', communityId) .sorted('createdAt', true); - const contact = useQuery(RealmSchema.Contact).filtered( - 'contactKey = $0', - community.with - )[0]; return ( diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 7ae82641e..f262ae024 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -109,11 +109,7 @@ const Contact = () => { unread: false, encryptedKeys: encryptedKeys, }; - const encryptedMessage = ChatEncryptionManager.encryptMessage( - JSON.stringify({ ...message }), - sessionKeys.aesKey - ); - chatPeer.sendMessage(publicKey, JSON.stringify({ ...encryptedMessage, communityId })); + chatPeer.sendMessage(publicKey, JSON.stringify({ ...message, communityId })); dbManager.createObject(RealmSchema.Message, message); showToast('New Contact added', false); } @@ -212,7 +208,7 @@ const Contact = () => { isShareContact={shareContact} setContactModalVisible={setContactModalVisible} navigation={navigation} - data={`keeper://contact/${app.contactsKey.publicKey}/`} + data={`keeper://contact/${app.contactsKey.publicKey}/${app.appName}`} onQrScan={onQrScan} /> )} diff --git a/src/screens/Home/components/Contact/component/ContactModalData.tsx b/src/screens/Home/components/Contact/component/ContactModalData.tsx index 429416a3c..4e0ae97c3 100644 --- a/src/screens/Home/components/Contact/component/ContactModalData.tsx +++ b/src/screens/Home/components/Contact/component/ContactModalData.tsx @@ -110,7 +110,7 @@ function ContactModalData({ const ContactOptions = [ { - id: 1, + id: 0, label: 'Share QR', icon: , onPress: () => { diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index c39f60302..374fda9a6 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -280,6 +280,29 @@ export default class Relay { }; }; + public static updateContactsKey = async ( + id: string, + contactsKey: string + ): Promise<{ + updated: boolean; + }> => { + 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 index f91dba713..008fd0933 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -122,7 +122,6 @@ export default class ChatPeerManager { async loadPendingMessages(lastBlock = 0) { try { - console.log('loadPendingMessages', lastBlock); const response = await this.getPeerMessages( this.app.contactsKey.publicKey, lastBlock === 0 ? 0 : lastBlock - 1 @@ -130,48 +129,63 @@ export default class ChatPeerManager { console.log('response', response); if (response.messages.length > 0) { for (const msg of response.messages) { - const message: Message = JSON.parse(msg.message); + const message = JSON.parse(msg.message); const communityId = message.communityId; let community = dbManager.getObjectByPrimaryId(RealmSchema.Community, 'id', communityId); - if (!community && message.encryptedKeys) { - const contact = await Relay.getAppProfiles([message.sender]); - if (contact.results.length > 0) { - dbManager.createObject(RealmSchema.Contact, contact.results[0]); - const sharedSecret = ChatEncryptionManager.performKeyExchange( - this.app.contactsKey, - message.sender - ); - const encryptedKeys = ChatEncryptionManager.decryptKeys( - message.encryptedKeys, - sharedSecret - ); - community = { - id: communityId, - communityId: communityId, - name: contact.results[0].name, - type: CommunityType.Peer, - createdAt: msg.timestamp, - updatedAt: msg.timestamp, - with: message.sender, - key: encryptedKeys.aesKey, - }; - dbManager.createObject(RealmSchema.Community, community); - } + if (!community) { + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + this.app.contactsKey.secretKey, + message.sender + ); + const decryptedKey = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); + const contact = dbManager.getObjectByPrimaryId( + RealmSchema.Contact, + 'contactKey', + message.sender + ); + community = { + id: communityId, + communityId: communityId, + name: contact.name, + type: CommunityType.Peer, + createdAt: msg.timestamp, + updatedAt: msg.timestamp, + with: message.sender, + key: decryptedKey.aesKey, + }; + dbManager.createObject(RealmSchema.Community, community); + + 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.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, + }); } - const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.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: messageData?.fileUrl, - request: messageData?.request, - }); } } } catch (error) { @@ -186,39 +200,38 @@ export default class ChatPeerManager { const message = JSON.parse(data.message); let community: Community = communities.find((c) => c.id === message.communityId); if (!community && message.encryptedKeys) { - const contact = await Relay.getAppProfiles([message.sender]); - if (contact.length > 0) { - const sharedSecret = ChatEncryptionManager.performKeyExchange( - this.app.contactsKey, - message.sender - ); - const encryptedKeys = ChatEncryptionManager.decryptKeys( - message.encryptedKeys, - sharedSecret - ); - dbManager.createObject(RealmSchema.Contact, contact[0]); - community = { - id: message.communityId, - name: contact[0].name, - type: CommunityType.Peer, - createdAt: data.timestamp, - updatedAt: data.timestamp, - with: message.sender, - key: encryptedKeys.aesKey, - }; - dbManager.createObject(RealmSchema.Community, community); - 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, - }); - } + const sharedSecret = ChatEncryptionManager.deriveSharedSecret( + this.app.contactsKey.secretKey, + message.sender + ); + const decryptedKeys = ChatEncryptionManager.decryptKeys( + message.encryptedKeys, + sharedSecret + ); + const decryptedMessage = JSON.parse( + ChatEncryptionManager.decryptMessage(message, decryptedKeys.aesKey) + ); + community = { + id: message.communityId, + name: decryptedMessage.senderName || 'Unknown Contact', + type: CommunityType.Peer, + createdAt: data.timestamp, + updatedAt: data.timestamp, + with: message.sender, + key: decryptedKeys.aesKey, + }; + dbManager.createObject(RealmSchema.Community, community); + 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.key); const messageData = JSON.parse(decryptedMessage); @@ -238,58 +251,4 @@ export default class ChatPeerManager { console.error('Error storing messages:', error); } }; - - // Mock method for testing - returns random contact objects - mockGetWalletProfiles(contactKeys: string[]) { - const mockNames = [ - 'Alice Johnson', - 'Bob Smith', - 'Charlie Brown', - 'Diana Prince', - 'Eve Davis', - 'Frank Miller', - 'Grace Lee', - 'Henry Wilson', - 'Ivy Chen', - 'Jack Turner', - ]; - - const mockImageUrls = [ - 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alice', - 'https://api.dicebear.com/7.x/avataaars/svg?seed=Bob', - 'https://api.dicebear.com/7.x/avataaars/svg?seed=Charlie', - 'https://api.dicebear.com/7.x/avataaars/svg?seed=Diana', - 'https://api.dicebear.com/7.x/avataaars/svg?seed=Eve', - ]; - - const results = contactKeys.map((contactKey) => ({ - appID: `app_${Math.random().toString(36).substr(2, 9)}`, - contactKey, - name: mockNames[Math.floor(Math.random() * mockNames.length)], - imageUrl: mockImageUrls[Math.floor(Math.random() * mockImageUrls.length)], - })); - - return { - results, - success: true, - message: 'Mock wallet profiles retrieved successfully', - }; - } - - async syncContacts() { - try { - const contacts: Contact[] = dbManager.getCollection(RealmSchema.Contact) as any; - const response = await Relay.getAppProfiles(contacts.map((c) => c.contactKey)); - - if (response.profiles.length > 0) { - dbManager.createObjectBulk( - RealmSchema.Contact, - response.profiles, - Realm.UpdateMode.Modified - ); - } - } catch (error) { - console.error('Error syncing contacts:', error); - } - } } diff --git a/src/utils/service-utilities/ChatEncryptionManager.ts b/src/utils/service-utilities/ChatEncryptionManager.ts index 315392d5d..d2b005626 100644 --- a/src/utils/service-utilities/ChatEncryptionManager.ts +++ b/src/utils/service-utilities/ChatEncryptionManager.ts @@ -105,7 +105,7 @@ export class ChatEncryptionManager { privateKey: string | Uint8Array, publicKey: string | Uint8Array ): string { - const sharedSecret = x25519.getSharedSecret(privateKey, publicKey); + const sharedSecret = x25519.getSharedSecret(privateKey.slice(0, 64), publicKey.slice(0, 64)); return this.uint8ArrayToHex(sharedSecret); } From 983967b634eef9f2b50581704ff1eb71e1fb9cab Mon Sep 17 00:00:00 2001 From: Shashank Date: Tue, 29 Jul 2025 17:06:40 +0530 Subject: [PATCH 47/80] fix: chat init, key exchange related issues --- src/hooks/useChatPeer.ts | 77 ++++++++++++------- .../components/Contact/component/ChatList.tsx | 2 +- .../components/Contact/component/ChatRoom.tsx | 18 ++--- .../components/Contact/component/Contact.tsx | 54 ++++++++----- src/services/p2p/ChatPeerManager.ts | 72 ++++++++++++----- .../ChatEncryptionManager.ts | 14 ++-- 6 files changed, 154 insertions(+), 83 deletions(-) diff --git a/src/hooks/useChatPeer.ts b/src/hooks/useChatPeer.ts index bcc03e219..a819b4f2b 100644 --- a/src/hooks/useChatPeer.ts +++ b/src/hooks/useChatPeer.ts @@ -120,9 +120,9 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn }); } } - } else { - return true; } + + return success; } catch (err) { console.log('err', err); setError(err instanceof Error ? err.message : 'Failed to initialize chat peer'); @@ -134,30 +134,48 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn }, [keeperApp, enableMessageListener, enableConnectionListener]); // Send message - const sendMessage = useCallback(async (pubKey: string, message: string): Promise => { - try { - setError(null); - return await chatManager.sendMessage(pubKey, message); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to send message'); - captureError(err); - throw err; - } - }, []); + 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'); + } + } - // Join peer - const joinPeer = useCallback(async (pubKey: string): Promise => { - try { - setError(null); - return await chatManager.joinPeers(pubKey); - } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to join peer'); - captureError(err); - throw err; - } - }, []); + 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] + ); - // Load pending messages const loadPendingMessages = useCallback(async (lastBlock = 0): Promise => { try { setError(null); @@ -168,10 +186,16 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn } }, []); - // Get peers 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; @@ -180,9 +204,8 @@ export const useChatPeer = (options: UseChatPeerOptions = {}): UseChatPeerReturn captureError(err); throw err; } - }, []); + }, [isInitialized, initChatPeer]); - // Get keys const getKeys = useCallback((): { [key: string]: string; } => { diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index 0bd63f7b7..a130609b7 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -7,8 +7,8 @@ import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png import { useNavigation } from '@react-navigation/native'; import Fonts from 'src/constants/Fonts'; import { LocalizationContext } from 'src/context/Localization/LocContext'; -import { useChatPeer } from 'src/hooks/useChatPeer'; import { CommunityType } from 'src/services/p2p/interface'; +import { useChatPeer } from 'src/hooks/useChatPeer'; interface ChatItemData { id: string; diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index f26ecfd1f..aa48cc026 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -21,8 +21,8 @@ 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 { useChatPeer } from 'src/hooks/useChatPeer'; import { v4 as uuidv4 } from 'uuid'; +import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; const groupMessagesByDate = (msgs) => { const groups = {}; @@ -45,10 +45,9 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community const { colorMode } = useColorMode(); const isDarkMode = colorMode === 'dark'; const scrollRef = useRef(null); - const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); - const chatPeer = useChatPeer(); - - const handleSend = () => { + const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp) as any as KeeperApp; + const chatManager = ChatPeerManager.getInstance(); + const handleSend = async () => { if (!inputValue.trim()) return; try { const messageData = { @@ -66,7 +65,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community JSON.stringify({ ...messageData }), community.key ); - chatPeer.sendMessage( + await chatManager.sendMessage( community.with, JSON.stringify({ ...encryptedMessage, communityId: community.id }) ); @@ -107,10 +106,9 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community - {/* Messages */} - {msgs.map((msg, index) => { + {(msgs as any[]).map((msg, index) => { const thisMoment = moment(msg.date); - const nextMsg = msgs[index + 1]; + const nextMsg = (msgs as any[])[index + 1]; const nextMoment = nextMsg ? moment(nextMsg.date) : null; const isLastInMinuteAndSenderGroup = @@ -118,7 +116,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community !thisMoment.isSame(nextMoment, 'minute') || msg.sender !== nextMsg.sender; - const prevMsg = msgs[index - 1]; + const prevMsg = (msgs as any[])[index - 1]; const prevMoment = prevMsg ? moment(prevMsg.date) : null; const isFirstInGroup = diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index f262ae024..d5435c7df 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,6 +1,6 @@ import { Box, ScrollView, useColorMode } from 'native-base'; -import React, { useContext, useEffect, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; +import React, { useContext, useEffect, useMemo, 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'; @@ -16,7 +16,6 @@ import ProfileContent from './ProfileContent'; import { useNavigation } from '@react-navigation/native'; import ContactModalData from './ContactModalData'; import { LocalizationContext } from 'src/context/Localization/LocContext'; -import { useChatPeer } from 'src/hooks/useChatPeer'; import useToastMessage from 'src/hooks/useToastMessage'; import ToastErrorIcon from 'src/assets/images/toast_error.svg'; import { KeeperApp } from 'src/models/interfaces/KeeperApp'; @@ -44,11 +43,12 @@ const Contact = () => { const lastBlock = useQuery(RealmSchema.Message).sorted('block', true)[0]?.block; const communities = useQuery(RealmSchema.Community); const { showToast } = useToastMessage(); - const chatPeer = useChatPeer(); + const chatManager = ChatPeerManager.getInstance(); + const [refreshing, setRefreshing] = useState(false); const initializeChat = async () => { try { - const chatPeerInitialized = await chatPeer.initChatPeer(); + const chatPeerInitialized = await chatManager.init(app.primarySeed); if (!chatPeerInitialized) { throw new Error(); } @@ -59,43 +59,56 @@ const Contact = () => { } }; + const contactShareLink = useMemo(() => { + if (app?.contactsKey?.secretKey) { + const pubKey = ChatEncryptionManager.derivePublicKey(app?.contactsKey?.secretKey); + return `keeper://contact/${app.contactsKey.publicKey}/${pubKey}/${app.appName}`; + } + return ''; + }, [app.contactsKey.publicKey, app.appName]); + + const onRefresh = () => { + setRefreshing(true); + chatManager.loadPendingMessages(lastBlock); + setRefreshing(false); + }; + useEffect(() => { - if (!chatPeer.isInitialized) initializeChat(); - else chatPeer.loadPendingMessages(lastBlock); - }, [chatPeer.isInitialized]); + 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]; - const name = urlParts[4]; if (path === 'contact') { - const publicKey = urlParts[3]; - initChat(publicKey, name); + initChat(urlParts[3], urlParts[4], urlParts[5]); } } else { showToast('Invalid QR code', ); } }; - const initChat = async (publicKey: string, name: string = 'Unknown Contact') => { + const initChat = async (peerKey: string, publicKey: string, name: string) => { try { - const communityId = hash256([app.contactsKey.publicKey, publicKey].sort().join('-')); + 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, + name: name || 'Unknown Contact', createdAt: Date.now(), type: CommunityType.Peer, - with: publicKey, + with: peerKey, key: sessionKeys.aesKey, }); const message = { @@ -108,8 +121,10 @@ const Contact = () => { senderName: app.appName, unread: false, encryptedKeys: encryptedKeys, + pubKey: pubKey, }; - chatPeer.sendMessage(publicKey, JSON.stringify({ ...message, communityId })); + + chatManager.sendMessage(peerKey, JSON.stringify({ ...message, communityId })); dbManager.createObject(RealmSchema.Message, message); showToast('New Contact added', false); } @@ -132,7 +147,10 @@ const Contact = () => { {contactText.recentChats} - + } + showsVerticalScrollIndicator={false} + > { isShareContact={shareContact} setContactModalVisible={setContactModalVisible} navigation={navigation} - data={`keeper://contact/${app.contactsKey.publicKey}/${app.appName}`} + data={contactShareLink} onQrScan={onQrScan} /> )} diff --git a/src/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts index 008fd0933..d97118be9 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -12,7 +12,7 @@ import { RPC_KEY, SEND_MESSAGE, } from './rpc-commands.mjs'; -import { Community, CommunityType, Contact, Message } from './interface'; +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'; @@ -61,6 +61,23 @@ export default class ChatPeerManager { } }); 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, + }, + }); + } + } + } return true; } catch (error) { console.error('Error initializing chat peer manager:', error); @@ -69,6 +86,9 @@ export default class ChatPeerManager { } 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(); @@ -77,6 +97,9 @@ export default class ChatPeerManager { } 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(); @@ -85,6 +108,9 @@ export default class ChatPeerManager { } 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(); @@ -93,6 +119,9 @@ export default class ChatPeerManager { } 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(); @@ -126,7 +155,6 @@ export default class ChatPeerManager { this.app.contactsKey.publicKey, lastBlock === 0 ? 0 : lastBlock - 1 ); - console.log('response', response); if (response.messages.length > 0) { for (const msg of response.messages) { const message = JSON.parse(msg.message); @@ -135,28 +163,23 @@ export default class ChatPeerManager { if (!community) { const sharedSecret = ChatEncryptionManager.deriveSharedSecret( this.app.contactsKey.secretKey, - message.sender + message.pubKey ); const decryptedKey = ChatEncryptionManager.decryptKeys( message.encryptedKeys, sharedSecret ); - const contact = dbManager.getObjectByPrimaryId( - RealmSchema.Contact, - 'contactKey', - message.sender - ); - community = { + const communityData = { id: communityId, communityId: communityId, - name: contact.name, + name: message.senderName || 'Unknown Contact', type: CommunityType.Peer, createdAt: msg.timestamp, updatedAt: msg.timestamp, with: message.sender, key: decryptedKey.aesKey, }; - dbManager.createObject(RealmSchema.Community, community); + dbManager.createObject(RealmSchema.Community, communityData); dbManager.createObject(RealmSchema.Message, { id: message.id, @@ -171,7 +194,10 @@ export default class ChatPeerManager { request: message?.request, }); } else { - const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.key); + const decryptedMessage = ChatEncryptionManager.decryptMessage( + message, + (community as any).key + ); const messageData = JSON.parse(decryptedMessage); dbManager.createObject(RealmSchema.Message, { id: messageData.id, @@ -198,29 +224,30 @@ export default class ChatPeerManager { const communities = dbManager.getCollection(RealmSchema.Community); const data = JSON.parse(payload.data); const message = JSON.parse(data.message); - let community: Community = communities.find((c) => c.id === message.communityId); + 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.sender + message.pubKey ); + console.log('sharedSecret', sharedSecret); const decryptedKeys = ChatEncryptionManager.decryptKeys( message.encryptedKeys, sharedSecret ); - const decryptedMessage = JSON.parse( - ChatEncryptionManager.decryptMessage(message, decryptedKeys.aesKey) - ); - community = { + console.log('decryptedKeys', decryptedKeys); + + const communityData = { id: message.communityId, - name: decryptedMessage.senderName || 'Unknown Contact', + name: message.senderName || 'Unknown Contact', type: CommunityType.Peer, createdAt: data.timestamp, updatedAt: data.timestamp, with: message.sender, key: decryptedKeys.aesKey, }; - dbManager.createObject(RealmSchema.Community, community); + dbManager.createObject(RealmSchema.Community, communityData); dbManager.createObject(RealmSchema.Message, { id: message.id, communityId: message.communityId, @@ -233,7 +260,10 @@ export default class ChatPeerManager { fileUrl: message?.fileUrl, }); } else { - const decryptedMessage = ChatEncryptionManager.decryptMessage(message, community.key); + const decryptedMessage = ChatEncryptionManager.decryptMessage( + message, + (community as any).key + ); const messageData = JSON.parse(decryptedMessage); dbManager.createObject(RealmSchema.Message, { id: messageData.id, diff --git a/src/utils/service-utilities/ChatEncryptionManager.ts b/src/utils/service-utilities/ChatEncryptionManager.ts index d2b005626..e7e78dc71 100644 --- a/src/utils/service-utilities/ChatEncryptionManager.ts +++ b/src/utils/service-utilities/ChatEncryptionManager.ts @@ -1,6 +1,7 @@ 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 @@ -101,12 +102,13 @@ export class ChatEncryptionManager { } } - static deriveSharedSecret( - privateKey: string | Uint8Array, - publicKey: string | Uint8Array - ): string { - const sharedSecret = x25519.getSharedSecret(privateKey.slice(0, 64), publicKey.slice(0, 64)); - return this.uint8ArrayToHex(sharedSecret); + 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))); } /** From 17cf847508d9271be89a3628b7f9707aa52e245d Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Tue, 29 Jul 2025 17:41:19 +0530 Subject: [PATCH 48/80] upt: EmptyChatText --- src/context/Localization/language/en.json | 2 +- src/context/Localization/language/es.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index f6c5c740f..1c13513eb 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -2025,6 +2025,6 @@ "enterName": "Enter your name", "editContactName": "Edit Contact Name", "noContactYet": "No Contacts Yet", - "noContactDesc": " Please add your trusted contacts to send/receive sats, advice and the latest bitcoin updates." + "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } } \ No newline at end of file diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index f6c5c740f..1c13513eb 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -2025,6 +2025,6 @@ "enterName": "Enter your name", "editContactName": "Edit Contact Name", "noContactYet": "No Contacts Yet", - "noContactDesc": " Please add your trusted contacts to send/receive sats, advice and the latest bitcoin updates." + "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } } \ No newline at end of file From a8a9e9c45944e5ea044c989b426c73d58760e7e0 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Tue, 29 Jul 2025 17:41:34 +0530 Subject: [PATCH 49/80] fix: navigationOnContactsQrScan --- src/screens/Home/components/Contact/component/Contact.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index f262ae024..df11943cf 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -72,6 +72,7 @@ const Contact = () => { if (path === 'contact') { const publicKey = urlParts[3]; initChat(publicKey, name); + navigation.goBack(); } } else { showToast('Invalid QR code', ); From 6aac66b6ef83aeb4b45373d4f2df86ff24065c3d Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 30 Jul 2025 12:46:07 +0500 Subject: [PATCH 50/80] fixes --- .../Contact/component/ContactModalData.tsx | 79 ++++++++++--------- src/screens/Recieve/ReceiveAddress.tsx | 2 +- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ContactModalData.tsx b/src/screens/Home/components/Contact/component/ContactModalData.tsx index 4e0ae97c3..3e72e4668 100644 --- a/src/screens/Home/components/Contact/component/ContactModalData.tsx +++ b/src/screens/Home/components/Contact/component/ContactModalData.tsx @@ -138,46 +138,47 @@ function ContactModalData({ ); }, }, - { - 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'); - } - }, - }, + // 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, - }); - }, - }, - ] - : []), + // { + // 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 ( diff --git a/src/screens/Recieve/ReceiveAddress.tsx b/src/screens/Recieve/ReceiveAddress.tsx index 6ab3ac956..c844642f7 100644 --- a/src/screens/Recieve/ReceiveAddress.tsx +++ b/src/screens/Recieve/ReceiveAddress.tsx @@ -69,7 +69,7 @@ const styles = StyleSheet.create({ paddingHorizontal: wp(6), }, value: { - fontSize: 12, + fontSize: 11, lineHeight: 18, }, iconContainer: { From b9104c026c7de625b1d62f646aa3d225f9729d62 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 30 Jul 2025 15:44:53 +0500 Subject: [PATCH 51/80] fix chat screen --- .../components/Contact/ChatRoomScreen.tsx | 5 +- .../components/Contact/component/ChatList.tsx | 2 +- .../components/Contact/component/ChatRoom.tsx | 69 ++++++++++++------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/screens/Home/components/Contact/ChatRoomScreen.tsx b/src/screens/Home/components/Contact/ChatRoomScreen.tsx index 31a8f3b33..bf99ee142 100644 --- a/src/screens/Home/components/Contact/ChatRoomScreen.tsx +++ b/src/screens/Home/components/Contact/ChatRoomScreen.tsx @@ -31,11 +31,14 @@ const ChatRoomScreen = () => { .filtered('communityId = $0', communityId) .sorted('createdAt', true); + const [editUserProfileImage, setEditUserProfileImage] = useState(''); + const [editReceiverProfileName, setEditReceiverProfileName] = useState(community.name); + return ( {formatTime(item.date)} - {item.message_count > 0 && ( + {item.message_count > 0 && item.lastMessage !== 'Start of conversation' && ( {item.message_count} diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index aa48cc026..43c2e6164 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -14,8 +14,8 @@ import Text from 'src/components/KeeperText'; import { Platform, StyleSheet, TouchableOpacity } from 'react-native'; import { hp, wp } from 'src/constants/responsive'; import PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; -import PlusIcon from 'src/assets/images/plus-green-icon.svg'; -import PlusWhiteIcon from 'src/assets/images/add-plus-white.svg'; +// 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'; @@ -26,16 +26,21 @@ import ChatPeerManager from 'src/services/p2p/ChatPeerManager'; const groupMessagesByDate = (msgs) => { const groups = {}; - msgs.forEach((msg) => { - const date = moment(msg.date); + + 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; }; @@ -49,10 +54,14 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community const chatManager = ChatPeerManager.getInstance(); const handleSend = async () => { if (!inputValue.trim()) return; + + const text = inputValue.trim(); + setInputValue(''); + try { const messageData = { id: uuidv4(), - text: inputValue.trim(), + text, createdAt: Date.now(), type: 'TEXT', sender: app.contactsKey.publicKey, @@ -60,23 +69,28 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community 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 }) ); - setInputValue(''); } catch (error) { console.error('Error sending message:', error); + setInputValue(text); } + setTimeout(() => { scrollRef?.current?.scrollToEnd({ animated: true }); }, 0); }; + useEffect(() => { setTimeout(() => { scrollRef?.current?.scrollToEnd({ animated: true }); @@ -107,9 +121,10 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community {(msgs as any[]).map((msg, index) => { - const thisMoment = moment(msg.date); + const thisMoment = moment(msg.createdAt); const nextMsg = (msgs as any[])[index + 1]; - const nextMoment = nextMsg ? moment(nextMsg.date) : null; + const nextMoment = nextMsg ? moment(nextMsg.createdAt) : null; + const isMyMessage = msg.sender === app.contactsKey.publicKey; const isLastInMinuteAndSenderGroup = !nextMoment || @@ -117,7 +132,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community msg.sender !== nextMsg.sender; const prevMsg = (msgs as any[])[index - 1]; - const prevMoment = prevMsg ? moment(prevMsg.date) : null; + const prevMoment = prevMsg ? moment(prevMsg.createdAt) : null; const isFirstInGroup = !prevMoment || @@ -127,12 +142,12 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community return ( - {msg.sender === 'other' && + {!isMyMessage && isFirstInGroup && (receiverProfileImage ? ( - {msg.sender === 'me' && + {isMyMessage && isLastInMinuteAndSenderGroup && (userProfileImage ? ( - {isDarkMode ? ( + {/* For the future if needed */} + {/* {isDarkMode ? ( ) : ( )} - + */} Date: Tue, 29 Jul 2025 14:13:39 +0530 Subject: [PATCH 52/80] feat: OtherSignerHCFlow --- .../SigningDevices/SetupOtherSDScreen.tsx | 35 +++++++++++++------ src/screens/Vault/HardwareModalMap.tsx | 1 + 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/screens/SigningDevices/SetupOtherSDScreen.tsx b/src/screens/SigningDevices/SetupOtherSDScreen.tsx index f5e4a4798..f58c97599 100644 --- a/src/screens/SigningDevices/SetupOtherSDScreen.tsx +++ b/src/screens/SigningDevices/SetupOtherSDScreen.tsx @@ -93,7 +93,7 @@ function SetupOtherSDScreen({ route }) { IToastCategory.SIGNING_DEVICE ); } else if (mode === InteracationMode.HEALTH_CHECK) { - if (key.xpub === hcSigner.xpub) { + if (key.masterFingerprint === hcSigner.masterFingerprint) { dispatch( healthCheckStatusUpdate([ { @@ -102,7 +102,6 @@ function SetupOtherSDScreen({ route }) { }, ]) ); - navigation.dispatch(CommonActions.goBack()); showToast(signerText.otherSignerVerified, ); } else { dispatch( @@ -167,15 +166,29 @@ function SetupOtherSDScreen({ route }) { }); if (signer) { dispatch(addSigningDevice([signer])); - const navigationState = addSignerFlow - ? { name: 'Home' } - : { name: 'AddSigningDevice', merge: true, params: {} }; - navigation.dispatch(CommonActions.navigate(navigationState)); - showToast( - signerText.signerAddedSuccessMessage, - , - IToastCategory.SIGNING_DEVICE - ); + if (mode === InteracationMode.VAULT_ADDITION) { + navigation.dispatch(CommonActions.navigate({ name: 'Home' })); + showToast( + signerText.signerAddedSuccessMessage, + , + IToastCategory.SIGNING_DEVICE + ); + } else if (mode === InteracationMode.HEALTH_CHECK) { + navigation.goBack(); + if (signer.masterFingerprint == hcSigner.masterFingerprint) { + dispatch( + healthCheckStatusUpdate([ + { + signerId: signer.masterFingerprint, + status: hcStatusType.HEALTH_CHECK_SUCCESSFULL, + }, + ]) + ); + showToast(signerText.otherSignerVerified, ); + } else { + showToast(common.somethingWrong, ); + } + } } } else { throw new HWError(HWErrorType.INVALID_SIG); diff --git a/src/screens/Vault/HardwareModalMap.tsx b/src/screens/Vault/HardwareModalMap.tsx index 74c772267..b71fd4b18 100644 --- a/src/screens/Vault/HardwareModalMap.tsx +++ b/src/screens/Vault/HardwareModalMap.tsx @@ -1297,6 +1297,7 @@ function HardwareModalMap({ addSignerFlow, Illustration, Instructions, + signer, }, }) ); From 2fc4bcd2188f6920e56a2543a6d8990f2cb65546 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 23 Jul 2025 16:19:00 +0500 Subject: [PATCH 53/80] home screen --- src/assets/images/ask-question.svg | 10 ++ src/assets/images/connect-advisor.svg | 4 + src/assets/images/hire-advisor.svg | 4 + src/assets/images/take-me-there.svg | 11 ++ src/context/Localization/language/en.json | 8 +- src/context/Localization/language/es.json | 8 +- .../Home/components/ConciergeComponent.tsx | 110 ++++++++++++++++++ 7 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 src/assets/images/ask-question.svg create mode 100644 src/assets/images/connect-advisor.svg create mode 100644 src/assets/images/hire-advisor.svg create mode 100644 src/assets/images/take-me-there.svg create mode 100644 src/screens/Home/components/ConciergeComponent.tsx diff --git a/src/assets/images/ask-question.svg b/src/assets/images/ask-question.svg new file mode 100644 index 000000000..f2ef67ee4 --- /dev/null +++ b/src/assets/images/ask-question.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/connect-advisor.svg b/src/assets/images/connect-advisor.svg new file mode 100644 index 000000000..22b5ae273 --- /dev/null +++ b/src/assets/images/connect-advisor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/hire-advisor.svg b/src/assets/images/hire-advisor.svg new file mode 100644 index 000000000..86e206e66 --- /dev/null +++ b/src/assets/images/hire-advisor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/take-me-there.svg b/src/assets/images/take-me-there.svg new file mode 100644 index 000000000..bed6f7980 --- /dev/null +++ b/src/assets/images/take-me-there.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index 1c13513eb..a95d411f6 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1760,7 +1760,13 @@ "keeperConciergeTeamHelp": "If you need assistance or face an issue, please feel free to reach out to our Keeper Concierge team to help you with any question!", "scheduleOnboardingCall": "Schedule Onboarding Call", "scheduleCallWithExpert": "Schedule a call with our experts today.", - "supportTeam": "Support Team" + "supportTeam": "Support Team", + "askQuestion": "Ask us a question.", + "submitTicket": "Submit a ticket to get all your queries answered.", + "takeMeThere": "Take me there", + "hireAdvisor": "Hire an Advisor", + "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", + "connetWithAdvisor": "Connect with an Advisor" }, "buyBTC": { "acquire": "Acquire", diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index 1c13513eb..a95d411f6 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1760,7 +1760,13 @@ "keeperConciergeTeamHelp": "If you need assistance or face an issue, please feel free to reach out to our Keeper Concierge team to help you with any question!", "scheduleOnboardingCall": "Schedule Onboarding Call", "scheduleCallWithExpert": "Schedule a call with our experts today.", - "supportTeam": "Support Team" + "supportTeam": "Support Team", + "askQuestion": "Ask us a question.", + "submitTicket": "Submit a ticket to get all your queries answered.", + "takeMeThere": "Take me there", + "hireAdvisor": "Hire an Advisor", + "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", + "connetWithAdvisor": "Connect with an Advisor" }, "buyBTC": { "acquire": "Acquire", diff --git a/src/screens/Home/components/ConciergeComponent.tsx b/src/screens/Home/components/ConciergeComponent.tsx new file mode 100644 index 000000000..6c79df511 --- /dev/null +++ b/src/screens/Home/components/ConciergeComponent.tsx @@ -0,0 +1,110 @@ +import { Box, useColorMode } from 'native-base'; +import React, { useContext } from 'react'; +import { StyleSheet } from 'react-native'; +import Buttons from 'src/components/Buttons'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import Text from 'src/components/KeeperText'; +import { hp, windowWidth, wp } from 'src/constants/responsive'; +import AskQuestionIcon from 'src/assets/images/ask-question.svg'; +import HireAdvisorIcon from 'src/assets/images/hire-advisor.svg'; +import TakeMeThereIcon from 'src/assets/images/take-me-there.svg'; +import ConnectAdvisor from 'src/assets/images/connect-advisor.svg'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; + +type ConciergeItem = { + title: string; + subtitle: string; + iconName: any; + buttonText: string; + callback: () => void; + buttonIcon?: any; +}; + +const ConciergeComponent = () => { + const { colorMode } = useColorMode(); + const { translations } = useContext(LocalizationContext); + const { concierge: conciergeText } = translations; + + const conciergeData: ConciergeItem[] = [ + { + title: conciergeText.askQuestion, + subtitle: conciergeText.submitTicket, + iconName: , + buttonText: conciergeText.takeMeThere, + callback: () => {}, + buttonIcon: TakeMeThereIcon, + }, + { + title: conciergeText.hireAdvisor, + subtitle: conciergeText.whiteGlovedService, + iconName: , + buttonText: conciergeText.connetWithAdvisor, + callback: () => {}, + buttonIcon: ConnectAdvisor, + }, + ]; + + return ( + + {conciergeData.map((item, index) => ( + + + + + + {item.title} + + + {item.subtitle} + + + + + + + + ))} + + ); +}; + +export default ConciergeComponent; + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingHorizontal: wp(22), + paddingVertical: hp(10), + }, + Card: { + width: windowWidth * 0.88, + borderWidth: 1, + padding: wp(20), + gap: 10, + borderRadius: 10, + marginBottom: hp(20), + }, + iconContainer: { + flexDirection: 'row', + gap: 15, + marginBottom: hp(10), + }, + textContainer: { + width: wp(220), + gap: 5, + }, +}); From 94b94384e64f8f27bd8bd12f05f11ea55d66065a Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 31 Jul 2025 17:42:49 +0530 Subject: [PATCH 54/80] feat: PersistedContactsProfilePicture --- src/models/interfaces/KeeperApp.ts | 1 + src/screens/Home/components/Contact/component/Contact.tsx | 2 +- .../Home/components/Contact/component/ProfileContent.tsx | 3 +++ src/storage/realm/realm.ts | 2 +- src/storage/realm/schema/app.ts | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/models/interfaces/KeeperApp.ts b/src/models/interfaces/KeeperApp.ts index 5d4a91f78..fe1081151 100644 --- a/src/models/interfaces/KeeperApp.ts +++ b/src/models/interfaces/KeeperApp.ts @@ -21,4 +21,5 @@ export interface KeeperApp { subscription: SubScription; enableAnalytics: boolean; contactsKey?: { [key: string]: string }; + profilePicture?: string; } diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index e834812ba..dd137d86f 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -137,7 +137,7 @@ const Contact = () => { return ( { try { + const persistedImage = await persistDocument(profileImage); dbManager.updateObjectById(RealmSchema.KeeperApp, app.id, { appName: profileName, + profilePicture: persistedImage, }); setUserProfileImage(profileImage); setUserProfileName(profileName); diff --git a/src/storage/realm/realm.ts b/src/storage/realm/realm.ts index 6d63a4af9..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 = 108; + 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 ff634e395..696ac467c 100644 --- a/src/storage/realm/schema/app.ts +++ b/src/storage/realm/schema/app.ts @@ -16,6 +16,7 @@ export const KeeperAppSchema: ObjectSchema = { backup: RealmSchema.Backup, enableAnalytics: { type: 'bool', default: false }, contactsKey: 'string?{}', + profilePicture: 'string?', }, primaryKey: 'id', }; From 934cc1d84456d91008c586fcdbdcc5fcf26960de Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 23 Jul 2025 16:38:44 +0500 Subject: [PATCH 55/80] keeper concierge --- .../Home/components/ConciergeComponent.tsx | 8 +++-- src/screens/KeeperConcierge/KeeperSupport.tsx | 34 +++++++++++++++++++ .../KeeperConcierge/TechnicalSupport.tsx | 1 - 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/screens/KeeperConcierge/KeeperSupport.tsx diff --git a/src/screens/Home/components/ConciergeComponent.tsx b/src/screens/Home/components/ConciergeComponent.tsx index 6c79df511..03e49fb7d 100644 --- a/src/screens/Home/components/ConciergeComponent.tsx +++ b/src/screens/Home/components/ConciergeComponent.tsx @@ -10,6 +10,7 @@ import HireAdvisorIcon from 'src/assets/images/hire-advisor.svg'; import TakeMeThereIcon from 'src/assets/images/take-me-there.svg'; import ConnectAdvisor from 'src/assets/images/connect-advisor.svg'; import { LocalizationContext } from 'src/context/Localization/LocContext'; +import { useNavigation } from '@react-navigation/native'; type ConciergeItem = { title: string; @@ -20,10 +21,11 @@ type ConciergeItem = { buttonIcon?: any; }; -const ConciergeComponent = () => { +const ConciergeComponent = ({ route }) => { const { colorMode } = useColorMode(); const { translations } = useContext(LocalizationContext); const { concierge: conciergeText } = translations; + const navigation = useNavigation(); const conciergeData: ConciergeItem[] = [ { @@ -31,7 +33,9 @@ const ConciergeComponent = () => { subtitle: conciergeText.submitTicket, iconName: , buttonText: conciergeText.takeMeThere, - callback: () => {}, + callback: () => { + navigation.navigate('KeeperSupport'); + }, buttonIcon: TakeMeThereIcon, }, { diff --git a/src/screens/KeeperConcierge/KeeperSupport.tsx b/src/screens/KeeperConcierge/KeeperSupport.tsx new file mode 100644 index 000000000..b2996fa5c --- /dev/null +++ b/src/screens/KeeperConcierge/KeeperSupport.tsx @@ -0,0 +1,34 @@ +import { useNavigation } from '@react-navigation/native'; +import { Box, useColorMode } from 'native-base'; +import React from 'react'; +import { StatusBar, StyleSheet } from 'react-native'; +import ScreenWrapper from 'src/components/ScreenWrapper'; +import { wp } from 'src/constants/responsive'; +import TechnicalSupport from '../KeeperConcierge/TechnicalSupport'; +import WalletHeader from 'src/components/WalletHeader'; + +const KeeperSupport = () => { + 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/KeeperConcierge/TechnicalSupport.tsx b/src/screens/KeeperConcierge/TechnicalSupport.tsx index 5c2c53a9c..4417813e9 100644 --- a/src/screens/KeeperConcierge/TechnicalSupport.tsx +++ b/src/screens/KeeperConcierge/TechnicalSupport.tsx @@ -1,6 +1,5 @@ import { Box, useColorMode } from 'native-base'; import React, { useContext, useEffect, useState } from 'react'; -import ConciergeScreenWrapper from './components/ConciergeScreenWrapper'; import ContentWrapper from 'src/components/ContentWrapper'; import { Image, StyleSheet, TouchableOpacity } from 'react-native'; import { hp, wp } from 'src/constants/responsive'; From 8e2640aaa61ea755085dd461872ad26506c9c388 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 24 Jul 2025 15:29:09 +0500 Subject: [PATCH 56/80] advisor screen --- src/assets/images/MapPinIcon.svg | 4 + src/assets/images/filter-icon.svg | 3 + src/assets/images/view-profile.svg | 11 ++ src/navigation/Navigator.tsx | 4 + src/screens/Advisors/Advisors.tsx | 101 ++++++++++++ src/screens/Advisors/FilterAdvisor.tsx | 16 ++ .../Advisors/component/AdvisorCard.tsx | 150 ++++++++++++++++++ .../Home/components/ConciergeComponent.tsx | 4 +- src/theme/Colors.ts | 1 + 9 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/assets/images/MapPinIcon.svg create mode 100644 src/assets/images/filter-icon.svg create mode 100644 src/assets/images/view-profile.svg create mode 100644 src/screens/Advisors/Advisors.tsx create mode 100644 src/screens/Advisors/FilterAdvisor.tsx create mode 100644 src/screens/Advisors/component/AdvisorCard.tsx diff --git a/src/assets/images/MapPinIcon.svg b/src/assets/images/MapPinIcon.svg new file mode 100644 index 000000000..b1c0a388e --- /dev/null +++ b/src/assets/images/MapPinIcon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/filter-icon.svg b/src/assets/images/filter-icon.svg new file mode 100644 index 000000000..f1c99cc6c --- /dev/null +++ b/src/assets/images/filter-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/view-profile.svg b/src/assets/images/view-profile.svg new file mode 100644 index 000000000..85386cf82 --- /dev/null +++ b/src/assets/images/view-profile.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index b9a1dcfe5..87a8a6a0b 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -164,6 +164,8 @@ import { SwapDetails } from 'src/screens/Home/components/buyBtc/Swap/SwapDetails import { SwapHistory } from 'src/screens/Home/components/buyBtc/Swap/SwapHistory'; import { SwapHistoryDetail } from 'src/screens/Home/components/buyBtc/Swap/SwapHistoryDetail'; import { SwapAllHistory } from 'src/screens/Home/components/buyBtc/Swap/SwapAllHistory'; +import Advisors from 'src/screens/Advisors/Advisors'; +import FilterAdvisor from 'src/screens/Advisors/FilterAdvisor'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -359,6 +361,8 @@ function AppStack() { + + ); diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx new file mode 100644 index 000000000..20f5e9529 --- /dev/null +++ b/src/screens/Advisors/Advisors.tsx @@ -0,0 +1,101 @@ +import { Box, FlatList, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; +import CircleIconWrapper from 'src/components/CircleIconWrapper'; +import ScreenWrapper from 'src/components/ScreenWrapper'; +import WalletHeader from 'src/components/WalletHeader'; +import { wp } from 'src/constants/responsive'; +import FilterIcon from 'src/assets/images/filter-icon.svg'; +import { useNavigation } from '@react-navigation/native'; +import KeeperTextInput from 'src/components/KeeperTextInput'; +import Text from 'src/components/KeeperText'; +import AdvisorCard from './component/AdvisorCard'; + +const dummyAdvisors = [ + { + expertise: ['Hardware Purchase', 'Multisig Setup'], + languages: ['Portuguese', 'English', 'Español'], + title: 'Diy Sec Lab', + country: 'Brazil', + description: + 'DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.', + image: 'https://bitcoinkeeper.app/public_assets_email/diySecLab.png', + link: 'https://calendly.com/vaibhav-cakesofttech/30min', + duration: '30 mins', + experience: '4 Years', + timezone: 'GMT - 3', + }, + { + expertise: ['Seed Backup', 'Cold Wallet Recovery', 'Multisig Setup'], + languages: ['English', 'German'], + title: 'SecureVault Pro', + country: 'Germany', + description: + 'SecureVault Pro helps clients recover lost access and offers consultation on safe Bitcoin cold storage setups.', + image: 'https://bitcoinkeeper.app/public_assets_email/diySecLab.png', + link: 'https://calendly.com/sample-link', + duration: '45 mins', + experience: '6 Years', + timezone: 'GMT + 1', + }, +]; + +const Advisors = () => { + const { colorMode } = useColorMode(); + const navigation = useNavigation(); + return ( + + navigation.navigate('FilterAdvisor')} + style={styles.filterIcon} + > + } + width={wp(30)} + backgroundColor={`${colorMode}.pantoneGreen`} + /> + + } + /> + + {}} + inpuBorderColor={`${colorMode}.pantoneGreen`} + /> + + + + Meet Our Experts + + + index.toString()} + renderItem={({ item }) => } + contentContainerStyle={{ paddingBottom: wp(20) }} + showsVerticalScrollIndicator={false} + /> + + + ); +}; + +export default Advisors; + +const styles = StyleSheet.create({ + filterIcon: { + marginRight: wp(10), + }, + searchContainer: { + marginTop: wp(10), + marginBottom: wp(10), + }, + Container: { + flex: 1, + marginVertical: wp(8), + }, +}); diff --git a/src/screens/Advisors/FilterAdvisor.tsx b/src/screens/Advisors/FilterAdvisor.tsx new file mode 100644 index 000000000..3e388909c --- /dev/null +++ b/src/screens/Advisors/FilterAdvisor.tsx @@ -0,0 +1,16 @@ +import { useColorMode } from 'native-base'; +import React from 'react'; +import ScreenWrapper from 'src/components/ScreenWrapper'; +import WalletHeader from 'src/components/WalletHeader'; + +const FilterAdvisor = () => { + const { colorMode } = useColorMode(); + + return ( + + + + ); +}; + +export default FilterAdvisor; diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx new file mode 100644 index 000000000..609b891f2 --- /dev/null +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -0,0 +1,150 @@ +import { Box, Image, ScrollView, useColorMode } from 'native-base'; +import React from 'react'; +import { StyleSheet } from 'react-native'; +import Text from 'src/components/KeeperText'; +import { hp, wp } from 'src/constants/responsive'; +import Colors from 'src/theme/Colors'; +import MapPin from 'src/assets/images/MapPinIcon.svg'; +import Buttons from 'src/components/Buttons'; +import ViewProfile from 'src/assets/images/view-profile.svg'; + +type Props = { + advisor?: any; +}; + +function ExpertiesPill({ name }: { name: string }) { + return ( + + + {name} + + + ); +} + +const AdvisorCard = ({ advisor }: Props) => { + const { colorMode } = useColorMode(); + + return ( + + + + + + + + + {advisor.title} + + + + {advisor.country} + + + + + + + + {advisor?.expertise?.map((item, index) => ( + + ))} + + + + + + Time zone: + + + {advisor.timezone} + + + + + Language: + + + {advisor.languages.join(', ')} + + + + + + + + + ); +}; + +export default AdvisorCard; + +const styles = StyleSheet.create({ + container: { + padding: wp(20), + borderWidth: 1, + borderRadius: 10, + marginBottom: wp(15), + top: wp(25), + overflow: 'hidden', + }, + header: { + flexDirection: 'row', + gap: wp(10), + marginBottom: wp(10), + }, + circle: { + width: wp(40), + height: wp(40), + borderRadius: 30, + justifyContent: 'center', + alignItems: 'center', + borderWidth: 1, + }, + pinContainer: { + flexDirection: 'row', + alignItems: 'center', + marginTop: wp(5), + marginBottom: wp(10), + gap: wp(5), + }, + PillsContainer: { + flexDirection: 'row', + gap: wp(5), + paddingRight: wp(20), + marginRight: wp(30), + }, + pillsScrollWrapper: { + height: wp(30), + marginBottom: wp(8), + }, + pill: { + paddingHorizontal: wp(10), + height: wp(22), + justifyContent: 'center', + alignItems: 'center', + borderRadius: wp(30), + }, + timeContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: wp(5), + marginBottom: wp(10), + }, + ButtonContainer: { + marginTop: wp(5), + }, + image: { + width: wp(35), + height: hp(20), + borderRadius: 10, + }, +}); diff --git a/src/screens/Home/components/ConciergeComponent.tsx b/src/screens/Home/components/ConciergeComponent.tsx index 03e49fb7d..6afa240ea 100644 --- a/src/screens/Home/components/ConciergeComponent.tsx +++ b/src/screens/Home/components/ConciergeComponent.tsx @@ -43,7 +43,9 @@ const ConciergeComponent = ({ route }) => { subtitle: conciergeText.whiteGlovedService, iconName: , buttonText: conciergeText.connetWithAdvisor, - callback: () => {}, + callback: () => { + navigation.navigate('Advisors'); + }, buttonIcon: ConnectAdvisor, }, ]; diff --git a/src/theme/Colors.ts b/src/theme/Colors.ts index 6b1de0c7d..805be5f94 100644 --- a/src/theme/Colors.ts +++ b/src/theme/Colors.ts @@ -157,5 +157,6 @@ const Colors = { coolGrey: 'rgba(211, 209, 206, 1)', lightstone: 'rgba(121, 121, 121, 1)', lightRed: 'rgba(217, 44, 44, 0.1)', + lightGrayBeige: 'rgba(114, 114, 114, 1)', }; export default Colors; From a374c6da8c6ba12eab6af38107201155bfdd8a49 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 25 Jul 2025 11:54:11 +0500 Subject: [PATCH 57/80] advisor detail --- src/assets/images/profile-arrow.svg | 3 + src/navigation/Navigator.tsx | 2 + src/screens/Advisors/AdvisorDetail.tsx | 237 ++++++++++++++++++ src/screens/Advisors/Advisors.tsx | 5 +- .../Advisors/component/AdvisorCard.tsx | 41 ++- .../component/AdvisorProfileHeader.tsx | 96 +++++++ 6 files changed, 371 insertions(+), 13 deletions(-) create mode 100644 src/assets/images/profile-arrow.svg create mode 100644 src/screens/Advisors/AdvisorDetail.tsx create mode 100644 src/screens/Advisors/component/AdvisorProfileHeader.tsx diff --git a/src/assets/images/profile-arrow.svg b/src/assets/images/profile-arrow.svg new file mode 100644 index 000000000..97cf5e83d --- /dev/null +++ b/src/assets/images/profile-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index 87a8a6a0b..0eab3e53d 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -166,6 +166,7 @@ import { SwapHistoryDetail } from 'src/screens/Home/components/buyBtc/Swap/SwapH import { SwapAllHistory } from 'src/screens/Home/components/buyBtc/Swap/SwapAllHistory'; import Advisors from 'src/screens/Advisors/Advisors'; import FilterAdvisor from 'src/screens/Advisors/FilterAdvisor'; +import AdvisorDetail from 'src/screens/Advisors/AdvisorDetail'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -363,6 +364,7 @@ function AppStack() { + ); diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx new file mode 100644 index 000000000..3d5840436 --- /dev/null +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -0,0 +1,237 @@ +import React, { useState } from 'react'; +import { View, StyleSheet } from 'react-native'; +import AdvisorProfileHeader from './component/AdvisorProfileHeader'; +import { wp } from 'src/constants/responsive'; +import { Box, useColorMode } from 'native-base'; +import MapPin from 'src/assets/images/MapPinIcon.svg'; +import Colors from 'src/theme/Colors'; +import Text from 'src/components/KeeperText'; +import { getUniqueRandomColors } from './component/AdvisorCard'; +import KeeperModal from 'src/components/KeeperModal'; +import openLink from 'src/utils/OpenLink'; +import Buttons from 'src/components/Buttons'; + +const ADVISOR_DETAILS = [ + { title: 'Time zone', key: 'timezone' }, + { title: 'Experience', key: 'experience' }, + { title: 'Language', key: 'languages' }, + { title: 'Session Duration', key: 'duration' }, +]; + +function DetailCard({ title, desc, isLast }) { + const { colorMode } = useColorMode(); + return ( + + + + {title} + + + + + {desc} + + + + ); +} + +const AdvisorDetail = ({ route }) => { + const { advisor } = route.params; + const { colorMode } = useColorMode(); + const [showModal, setShowModal] = useState(false); + const previewLength = 160; + const isLong = advisor.description.length > previewLength; + const previewText = isLong + ? `${advisor.description.slice(0, previewLength)}...` + : advisor.description; + + function ExpertiesPill({ name }: { name: string }) { + const backgroundColor = getUniqueRandomColors(advisor?.expertise.length)[0]; + + return ( + + + {name} + + + ); + } + + return ( + + + + + + + {advisor.title} + + + + + {advisor.country} + + + + + + {advisor?.expertise?.map((item, index) => ( + + ))} + + + + + + Advisor Detail: + + + {previewText} + {isLong && ( + setShowModal(true)}> + {' '} + Read more + + )} + + + + + + Advisor Details + + + {ADVISOR_DETAILS.map((item, index) => { + const desc = + item.key === 'languages' ? advisor.languages.join(', ') : advisor[item.key]; + return ( + + ); + })} + + + + + + + + + openLink(advisor.link)} + fullWidth + /> + + + setShowModal(false)} + title={advisor.title} + modalBackground={`${colorMode}.modalWhiteBackground`} + textColor={`${colorMode}.textGreen`} + subTitleColor={`${colorMode}.modalSubtitleBlack`} + Content={() => ( + + + {advisor.description} + + + )} + buttonText="Schedule your call" + buttonCallback={() => { + setShowModal(false); + openLink(advisor.link); + }} + /> + + ); +}; + +export default AdvisorDetail; + +const styles = StyleSheet.create({ + container: { + paddingHorizontal: wp(22), + paddingTop: wp(65), + }, + scrollContent: { + paddingBottom: wp(120), + }, + pinContainer: { + flexDirection: 'row', + alignItems: 'center', + marginTop: wp(5), + marginBottom: wp(10), + gap: wp(5), + }, + PillsContainer: { + flexDirection: 'row', + gap: wp(5), + paddingRight: wp(20), + marginRight: wp(30), + }, + pillsScrollWrapper: { + height: wp(30), + marginBottom: wp(8), + }, + pill: { + paddingHorizontal: wp(10), + height: wp(22), + justifyContent: 'center', + alignItems: 'center', + borderRadius: wp(30), + }, + advisorContainer: { + gap: wp(6), + marginBottom: wp(20), + marginTop: wp(10), + }, + modalContent: { + marginTop: wp(-10), + }, + detailsContainer: { + paddingHorizontal: wp(23), + paddingVertical: wp(18), + paddingRight: wp(10), + borderWidth: 1, + borderRadius: 10, + marginTop: wp(8), + }, + detailBox: { + flexDirection: 'row', + paddingBottom: wp(10), + marginBottom: wp(10), + borderBottomWidth: 1, + }, + detailColumnLeft: { + flex: 1, + paddingRight: wp(50), + }, + detailColumnRight: { + flex: 1, + alignItems: 'flex-start', + }, + fixedButtonContainer: { + position: 'absolute', + bottom: wp(20), + left: wp(20), + right: wp(20), + marginBottom: wp(10), + }, +}); diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 20f5e9529..9f34d4e9f 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -18,7 +18,7 @@ const dummyAdvisors = [ title: 'Diy Sec Lab', country: 'Brazil', description: - 'DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.', + 'DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.', image: 'https://bitcoinkeeper.app/public_assets_email/diySecLab.png', link: 'https://calendly.com/vaibhav-cakesofttech/30min', duration: '30 mins', @@ -42,6 +42,7 @@ const dummyAdvisors = [ const Advisors = () => { const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; const navigation = useNavigation(); return ( @@ -64,7 +65,7 @@ const Advisors = () => { {}} - inpuBorderColor={`${colorMode}.pantoneGreen`} + inpuBorderColor={isDarkMode ? `${colorMode}.separator` : `${colorMode}.pantoneGreen`} /> diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx index 609b891f2..4b3c55c98 100644 --- a/src/screens/Advisors/component/AdvisorCard.tsx +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -7,23 +7,35 @@ import Colors from 'src/theme/Colors'; import MapPin from 'src/assets/images/MapPinIcon.svg'; import Buttons from 'src/components/Buttons'; import ViewProfile from 'src/assets/images/view-profile.svg'; +import { useNavigation } from '@react-navigation/native'; type Props = { advisor?: any; }; +export function getUniqueRandomColors(count: number): string[] { + const tagColors = Object.entries(Colors) + .filter(([key]) => key.startsWith('TagLight')) + .map(([, value]) => value); -function ExpertiesPill({ name }: { name: string }) { - return ( - - - {name} - - - ); + const shuffled = [...tagColors].sort(() => 0.5 - Math.random()); + return shuffled.slice(0, count); } const AdvisorCard = ({ advisor }: Props) => { const { colorMode } = useColorMode(); + const navigation = useNavigation(); + + function ExpertiesPill({ name }: { name: string }) { + const backgroundColor = getUniqueRandomColors(advisor?.expertise.length); + + return ( + + + {name} + + + ); + } return ( { borderColor={`${colorMode}.pantoneGreen`} backgroundColor={Colors.headerWhite} > - + image - {advisor.title} + + {advisor.title} + @@ -79,7 +93,12 @@ const AdvisorCard = ({ advisor }: Props) => { - + navigation.navigate('AdvisorDetail', { advisor })} + /> ); diff --git a/src/screens/Advisors/component/AdvisorProfileHeader.tsx b/src/screens/Advisors/component/AdvisorProfileHeader.tsx new file mode 100644 index 000000000..f78cadbcf --- /dev/null +++ b/src/screens/Advisors/component/AdvisorProfileHeader.tsx @@ -0,0 +1,96 @@ +import { Box, Image } from 'native-base'; +import React from 'react'; +import { StatusBar, StyleSheet, TouchableOpacity } from 'react-native'; +import Text from 'src/components/KeeperText'; +import ThemedColor from 'src/components/ThemedColor/ThemedColor'; +import Fonts from 'src/constants/Fonts'; +import { hp, wp } from 'src/constants/responsive'; +import BackWhiteButton from 'src/assets/images/leftarrowCampainlight.svg'; +import ProfileArrow from 'src/assets/images/profile-arrow.svg'; +import { useNavigation } from '@react-navigation/native'; +import Colors from 'src/theme/Colors'; + +const AdvisorProfileHeader = ({ advisorImage }) => { + const backgroundColor = ThemedColor({ name: 'homeScreen_header_background' }); + + const navigation = useNavigation(); + + return ( + + + + + + { + navigation.goBack(); + }} + style={styles.backButton} + > + + + + Expert Profile + + + + + + + + + + image + + + ); +}; + +export default AdvisorProfileHeader; + +const styles = StyleSheet.create({ + padding: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: hp(64), + }, + + wrapper: { + paddingHorizontal: wp(5), + height: hp(164), + width: '100%', + alignItems: 'center', + justifyContent: 'flex-end', + position: 'relative', + }, + headerData: { + flexDirection: 'row', + alignItems: 'center', + }, + headerText: { + fontSize: 18, + fontFamily: Fonts.LoraMedium, + }, + backButton: { + height: hp(40), + width: wp(25), + justifyContent: 'center', + }, + circle: { + position: 'absolute', + bottom: -hp(50), + left: wp(22), + width: wp(105), + height: wp(105), + borderRadius: 70, + justifyContent: 'center', + alignItems: 'center', + borderWidth: 1, + }, + image: { + width: wp(70), + height: hp(70), + borderRadius: 10, + }, +}); From ee080e3248821e5b9465a89b674637b6440cb941 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 25 Jul 2025 12:27:11 +0500 Subject: [PATCH 58/80] filter page --- src/screens/Advisors/Advisors.tsx | 25 +++--- src/screens/Advisors/FilterAdvisor.tsx | 104 ++++++++++++++++++++++++- 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 9f34d4e9f..098f245ca 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -48,18 +48,19 @@ const Advisors = () => { navigation.navigate('FilterAdvisor')} - style={styles.filterIcon} - > - } - width={wp(30)} - backgroundColor={`${colorMode}.pantoneGreen`} - /> - - } + // Filter remove for now + // rightComponent={ + // navigation.navigate('FilterAdvisor')} + // style={styles.filterIcon} + // > + // } + // width={wp(30)} + // backgroundColor={`${colorMode}.pantoneGreen`} + // /> + // + // } /> { const { colorMode } = useColorMode(); @@ -9,8 +14,105 @@ const FilterAdvisor = () => { return ( + + {/* country */} + + Choose a Country + {}}> + + + + Choose country + + + + + + + {/* TimeZone */} + + Time zone + + Choose your preferred time zone + + {}}> + + + Choose time zone{' '} + + + + + + + {/* Language */} + + Language + + Choose your language preference + + {}}> + + + Choose language + + + + + + {/* Experties */} + + Area of expertise + + What do you need help with? + + {}}> + + Choose Expertise + + + + + + + ); }; export default FilterAdvisor; + +const styles = StyleSheet.create({ + selectingWallet: { + minHeight: wp(45), + borderRadius: wp(10), + paddingHorizontal: wp(16), + paddingVertical: wp(18), + borderWidth: 1, + marginBottom: wp(10), + marginTop: wp(5), + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + container: { + gap: wp(10), + marginTop: wp(10), + }, +}); From 20ffc6264de945b4935155038adf264821d71a4e Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Fri, 25 Jul 2025 14:49:31 +0500 Subject: [PATCH 59/80] dark and private --- src/assets/images/advisor-white-icon.svg | 4 ++ src/assets/images/concierge-white-icon.svg | 10 +++++ src/screens/Advisors/AdvisorDetail.tsx | 42 ++++++++++++------- src/screens/Advisors/Advisors.tsx | 16 +++++-- .../Advisors/component/AdvisorCard.tsx | 31 ++++++++------ .../component/AdvisorProfileHeader.tsx | 27 ++++++++---- .../Home/components/ConciergeComponent.tsx | 9 ++-- 7 files changed, 98 insertions(+), 41 deletions(-) create mode 100644 src/assets/images/advisor-white-icon.svg create mode 100644 src/assets/images/concierge-white-icon.svg diff --git a/src/assets/images/advisor-white-icon.svg b/src/assets/images/advisor-white-icon.svg new file mode 100644 index 000000000..4a7d59559 --- /dev/null +++ b/src/assets/images/advisor-white-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/concierge-white-icon.svg b/src/assets/images/concierge-white-icon.svg new file mode 100644 index 000000000..ef4a0de90 --- /dev/null +++ b/src/assets/images/concierge-white-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx index 3d5840436..eaa67f1ed 100644 --- a/src/screens/Advisors/AdvisorDetail.tsx +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -2,14 +2,16 @@ import React, { useState } from 'react'; import { View, StyleSheet } from 'react-native'; import AdvisorProfileHeader from './component/AdvisorProfileHeader'; import { wp } from 'src/constants/responsive'; -import { Box, useColorMode } from 'native-base'; +import { Box, ScrollView, useColorMode } from 'native-base'; import MapPin from 'src/assets/images/MapPinIcon.svg'; import Colors from 'src/theme/Colors'; import Text from 'src/components/KeeperText'; -import { getUniqueRandomColors } from './component/AdvisorCard'; import KeeperModal from 'src/components/KeeperModal'; import openLink from 'src/utils/OpenLink'; import Buttons from 'src/components/Buttons'; +import sha256 from 'crypto-js/sha256'; +import ConnectAdvisor from 'src/assets/images/connect-advisor.svg'; +import ThemedColor from 'src/components/ThemedColor/ThemedColor'; const ADVISOR_DETAILS = [ { title: 'Time zone', key: 'timezone' }, @@ -17,6 +19,11 @@ const ADVISOR_DETAILS = [ { title: 'Language', key: 'languages' }, { title: 'Session Duration', key: 'duration' }, ]; +const getColorForLabel = (label: string, colorsArray: string[]) => { + const hash = sha256(label).toString(); + const hashNum = parseInt(hash.slice(0, 8), 16); + return colorsArray[hashNum % colorsArray.length]; +}; function DetailCard({ title, desc, isLast }) { const { colorMode } = useColorMode(); @@ -46,14 +53,20 @@ const AdvisorDetail = ({ route }) => { const { advisor } = route.params; const { colorMode } = useColorMode(); const [showModal, setShowModal] = useState(false); - const previewLength = 160; + const previewLength = 150; const isLong = advisor.description.length > previewLength; const previewText = isLong ? `${advisor.description.slice(0, previewLength)}...` : advisor.description; + const viewAll_color = ThemedColor({ name: 'viewAll_color' }); + function ExpertiesPill({ name }: { name: string }) { - const backgroundColor = getUniqueRandomColors(advisor?.expertise.length)[0]; + const tagColors = Object.entries(Colors) + .filter(([key]) => key.startsWith('TagLight')) + .map(([, value]) => value); + + const backgroundColor = getColorForLabel(name, tagColors); return ( @@ -63,12 +76,11 @@ const AdvisorDetail = ({ route }) => { ); } - return ( - + {advisor.title} @@ -95,7 +107,7 @@ const AdvisorDetail = ({ route }) => { {previewText} {isLong && ( - setShowModal(true)}> + setShowModal(true)}> {' '} Read more @@ -127,15 +139,13 @@ const AdvisorDetail = ({ route }) => { - - - - + openLink(advisor.link)} fullWidth + RightIcon={ConnectAdvisor} /> @@ -168,10 +178,10 @@ export default AdvisorDetail; const styles = StyleSheet.create({ container: { paddingHorizontal: wp(22), - paddingTop: wp(65), }, scrollContent: { - paddingBottom: wp(120), + marginTop: wp(65), + marginBottom: wp(100), }, pinContainer: { flexDirection: 'row', @@ -206,9 +216,8 @@ const styles = StyleSheet.create({ marginTop: wp(-10), }, detailsContainer: { - paddingHorizontal: wp(23), + paddingHorizontal: wp(10), paddingVertical: wp(18), - paddingRight: wp(10), borderWidth: 1, borderRadius: 10, marginTop: wp(8), @@ -222,10 +231,13 @@ const styles = StyleSheet.create({ detailColumnLeft: { flex: 1, paddingRight: wp(50), + paddingLeft: wp(15), + justifyContent: 'center', }, detailColumnRight: { flex: 1, alignItems: 'flex-start', + justifyContent: 'center', }, fixedButtonContainer: { position: 'absolute', diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 098f245ca..40fb4a126 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -1,5 +1,5 @@ import { Box, FlatList, useColorMode } from 'native-base'; -import React from 'react'; +import React, { useMemo, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import CircleIconWrapper from 'src/components/CircleIconWrapper'; import ScreenWrapper from 'src/components/ScreenWrapper'; @@ -44,6 +44,15 @@ const Advisors = () => { const { colorMode } = useColorMode(); const isDarkMode = colorMode === 'dark'; const navigation = useNavigation(); + const [search, setSearch] = useState(''); + console.log('search', search); + + const filteredAdvisors = useMemo(() => { + return dummyAdvisors.filter((advisor) => + advisor.title.toLowerCase().includes(search.toLowerCase()) + ); + }, [search]); + return ( { {}} + value={search} + onChangeText={setSearch} inpuBorderColor={isDarkMode ? `${colorMode}.separator` : `${colorMode}.pantoneGreen`} /> @@ -75,7 +85,7 @@ const Advisors = () => { index.toString()} renderItem={({ item }) => } contentContainerStyle={{ paddingBottom: wp(20) }} diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx index 4b3c55c98..2bcdde3dc 100644 --- a/src/screens/Advisors/component/AdvisorCard.tsx +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -1,5 +1,5 @@ import { Box, Image, ScrollView, useColorMode } from 'native-base'; -import React from 'react'; +import React, { useMemo } from 'react'; import { StyleSheet } from 'react-native'; import Text from 'src/components/KeeperText'; import { hp, wp } from 'src/constants/responsive'; @@ -8,26 +8,30 @@ import MapPin from 'src/assets/images/MapPinIcon.svg'; import Buttons from 'src/components/Buttons'; import ViewProfile from 'src/assets/images/view-profile.svg'; import { useNavigation } from '@react-navigation/native'; +import sha256 from 'crypto-js/sha256'; type Props = { advisor?: any; }; -export function getUniqueRandomColors(count: number): string[] { - const tagColors = Object.entries(Colors) - .filter(([key]) => key.startsWith('TagLight')) - .map(([, value]) => value); - const shuffled = [...tagColors].sort(() => 0.5 - Math.random()); - return shuffled.slice(0, count); -} +const getColorForLabel = (label: string, colorsArray: string[]) => { + const hash = sha256(label).toString(); + const hashNum = parseInt(hash.slice(0, 8), 16); + return colorsArray[hashNum % colorsArray.length]; +}; const AdvisorCard = ({ advisor }: Props) => { const { colorMode } = useColorMode(); const navigation = useNavigation(); - function ExpertiesPill({ name }: { name: string }) { - const backgroundColor = getUniqueRandomColors(advisor?.expertise.length); + const tagColors = useMemo(() => { + return Object.entries(Colors) + .filter(([key]) => key.startsWith('TagLight')) + .map(([, value]) => value); + }, []); + function ExpertiesPill({ name }: { name: string }) { + const backgroundColor = getColorForLabel(name, tagColors); return ( @@ -68,12 +72,13 @@ const AdvisorCard = ({ advisor }: Props) => { - {advisor?.expertise?.map((item, index) => ( - + {advisor?.expertise?.map((item) => ( + ))} + Time zone: @@ -82,6 +87,7 @@ const AdvisorCard = ({ advisor }: Props) => { {advisor.timezone} + Language: @@ -92,6 +98,7 @@ const AdvisorCard = ({ advisor }: Props) => { + { const backgroundColor = ThemedColor({ name: 'homeScreen_header_background' }); - const navigation = useNavigation(); + const isSmallerDevices = useIsSmallDevices(); return ( - - + + { @@ -39,7 +46,14 @@ const AdvisorProfileHeader = ({ advisorImage }) => { - + image @@ -53,12 +67,10 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', - marginBottom: hp(64), }, wrapper: { paddingHorizontal: wp(5), - height: hp(164), width: '100%', alignItems: 'center', justifyContent: 'flex-end', @@ -81,8 +93,7 @@ const styles = StyleSheet.create({ position: 'absolute', bottom: -hp(50), left: wp(22), - width: wp(105), - height: wp(105), + borderRadius: 70, justifyContent: 'center', alignItems: 'center', diff --git a/src/screens/Home/components/ConciergeComponent.tsx b/src/screens/Home/components/ConciergeComponent.tsx index 6afa240ea..7bc5ff782 100644 --- a/src/screens/Home/components/ConciergeComponent.tsx +++ b/src/screens/Home/components/ConciergeComponent.tsx @@ -11,6 +11,8 @@ import TakeMeThereIcon from 'src/assets/images/take-me-there.svg'; import ConnectAdvisor from 'src/assets/images/connect-advisor.svg'; import { LocalizationContext } from 'src/context/Localization/LocContext'; import { useNavigation } from '@react-navigation/native'; +import ConciergeWhiteIcon from 'src/assets/images/concierge-white-icon.svg'; +import AdvisorWhiteIcon from 'src/assets/images/advisor-white-icon.svg'; type ConciergeItem = { title: string; @@ -23,6 +25,7 @@ type ConciergeItem = { const ConciergeComponent = ({ route }) => { const { colorMode } = useColorMode(); + const isDarkMode = colorMode === 'dark'; const { translations } = useContext(LocalizationContext); const { concierge: conciergeText } = translations; const navigation = useNavigation(); @@ -31,7 +34,7 @@ const ConciergeComponent = ({ route }) => { { title: conciergeText.askQuestion, subtitle: conciergeText.submitTicket, - iconName: , + iconName: isDarkMode ? : , buttonText: conciergeText.takeMeThere, callback: () => { navigation.navigate('KeeperSupport'); @@ -41,7 +44,7 @@ const ConciergeComponent = ({ route }) => { { title: conciergeText.hireAdvisor, subtitle: conciergeText.whiteGlovedService, - iconName: , + iconName: isDarkMode ? : , buttonText: conciergeText.connetWithAdvisor, callback: () => { navigation.navigate('Advisors'); @@ -56,7 +59,7 @@ const ConciergeComponent = ({ route }) => { From 064973b0a6a75e917f04a314d9a1e71e681fb6b5 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Mon, 28 Jul 2025 12:10:44 +0530 Subject: [PATCH 60/80] feat: advisorSaga/ api/ fastImage/ scrollableExpertise --- src/screens/Advisors/AdvisorDetail.tsx | 8 ++- src/screens/Advisors/Advisors.tsx | 64 +++++++++---------- .../Advisors/component/AdvisorCard.tsx | 5 +- .../component/AdvisorProfileHeader.tsx | 5 +- src/screens/LoginScreen/Login.tsx | 2 + src/services/backend/Relay.ts | 11 ++++ src/store/reducers/advisor.ts | 34 ++++++++++ src/store/sagaActions/advisor.ts | 6 ++ src/store/sagas/advisor.ts | 28 ++++++++ src/store/sagas/index.ts | 3 + src/store/sagas/storage.ts | 4 +- src/store/store.ts | 2 + 12 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 src/store/reducers/advisor.ts create mode 100644 src/store/sagaActions/advisor.ts create mode 100644 src/store/sagas/advisor.ts diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx index eaa67f1ed..272a6f703 100644 --- a/src/screens/Advisors/AdvisorDetail.tsx +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -93,11 +93,15 @@ const AdvisorDetail = ({ route }) => { - + {advisor?.expertise?.map((item, index) => ( ))} - + diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 40fb4a126..2558a4e7e 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -1,5 +1,5 @@ import { Box, FlatList, useColorMode } from 'native-base'; -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import CircleIconWrapper from 'src/components/CircleIconWrapper'; import ScreenWrapper from 'src/components/ScreenWrapper'; @@ -10,54 +10,47 @@ import { useNavigation } from '@react-navigation/native'; import KeeperTextInput from 'src/components/KeeperTextInput'; import Text from 'src/components/KeeperText'; import AdvisorCard from './component/AdvisorCard'; - -const dummyAdvisors = [ - { - expertise: ['Hardware Purchase', 'Multisig Setup'], - languages: ['Portuguese', 'English', 'Español'], - title: 'Diy Sec Lab', - country: 'Brazil', - description: - 'DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.DIY Labs is a Brazil-based fintech company specializing in ultra‑secure, open‑source tools for self‑custody of Bitcoin.', - image: 'https://bitcoinkeeper.app/public_assets_email/diySecLab.png', - link: 'https://calendly.com/vaibhav-cakesofttech/30min', - duration: '30 mins', - experience: '4 Years', - timezone: 'GMT - 3', - }, - { - expertise: ['Seed Backup', 'Cold Wallet Recovery', 'Multisig Setup'], - languages: ['English', 'German'], - title: 'SecureVault Pro', - country: 'Germany', - description: - 'SecureVault Pro helps clients recover lost access and offers consultation on safe Bitcoin cold storage setups.', - image: 'https://bitcoinkeeper.app/public_assets_email/diySecLab.png', - link: 'https://calendly.com/sample-link', - duration: '45 mins', - experience: '6 Years', - timezone: 'GMT + 1', - }, -]; +import ActivityIndicatorView from 'src/components/AppActivityIndicator/ActivityIndicatorView'; +import useToastMessage from 'src/hooks/useToastMessage'; +import ToastErrorIcon from 'src/assets/images/toast_error.svg'; +import { useAppSelector } from 'src/store/hooks'; +import { useDispatch } from 'react-redux'; +import { getAdvisors } from 'src/store/sagaActions/advisor'; const Advisors = () => { const { colorMode } = useColorMode(); const isDarkMode = colorMode === 'dark'; const navigation = useNavigation(); const [search, setSearch] = useState(''); - console.log('search', search); + const [loading, setLoading] = useState(false); + const { advisors } = useAppSelector((state) => state.advisor); + const dispatch = useDispatch(); + const { showToast } = useToastMessage(); const filteredAdvisors = useMemo(() => { - return dummyAdvisors.filter((advisor) => - advisor.title.toLowerCase().includes(search.toLowerCase()) - ); + return advisors.filter((advisor) => advisor.title.toLowerCase().includes(search.toLowerCase())); }, [search]); + useEffect(() => { + if (!advisors.length) { + setLoading(true); + dispatch( + getAdvisors(({ status, error }) => { + setLoading(false); + if (!status) { + navigation.goBack(); + showToast(error, ); + } + }) + ); + } + }, []); + return ( navigation.navigate('FilterAdvisor')} @@ -92,6 +85,7 @@ const Advisors = () => { showsVerticalScrollIndicator={false} /> + ); }; diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx index 2bcdde3dc..338ecbaf1 100644 --- a/src/screens/Advisors/component/AdvisorCard.tsx +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -1,4 +1,4 @@ -import { Box, Image, ScrollView, useColorMode } from 'native-base'; +import { Box, ScrollView, useColorMode } from 'native-base'; import React, { useMemo } from 'react'; import { StyleSheet } from 'react-native'; import Text from 'src/components/KeeperText'; @@ -9,6 +9,7 @@ import Buttons from 'src/components/Buttons'; import ViewProfile from 'src/assets/images/view-profile.svg'; import { useNavigation } from '@react-navigation/native'; import sha256 from 'crypto-js/sha256'; +import FastImage from 'react-native-fast-image'; type Props = { advisor?: any; @@ -53,7 +54,7 @@ const AdvisorCard = ({ advisor }: Props) => { borderColor={`${colorMode}.pantoneGreen`} backgroundColor={Colors.headerWhite} > - image + diff --git a/src/screens/Advisors/component/AdvisorProfileHeader.tsx b/src/screens/Advisors/component/AdvisorProfileHeader.tsx index c3d60c4df..60adfe73b 100644 --- a/src/screens/Advisors/component/AdvisorProfileHeader.tsx +++ b/src/screens/Advisors/component/AdvisorProfileHeader.tsx @@ -1,4 +1,4 @@ -import { Box, Image } from 'native-base'; +import { Box } from 'native-base'; import React from 'react'; import { StatusBar, StyleSheet, TouchableOpacity } from 'react-native'; import Text from 'src/components/KeeperText'; @@ -10,6 +10,7 @@ import ProfileArrow from 'src/assets/images/profile-arrow.svg'; import { useNavigation } from '@react-navigation/native'; import Colors from 'src/theme/Colors'; import useIsSmallDevices from 'src/hooks/useSmallDevices'; +import FastImage from 'react-native-fast-image'; const AdvisorProfileHeader = ({ advisorImage }) => { const backgroundColor = ThemedColor({ name: 'homeScreen_header_background' }); @@ -54,7 +55,7 @@ const AdvisorProfileHeader = ({ advisorImage }) => { ]} backgroundColor={Colors.headerWhite} > - image + ); diff --git a/src/screens/LoginScreen/Login.tsx b/src/screens/LoginScreen/Login.tsx index e40cfdb03..dfd83da66 100644 --- a/src/screens/LoginScreen/Login.tsx +++ b/src/screens/LoginScreen/Login.tsx @@ -52,6 +52,7 @@ import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; import CampaignModalIllustration from 'src/assets/images/CampaignModalIllustration.svg'; import { uaiType } from 'src/models/interfaces/Uai'; import { addToUaiStack, uaiChecks } from 'src/store/sagaActions/uai'; +import { getAdvisors } from 'src/store/sagaActions/advisor'; const RNBiometrics = new ReactNativeBiometrics(); @@ -129,6 +130,7 @@ function LoginScreen({ navigation, route }) { useEffect(() => { dispatch(fetchOneDayInsight()); + dispatch(getAdvisors()); fetchCampaignDetails(); }, []); diff --git a/src/services/backend/Relay.ts b/src/services/backend/Relay.ts index 374fda9a6..9a60bfa2e 100644 --- a/src/services/backend/Relay.ts +++ b/src/services/backend/Relay.ts @@ -768,4 +768,15 @@ export default class Relay { } return res ? res.data || res.json : null; }; + + public static getAdvisors = async (): Promise => { + let res; + try { + res = await RestClient.get(`${RELAY}getAdvisors`); + } catch (err) { + if (err?.message) throw new Error(err.message); + if (err?.code) throw new Error(err.code); + } + return res ? res.data || res.json : null; + }; } diff --git a/src/store/reducers/advisor.ts b/src/store/reducers/advisor.ts new file mode 100644 index 000000000..f928ff62b --- /dev/null +++ b/src/store/reducers/advisor.ts @@ -0,0 +1,34 @@ +import { createSlice } from '@reduxjs/toolkit'; + +type Advisor = { + duration: string; + description: string; + country: string; + experience: string; + expertise: string[]; + image: string; + languages: string[]; + link: string; + timezone: string; + title: string; +}; + +const initialState: { + advisors: Advisor[]; +} = { + advisors: [], +}; + +const advisorSlice = createSlice({ + name: 'advisor', + initialState, + reducers: { + setAdvisors: (state, action) => { + state.advisors = action.payload; + }, + }, +}); + +export const { setAdvisors } = advisorSlice.actions; + +export default advisorSlice.reducer; diff --git a/src/store/sagaActions/advisor.ts b/src/store/sagaActions/advisor.ts new file mode 100644 index 000000000..1077dac9d --- /dev/null +++ b/src/store/sagaActions/advisor.ts @@ -0,0 +1,6 @@ +export const GET_ADVISORS = 'GET_ADVISORS'; + +export const getAdvisors = (callback = null) => ({ + type: GET_ADVISORS, + callback, +}); diff --git a/src/store/sagas/advisor.ts b/src/store/sagas/advisor.ts new file mode 100644 index 000000000..b49218a0e --- /dev/null +++ b/src/store/sagas/advisor.ts @@ -0,0 +1,28 @@ +import { call, put } from 'redux-saga/effects'; +import { GET_ADVISORS } from '../sagaActions/advisor'; +import { createWatcher } from '../utilities'; +import Relay from 'src/services/backend/Relay'; +import { setAdvisors } from '../reducers/advisor'; + +export function* getAdvisorWorker({ callback }) { + try { + let res = yield call(Relay.getAdvisors); + if (res.length) { + yield put(setAdvisors(res)); + if (callback) { + callback({ status: true }); + } + } else { + if (callback) { + callback({ status: false, error: 'No advisors found' }); + } + } + } catch (error) { + console.log('🚀 ~ getAdvisorWorker ~ error:', error); + if (callback) { + callback({ status: false, error: error.message || 'Something went wrong' }); + } + } +} + +export const getAdvisorWatcher = createWatcher(getAdvisorWorker, GET_ADVISORS); diff --git a/src/store/sagas/index.ts b/src/store/sagas/index.ts index 13eea96ec..9429b82af 100644 --- a/src/store/sagas/index.ts +++ b/src/store/sagas/index.ts @@ -80,6 +80,7 @@ import { getTnxDetailsWatcher, loadCoinDetailsWatcher, } from './swap'; +import { getAdvisorWatcher } from './advisor'; const rootSaga = function* () { const sagas = [ @@ -180,6 +181,8 @@ const rootSaga = function* () { getSwapQuoteWatcher, createSwapTnxWatcher, getTnxDetailsWatcher, + // advisor + getAdvisorWatcher, ]; yield all( diff --git a/src/store/sagas/storage.ts b/src/store/sagas/storage.ts index 8522bf7d6..2f6e24308 100644 --- a/src/store/sagas/storage.ts +++ b/src/store/sagas/storage.ts @@ -1,5 +1,5 @@ import * as bip39 from 'bip39'; -import { call, put, select } from 'redux-saga/effects'; +import { call, put, select, fork } from 'redux-saga/effects'; import { generateEncryptionKey } from 'src/utils/service-utilities/encryption'; import BIP85 from 'src/services/wallets/operations/BIP85'; import DeviceInfo from 'react-native-device-info'; @@ -41,6 +41,7 @@ import { } from '../reducers/account'; import { loadConciergeTickets, loadConciergeUser } from '../reducers/concierge'; import LoginMethod from 'src/models/enums/LoginMethod'; +import { getAdvisorWorker } from './advisor'; export function* setupKeeperAppWorker({ payload }) { try { @@ -126,6 +127,7 @@ export function* setupKeeperAppWorker({ payload }) { const { allAccounts } = yield select((state: RootState) => state.account); if (allAccounts.length == 1) yield put(setBiometricEnabledAppId(appID)); } + yield fork(getAdvisorWorker, { callback: null }); } else { yield put(setAppCreationError(true)); } diff --git a/src/store/store.ts b/src/store/store.ts index f9c8067a0..6b2c3cc0e 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -21,6 +21,7 @@ import cachedTxnReducer from './reducers/cachedTxn'; import signerReducer from './reducers/signer'; import accountReducer from './reducers/account'; import swapReducer from './reducers/swap'; +import advisorReducer from './reducers/advisor'; import { RESET_REDUX_STORE } from './sagaActions/upgrade'; import reduxPersistMigrations from './migrations'; @@ -42,6 +43,7 @@ const appReducer = combineReducers({ signer: signerReducer, account: accountReducer, swap: swapReducer, + advisor: advisorReducer, }); const rootReducer = (state, action) => { From a72d8061c57b2a673c9320d039f6026035682f87 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Tue, 29 Jul 2025 13:26:17 +0530 Subject: [PATCH 61/80] fix: AdvisorImageCtr --- src/screens/Advisors/component/AdvisorCard.tsx | 10 +++++----- .../Advisors/component/AdvisorProfileHeader.tsx | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx index 338ecbaf1..feabd7b90 100644 --- a/src/screens/Advisors/component/AdvisorCard.tsx +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -129,8 +129,8 @@ const styles = StyleSheet.create({ marginBottom: wp(10), }, circle: { - width: wp(40), - height: wp(40), + width: wp(60), + height: wp(60), borderRadius: 30, justifyContent: 'center', alignItems: 'center', @@ -170,8 +170,8 @@ const styles = StyleSheet.create({ marginTop: wp(5), }, image: { - width: wp(35), - height: hp(20), - borderRadius: 10, + width: '100%', + height: '100%', + borderRadius: 30, }, }); diff --git a/src/screens/Advisors/component/AdvisorProfileHeader.tsx b/src/screens/Advisors/component/AdvisorProfileHeader.tsx index 60adfe73b..84ff3c56b 100644 --- a/src/screens/Advisors/component/AdvisorProfileHeader.tsx +++ b/src/screens/Advisors/component/AdvisorProfileHeader.tsx @@ -101,8 +101,8 @@ const styles = StyleSheet.create({ borderWidth: 1, }, image: { - width: wp(70), - height: hp(70), - borderRadius: 10, + width: '100%', + height: '100%', + borderRadius: wp(60), }, }); From 7cda9a935ad5448bdf6c707b84de766c03957042 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Tue, 29 Jul 2025 13:27:33 +0530 Subject: [PATCH 62/80] fix: removedShareAdvisor CTA --- src/screens/Advisors/component/AdvisorProfileHeader.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/screens/Advisors/component/AdvisorProfileHeader.tsx b/src/screens/Advisors/component/AdvisorProfileHeader.tsx index 84ff3c56b..76b858b36 100644 --- a/src/screens/Advisors/component/AdvisorProfileHeader.tsx +++ b/src/screens/Advisors/component/AdvisorProfileHeader.tsx @@ -42,9 +42,10 @@ const AdvisorProfileHeader = ({ advisorImage }) => { - + {/* // * For future use */} + {/* - + */} Date: Tue, 29 Jul 2025 11:30:43 +0500 Subject: [PATCH 63/80] fixes --- src/context/Localization/language/en.json | 16 +++++++++-- src/context/Localization/language/es.json | 16 +++++++++-- src/screens/Advisors/AdvisorDetail.tsx | 28 +++++++++++-------- src/screens/Advisors/Advisors.tsx | 13 +++++---- .../Advisors/component/AdvisorCard.tsx | 24 ++++++++++++---- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index a95d411f6..f6364e075 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1766,7 +1766,19 @@ "takeMeThere": "Take me there", "hireAdvisor": "Hire an Advisor", "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", - "connetWithAdvisor": "Connect with an Advisor" + "connetWithAdvisor": "Connect with an Advisor", + "MeetAdvisors": "Meet Our Advisors", + "searchExprt": "Search for an Expert", + "meetExperts": "Meet Our Experts", + "timeZone": " Time zone", + "language": "Language", + "ViewProfile": "View Profile", + "Experience": "Experience", + "sessionDuration": "Session Duration", + "advisorDetail": "Advisor Detail", + "readMore": "Read more", + "advisorDetails": "Advisor Details", + "scheduleCall": "Schedule your call" }, "buyBTC": { "acquire": "Acquire", @@ -2033,4 +2045,4 @@ "noContactYet": "No Contacts Yet", "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } -} \ No newline at end of file +} diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index a95d411f6..f6364e075 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1766,7 +1766,19 @@ "takeMeThere": "Take me there", "hireAdvisor": "Hire an Advisor", "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", - "connetWithAdvisor": "Connect with an Advisor" + "connetWithAdvisor": "Connect with an Advisor", + "MeetAdvisors": "Meet Our Advisors", + "searchExprt": "Search for an Expert", + "meetExperts": "Meet Our Experts", + "timeZone": " Time zone", + "language": "Language", + "ViewProfile": "View Profile", + "Experience": "Experience", + "sessionDuration": "Session Duration", + "advisorDetail": "Advisor Detail", + "readMore": "Read more", + "advisorDetails": "Advisor Details", + "scheduleCall": "Schedule your call" }, "buyBTC": { "acquire": "Acquire", @@ -2033,4 +2045,4 @@ "noContactYet": "No Contacts Yet", "noContactDesc": "Please add your trusted contacts to Send/Receive sats seamlessly." } -} \ No newline at end of file +} diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx index 272a6f703..4a84c59dd 100644 --- a/src/screens/Advisors/AdvisorDetail.tsx +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { View, StyleSheet } from 'react-native'; import AdvisorProfileHeader from './component/AdvisorProfileHeader'; import { wp } from 'src/constants/responsive'; @@ -12,13 +12,8 @@ import Buttons from 'src/components/Buttons'; import sha256 from 'crypto-js/sha256'; import ConnectAdvisor from 'src/assets/images/connect-advisor.svg'; import ThemedColor from 'src/components/ThemedColor/ThemedColor'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; -const ADVISOR_DETAILS = [ - { title: 'Time zone', key: 'timezone' }, - { title: 'Experience', key: 'experience' }, - { title: 'Language', key: 'languages' }, - { title: 'Session Duration', key: 'duration' }, -]; const getColorForLabel = (label: string, colorsArray: string[]) => { const hash = sha256(label).toString(); const hashNum = parseInt(hash.slice(0, 8), 16); @@ -53,6 +48,8 @@ const AdvisorDetail = ({ route }) => { const { advisor } = route.params; const { colorMode } = useColorMode(); const [showModal, setShowModal] = useState(false); + const { translations } = useContext(LocalizationContext); + const { concierge } = translations; const previewLength = 150; const isLong = advisor.description.length > previewLength; const previewText = isLong @@ -76,6 +73,13 @@ const AdvisorDetail = ({ route }) => { ); } + + const ADVISOR_DETAILS = [ + { title: concierge.timeZone, key: 'timezone' }, + { title: concierge.Experience, key: 'experience' }, + { title: concierge.language, key: 'languages' }, + { title: concierge.sessionDuration, key: 'duration' }, + ]; return ( @@ -106,14 +110,14 @@ const AdvisorDetail = ({ route }) => { - Advisor Detail: + {concierge.advisorDetail}: {previewText} {isLong && ( setShowModal(true)}> {' '} - Read more + {concierge.readMore} )} @@ -121,7 +125,7 @@ const AdvisorDetail = ({ route }) => { - Advisor Details + {concierge.advisorDetails} { openLink(advisor.link)} fullWidth RightIcon={ConnectAdvisor} @@ -167,7 +171,7 @@ const AdvisorDetail = ({ route }) => { )} - buttonText="Schedule your call" + buttonText={concierge.scheduleCall} buttonCallback={() => { setShowModal(false); openLink(advisor.link); diff --git a/src/screens/Advisors/Advisors.tsx b/src/screens/Advisors/Advisors.tsx index 2558a4e7e..a5bbffd3b 100644 --- a/src/screens/Advisors/Advisors.tsx +++ b/src/screens/Advisors/Advisors.tsx @@ -1,5 +1,5 @@ import { Box, FlatList, useColorMode } from 'native-base'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; import CircleIconWrapper from 'src/components/CircleIconWrapper'; import ScreenWrapper from 'src/components/ScreenWrapper'; @@ -16,6 +16,7 @@ import ToastErrorIcon from 'src/assets/images/toast_error.svg'; import { useAppSelector } from 'src/store/hooks'; import { useDispatch } from 'react-redux'; import { getAdvisors } from 'src/store/sagaActions/advisor'; +import { LocalizationContext } from 'src/context/Localization/LocContext'; const Advisors = () => { const { colorMode } = useColorMode(); @@ -26,6 +27,8 @@ const Advisors = () => { const { advisors } = useAppSelector((state) => state.advisor); const dispatch = useDispatch(); const { showToast } = useToastMessage(); + const { translations } = useContext(LocalizationContext); + const { concierge } = translations; const filteredAdvisors = useMemo(() => { return advisors.filter((advisor) => advisor.title.toLowerCase().includes(search.toLowerCase())); @@ -49,8 +52,8 @@ const Advisors = () => { return ( navigation.navigate('FilterAdvisor')} @@ -66,7 +69,7 @@ const Advisors = () => { /> { - Meet Our Experts + {concierge.meetExperts} { const AdvisorCard = ({ advisor }: Props) => { const { colorMode } = useColorMode(); const navigation = useNavigation(); + const { translations } = useContext(LocalizationContext); + const { concierge } = translations; const tagColors = useMemo(() => { return Object.entries(Colors) @@ -82,7 +85,7 @@ const AdvisorCard = ({ advisor }: Props) => { - Time zone: + {concierge.timeZone}: {advisor.timezone} @@ -91,9 +94,14 @@ const AdvisorCard = ({ advisor }: Props) => { - Language: + {concierge.language}: - + {advisor.languages.join(', ')} @@ -102,10 +110,11 @@ const AdvisorCard = ({ advisor }: Props) => { navigation.navigate('AdvisorDetail', { advisor })} + paddingVertical={wp(10)} /> @@ -174,4 +183,7 @@ const styles = StyleSheet.create({ height: '100%', borderRadius: 30, }, + languageContainer: { + width: wp(160), + }, }); From ba286138697d84ceb550dabe93a8cacee790edbd Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Tue, 29 Jul 2025 11:36:25 +0500 Subject: [PATCH 64/80] fixes pil --- src/screens/Advisors/AdvisorDetail.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx index 4a84c59dd..ab98d63a0 100644 --- a/src/screens/Advisors/AdvisorDetail.tsx +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -203,9 +203,10 @@ const styles = StyleSheet.create({ gap: wp(5), paddingRight: wp(20), marginRight: wp(30), + flexWrap: 'wrap', }, pillsScrollWrapper: { - height: wp(30), + minHeight: wp(30), marginBottom: wp(8), }, pill: { From 10597267b9248b3de8e7494b308733d226cad071 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Tue, 29 Jul 2025 12:20:44 +0500 Subject: [PATCH 65/80] card fixes --- src/screens/Advisors/component/AdvisorCard.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/screens/Advisors/component/AdvisorCard.tsx b/src/screens/Advisors/component/AdvisorCard.tsx index be2c8012b..0b0051d53 100644 --- a/src/screens/Advisors/component/AdvisorCard.tsx +++ b/src/screens/Advisors/component/AdvisorCard.tsx @@ -62,11 +62,11 @@ const AdvisorCard = ({ advisor }: Props) => { - + {advisor.title} - + {advisor.country} @@ -114,7 +114,6 @@ const AdvisorCard = ({ advisor }: Props) => { fullWidth RightIcon={ViewProfile} primaryCallback={() => navigation.navigate('AdvisorDetail', { advisor })} - paddingVertical={wp(10)} /> @@ -171,7 +170,6 @@ const styles = StyleSheet.create({ }, timeContainer: { flexDirection: 'row', - alignItems: 'center', gap: wp(5), marginBottom: wp(10), }, From b157191499e96bfb9a06cd1c158d104dd1a152f4 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 30 Jul 2025 12:49:33 +0500 Subject: [PATCH 66/80] fix text --- src/context/Localization/language/en.json | 4 ++-- src/context/Localization/language/es.json | 4 ++-- src/screens/Advisors/AdvisorDetail.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/context/Localization/language/en.json b/src/context/Localization/language/en.json index f6364e075..488fd7dde 100644 --- a/src/context/Localization/language/en.json +++ b/src/context/Localization/language/en.json @@ -1764,7 +1764,7 @@ "askQuestion": "Ask us a question.", "submitTicket": "Submit a ticket to get all your queries answered.", "takeMeThere": "Take me there", - "hireAdvisor": "Hire an Advisor", + "hireAdvisor": "Find an Advisor", "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", "connetWithAdvisor": "Connect with an Advisor", "MeetAdvisors": "Meet Our Advisors", @@ -1775,7 +1775,7 @@ "ViewProfile": "View Profile", "Experience": "Experience", "sessionDuration": "Session Duration", - "advisorDetail": "Advisor Detail", + "aboutAdvisor": "About the Advisor", "readMore": "Read more", "advisorDetails": "Advisor Details", "scheduleCall": "Schedule your call" diff --git a/src/context/Localization/language/es.json b/src/context/Localization/language/es.json index f6364e075..488fd7dde 100644 --- a/src/context/Localization/language/es.json +++ b/src/context/Localization/language/es.json @@ -1764,7 +1764,7 @@ "askQuestion": "Ask us a question.", "submitTicket": "Submit a ticket to get all your queries answered.", "takeMeThere": "Take me there", - "hireAdvisor": "Hire an Advisor", + "hireAdvisor": "Find an Advisor", "whiteGlovedService": "White gloved services for comprehensive bitcoin management.", "connetWithAdvisor": "Connect with an Advisor", "MeetAdvisors": "Meet Our Advisors", @@ -1775,7 +1775,7 @@ "ViewProfile": "View Profile", "Experience": "Experience", "sessionDuration": "Session Duration", - "advisorDetail": "Advisor Detail", + "aboutAdvisor": "About the Advisor", "readMore": "Read more", "advisorDetails": "Advisor Details", "scheduleCall": "Schedule your call" diff --git a/src/screens/Advisors/AdvisorDetail.tsx b/src/screens/Advisors/AdvisorDetail.tsx index ab98d63a0..1fbc0e9aa 100644 --- a/src/screens/Advisors/AdvisorDetail.tsx +++ b/src/screens/Advisors/AdvisorDetail.tsx @@ -110,7 +110,7 @@ const AdvisorDetail = ({ route }) => { - {concierge.advisorDetail}: + {concierge.aboutAdvisor}: {previewText} From 235c27015d01de3360a91ba826297416bb505acb Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Wed, 30 Jul 2025 12:58:10 +0530 Subject: [PATCH 67/80] release 555 --- android/app/build.gradle | 2 +- ios/hexa_keeper.xcodeproj/project.pbxproj | 20 ++++++++++---------- ios/hexa_keeper/Info.plist | 2 +- ios/hexa_keeperTests/Info.plist | 2 +- ios/hexa_keeper_dev-Info.plist | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8cf202757..27f1189db 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 551 + versionCode 555 versionName "2.5.2" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' diff --git a/ios/hexa_keeper.xcodeproj/project.pbxproj b/ios/hexa_keeper.xcodeproj/project.pbxproj index 5bda2c148..76bf3ebdd 100644 --- a/ios/hexa_keeper.xcodeproj/project.pbxproj +++ b/ios/hexa_keeper.xcodeproj/project.pbxproj @@ -732,8 +732,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "cd711cf7-1c75-4302-ad18-39de9d4f8bef"; - PROVISIONING_PROFILE_SPECIFIER = "Keeper Development"; + PROVISIONING_PROFILE = "4bff6035-a2eb-4b53-a161-79f0e10e8ede"; + PROVISIONING_PROFILE_SPECIFIER = "Keeper Distribution"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hexa_keeper.app/hexa_keeper"; }; name = Release; @@ -748,7 +748,7 @@ CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 551; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = Y5TCB759QL; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( @@ -874,7 +874,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: Bithyve UK Ltd (Y5TCB759QL)"; - CURRENT_PROJECT_VERSION = 551; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( @@ -983,8 +983,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = io.hexawallet.keeper; PRODUCT_NAME = hexa_keeper; - PROVISIONING_PROFILE = "cd711cf7-1c75-4302-ad18-39de9d4f8bef"; - PROVISIONING_PROFILE_SPECIFIER = "Keeper Development"; + PROVISIONING_PROFILE = "4bff6035-a2eb-4b53-a161-79f0e10e8ede"; + PROVISIONING_PROFILE_SPECIFIER = "Keeper Distribution"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Keeper Distribution"; SWIFT_OBJC_BRIDGING_HEADER = "whirlpool/hexa_keeper-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1144,7 +1144,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 551; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; ENABLE_BITCODE = NO; @@ -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 = 551; + CURRENT_PROJECT_VERSION = 555; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( @@ -1385,8 +1385,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = io.hexawallet.hexakeeper.dev; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "cd711cf7-1c75-4302-ad18-39de9d4f8bef"; - PROVISIONING_PROFILE_SPECIFIER = "Keeper Development"; + PROVISIONING_PROFILE = "4bff6035-a2eb-4b53-a161-79f0e10e8ede"; + PROVISIONING_PROFILE_SPECIFIER = "Keeper Distribution"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Keeper Development"; SWIFT_OBJC_BRIDGING_HEADER = "whirlpool/hexa_keeper-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "hexa_keeper-Swift.h"; diff --git a/ios/hexa_keeper/Info.plist b/ios/hexa_keeper/Info.plist index 072b186b5..a15d30129 100644 --- a/ios/hexa_keeper/Info.plist +++ b/ios/hexa_keeper/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 551 + 555 LSApplicationQueriesSchemes itms-apps diff --git a/ios/hexa_keeperTests/Info.plist b/ios/hexa_keeperTests/Info.plist index ef5eb12e2..7ec29f6c7 100644 --- a/ios/hexa_keeperTests/Info.plist +++ b/ios/hexa_keeperTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 551 + 555 diff --git a/ios/hexa_keeper_dev-Info.plist b/ios/hexa_keeper_dev-Info.plist index 7841440f3..0fb0aa907 100644 --- a/ios/hexa_keeper_dev-Info.plist +++ b/ios/hexa_keeper_dev-Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 551 + 555 LSApplicationQueriesSchemes itms-apps From 208a1a66783ce1d19f3f9ef530954ea76eb92239 Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Wed, 30 Jul 2025 16:30:47 +0500 Subject: [PATCH 68/80] fix logo --- src/assets/privateImages/buy-btc-header-private.svg | 3 +++ src/components/ThemedSvg.tsx/ThemedIcons.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 src/assets/privateImages/buy-btc-header-private.svg diff --git a/src/assets/privateImages/buy-btc-header-private.svg b/src/assets/privateImages/buy-btc-header-private.svg new file mode 100644 index 000000000..32b9d48bc --- /dev/null +++ b/src/assets/privateImages/buy-btc-header-private.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ThemedSvg.tsx/ThemedIcons.js b/src/components/ThemedSvg.tsx/ThemedIcons.js index 64c99c9d9..ad8d62346 100644 --- a/src/components/ThemedSvg.tsx/ThemedIcons.js +++ b/src/components/ThemedSvg.tsx/ThemedIcons.js @@ -337,6 +337,7 @@ 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'; 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: { @@ -1149,7 +1150,7 @@ const themeIcons = { header_buy_btc: { DARK: BtcLogo, LIGHT: BtcLogo, - PRIVATE: AquireHeadrWhite, + PRIVATE: BuyBtcPRivate, PRIVATE_LIGHT: BtcLogoGold, }, footer_buy_btc: { From 63d2933942acc301b45f1574daaa25c550c7e20b Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Wed, 30 Jul 2025 14:59:35 +0530 Subject: [PATCH 69/80] release 556 --- android/app/build.gradle | 2 +- ios/hexa_keeper.xcodeproj/project.pbxproj | 8 ++++---- ios/hexa_keeper/Info.plist | 2 +- ios/hexa_keeperTests/Info.plist | 2 +- ios/hexa_keeper_dev-Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 27f1189db..41e033712 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 555 + versionCode 556 versionName "2.5.2" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' diff --git a/ios/hexa_keeper.xcodeproj/project.pbxproj b/ios/hexa_keeper.xcodeproj/project.pbxproj index 76bf3ebdd..9bb8816c0 100644 --- a/ios/hexa_keeper.xcodeproj/project.pbxproj +++ b/ios/hexa_keeper.xcodeproj/project.pbxproj @@ -748,7 +748,7 @@ CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 555; + CURRENT_PROJECT_VERSION = 556; DEVELOPMENT_TEAM = Y5TCB759QL; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( @@ -874,7 +874,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: Bithyve UK Ltd (Y5TCB759QL)"; - CURRENT_PROJECT_VERSION = 555; + CURRENT_PROJECT_VERSION = 556; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( @@ -1144,7 +1144,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 555; + CURRENT_PROJECT_VERSION = 556; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; ENABLE_BITCODE = NO; @@ -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 = 555; + CURRENT_PROJECT_VERSION = 556; 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 a15d30129..01dcd2dc5 100644 --- a/ios/hexa_keeper/Info.plist +++ b/ios/hexa_keeper/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 555 + 556 LSApplicationQueriesSchemes itms-apps diff --git a/ios/hexa_keeperTests/Info.plist b/ios/hexa_keeperTests/Info.plist index 7ec29f6c7..f690f78e8 100644 --- a/ios/hexa_keeperTests/Info.plist +++ b/ios/hexa_keeperTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 555 + 556 diff --git a/ios/hexa_keeper_dev-Info.plist b/ios/hexa_keeper_dev-Info.plist index 0fb0aa907..0b8a566b0 100644 --- a/ios/hexa_keeper_dev-Info.plist +++ b/ios/hexa_keeper_dev-Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 555 + 556 LSApplicationQueriesSchemes itms-apps From 7099340a837d8632fa8a30e41d860edfeaefbc5c Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 31 Jul 2025 11:52:34 +0530 Subject: [PATCH 70/80] release 557 --- android/app/build.gradle | 2 +- ios/hexa_keeper.xcodeproj/project.pbxproj | 12 ++++++------ ios/hexa_keeper/Info.plist | 2 +- ios/hexa_keeperTests/Info.plist | 2 +- ios/hexa_keeper_dev-Info.plist | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 41e033712..244499d0a 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 556 + versionCode 557 versionName "2.5.2" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' diff --git a/ios/hexa_keeper.xcodeproj/project.pbxproj b/ios/hexa_keeper.xcodeproj/project.pbxproj index 9bb8816c0..d2404d944 100644 --- a/ios/hexa_keeper.xcodeproj/project.pbxproj +++ b/ios/hexa_keeper.xcodeproj/project.pbxproj @@ -464,7 +464,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/zsh; - shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "set -e\n\nexport NODE_BINARY=/opt/homebrew/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; 44AB5644361D37D684C09DDC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -635,7 +635,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/zsh; - shellScript = "set -e\n\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "set -e\n\nexport NODE_BINARY=/opt/homebrew/bin/node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -748,7 +748,7 @@ CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 556; + CURRENT_PROJECT_VERSION = 557; DEVELOPMENT_TEAM = Y5TCB759QL; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( @@ -874,7 +874,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: Bithyve UK Ltd (Y5TCB759QL)"; - CURRENT_PROJECT_VERSION = 556; + CURRENT_PROJECT_VERSION = 557; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; HEADER_SEARCH_PATHS = ( @@ -1144,7 +1144,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 556; + CURRENT_PROJECT_VERSION = 557; DEVELOPMENT_TEAM = Y5TCB759QL; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL; ENABLE_BITCODE = NO; @@ -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 = 556; + CURRENT_PROJECT_VERSION = 557; 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 01dcd2dc5..e5e955b88 100644 --- a/ios/hexa_keeper/Info.plist +++ b/ios/hexa_keeper/Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 556 + 557 LSApplicationQueriesSchemes itms-apps diff --git a/ios/hexa_keeperTests/Info.plist b/ios/hexa_keeperTests/Info.plist index f690f78e8..9d400e8ef 100644 --- a/ios/hexa_keeperTests/Info.plist +++ b/ios/hexa_keeperTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 556 + 557 diff --git a/ios/hexa_keeper_dev-Info.plist b/ios/hexa_keeper_dev-Info.plist index 0b8a566b0..4425b2456 100644 --- a/ios/hexa_keeper_dev-Info.plist +++ b/ios/hexa_keeper_dev-Info.plist @@ -36,7 +36,7 @@ CFBundleVersion - 556 + 557 LSApplicationQueriesSchemes itms-apps From 3eabba63d93f3a5cc7c1d03b8b2ae1da731d46f1 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 31 Jul 2025 18:53:20 +0530 Subject: [PATCH 71/80] feat: AppendKeyInUnknownContact --- src/screens/Home/components/Contact/component/ChatList.tsx | 3 ++- src/screens/Home/components/Contact/component/Contact.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/screens/Home/components/Contact/component/ChatList.tsx b/src/screens/Home/components/Contact/component/ChatList.tsx index 27907a8f8..d93839719 100644 --- a/src/screens/Home/components/Contact/component/ChatList.tsx +++ b/src/screens/Home/components/Contact/component/ChatList.tsx @@ -2,7 +2,7 @@ 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 { wp } from 'src/constants/responsive'; +import { windowWidth, wp } from 'src/constants/responsive'; import ChatPlaceHolderIcon from 'src/assets/images/contact-placeholder-image.png'; import { useNavigation } from '@react-navigation/native'; import Fonts from 'src/constants/Fonts'; @@ -218,6 +218,7 @@ const styles = StyleSheet.create({ nameText: { fontSize: wp(15), marginBottom: wp(8), + maxWidth: windowWidth * 0.8, }, messageText: { fontSize: wp(12), diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index e834812ba..70a79355a 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -106,7 +106,7 @@ const Contact = () => { dbManager.createObject(RealmSchema.Community, { id: communityId, communityId: communityId, - name: name || 'Unknown Contact', + name: name || `Unknown Contact (${peerKey.substring(0, 8)})`, createdAt: Date.now(), type: CommunityType.Peer, with: peerKey, From 94b5436d4bdb9fdbfdbc445b0ada488f2110b9cc Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 31 Jul 2025 18:27:17 +0500 Subject: [PATCH 72/80] remove duplication --- src/navigation/Navigator.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/navigation/Navigator.tsx b/src/navigation/Navigator.tsx index 9a3f238b5..7e5a7b2b4 100644 --- a/src/navigation/Navigator.tsx +++ b/src/navigation/Navigator.tsx @@ -167,9 +167,6 @@ import { SwapDetails } from 'src/screens/Home/components/buyBtc/Swap/SwapDetails import { SwapHistory } from 'src/screens/Home/components/buyBtc/Swap/SwapHistory'; import { SwapHistoryDetail } from 'src/screens/Home/components/buyBtc/Swap/SwapHistoryDetail'; import { SwapAllHistory } from 'src/screens/Home/components/buyBtc/Swap/SwapAllHistory'; -import Advisors from 'src/screens/Advisors/Advisors'; -import FilterAdvisor from 'src/screens/Advisors/FilterAdvisor'; -import AdvisorDetail from 'src/screens/Advisors/AdvisorDetail'; function LoginStack() { const Stack = createNativeStackNavigator(); @@ -369,9 +366,6 @@ function AppStack() { - - - ); From 41fae42c15a1c766545c74f75b7910b9a1cf5e5a Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Fri, 1 Aug 2025 11:32:29 +0530 Subject: [PATCH 73/80] release 558 --- android/app/build.gradle | 2 +- ios/hexa_keeper.xcodeproj/project.pbxproj | 8 ++++---- ios/hexa_keeper/Info.plist | 2 +- ios/hexa_keeperTests/Info.plist | 2 +- ios/hexa_keeper_dev-Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) 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/ios/hexa_keeper.xcodeproj/project.pbxproj b/ios/hexa_keeper.xcodeproj/project.pbxproj index 8c7d912d1..9747ffab1 100644 --- a/ios/hexa_keeper.xcodeproj/project.pbxproj +++ b/ios/hexa_keeper.xcodeproj/project.pbxproj @@ -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 = ( @@ -874,7 +874,7 @@ 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 = ( @@ -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; @@ -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 From a734816b6606c4b8f925c4085f04be36f029d37c Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Tue, 5 Aug 2025 18:17:55 +0500 Subject: [PATCH 74/80] fix contact issues --- .../components/Contact/component/Contact.tsx | 25 ++++++++++++++++--- .../Contact/component/ContactHeader.tsx | 21 ++-------------- .../Contact/component/ProfileContent.tsx | 6 +---- src/screens/QRScreens/ScanQR.tsx | 17 ++++++++++++- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 7e9305693..d0cf8ed8d 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -13,7 +13,7 @@ import Buttons from 'src/components/Buttons'; import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; import KeeperModal from 'src/components/KeeperModal'; import ProfileContent from './ProfileContent'; -import { useNavigation } from '@react-navigation/native'; +import { CommonActions, useNavigation } from '@react-navigation/native'; import ContactModalData from './ContactModalData'; import { LocalizationContext } from 'src/context/Localization/LocContext'; import useToastMessage from 'src/hooks/useToastMessage'; @@ -58,6 +58,12 @@ const Contact = () => { return; } }; + useEffect(() => { + if (app.profilePicture !== 'undefined' || app.appName !== 'undefined') { + setUserProfileImage(app.profilePicture); + setUserProfileName(app.appName); + } + }, [app.profilePicture, app.appName]); const contactShareLink = useMemo(() => { if (app?.contactsKey?.secretKey) { @@ -189,8 +195,19 @@ const Contact = () => { { - setContactModalVisible(true); - setShareContact(false); + navigation.dispatch( + CommonActions.navigate({ + name: 'ScanQR', + params: { + title: 'Scan QR', + subtitle: 'Scan QR', + onQrScan, + importOptions: false, + isSingning: true, + contactShareData: contactShareLink, + }, + }) + ); }} fullWidth LeftIcon={ContactAddicon} @@ -224,7 +241,7 @@ const Contact = () => { modalBackground={`${colorMode}.modalWhiteBackground`} Content={() => ( - { - setContactModalVisible(true); - setShareContact(true); - }} - > - - - - {contactText.share} - - - ); }; @@ -101,7 +84,7 @@ const styles = StyleSheet.create({ borderRadius: wp(10), padding: wp(15), justifyContent: 'space-between', - width: wp(248), + width: windowWidth * 0.9, }, text: { fontSize: 14, diff --git a/src/screens/Home/components/Contact/component/ProfileContent.tsx b/src/screens/Home/components/Contact/component/ProfileContent.tsx index 2bd5d4e2d..8beed10a3 100644 --- a/src/screens/Home/components/Contact/component/ProfileContent.tsx +++ b/src/screens/Home/components/Contact/component/ProfileContent.tsx @@ -94,11 +94,7 @@ const ProfileContent = ({ /> )} - {profileImage ? ( - - ) : ( - - )} + 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} + /> + + )} { From 463c6988f81c59e4f1c7033c08db5e9d34ccbbad Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Wed, 6 Aug 2025 13:55:18 +0530 Subject: [PATCH 75/80] fix: AdvisorCTA/ customCTAForContacts --- src/assets/images/hire-advisor.svg | 6 +- .../components/Contact/component/Contact.tsx | 27 +++++-- .../Contact/component/ContactsCta.tsx | 75 +++++++++++++++++++ 3 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 src/screens/Home/components/Contact/component/ContactsCta.tsx 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/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index d0cf8ed8d..1e507f91a 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -9,7 +9,6 @@ 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 Buttons from 'src/components/Buttons'; import ContactAddicon from 'src/assets/images/contact-add-icon.svg'; import KeeperModal from 'src/components/KeeperModal'; import ProfileContent from './ProfileContent'; @@ -27,6 +26,9 @@ import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptio 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'; const Contact = () => { const { colorMode } = useColorMode(); @@ -38,7 +40,7 @@ const Contact = () => { const [contactmodalVisible, setContactModalVisible] = useState(false); const [shareContact, setShareContact] = useState(false); const { translations } = useContext(LocalizationContext); - const { contactText } = translations; + 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); @@ -192,9 +194,8 @@ const Contact = () => { - { + { navigation.dispatch( CommonActions.navigate({ name: 'ScanQR', @@ -209,10 +210,21 @@ const Contact = () => { }) ); }} - fullWidth + 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)} @@ -290,5 +302,8 @@ const styles = StyleSheet.create({ bottomButton: { marginTop: wp(10), marginBottom: wp(15), + flexDirection: 'row', + width: '100%', + justifyContent: 'space-between', }, }); 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, + }, +}); From 4d9da9992b50ae5003d1f4d38fa6a2e43ec355be Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 7 Aug 2025 15:31:15 +0530 Subject: [PATCH 76/80] feat: ClearChatPeerInstanceOnAppLock --- src/navigation/AppStateHandler.tsx | 2 ++ src/services/p2p/ChatPeerManager.ts | 4 ++++ 2 files changed, 6 insertions(+) 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/services/p2p/ChatPeerManager.ts b/src/services/p2p/ChatPeerManager.ts index d97118be9..d7652428d 100644 --- a/src/services/p2p/ChatPeerManager.ts +++ b/src/services/p2p/ChatPeerManager.ts @@ -42,6 +42,10 @@ export default class ChatPeerManager { return ChatPeerManager.instance; } + static resetInstance() { + ChatPeerManager.instance = null; + } + async init(seed: string): Promise { try { await this.worklet.start('/app.bundle', bundle, [seed]); From 1ce044d4f45ab5d717204800bd8c3fb627abf2a4 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 7 Aug 2025 15:58:55 +0530 Subject: [PATCH 77/80] fix: ShareOnMissingContactLink/ codeCleanup --- .../components/Contact/component/Contact.tsx | 37 ++----------------- .../Contact/component/ContactHeader.tsx | 11 +----- 2 files changed, 5 insertions(+), 43 deletions(-) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 1e507f91a..05416b048 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -1,5 +1,5 @@ import { Box, ScrollView, useColorMode } from 'native-base'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; +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'; @@ -13,7 +13,6 @@ 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 ContactModalData from './ContactModalData'; import { LocalizationContext } from 'src/context/Localization/LocContext'; import useToastMessage from 'src/hooks/useToastMessage'; import ToastErrorIcon from 'src/assets/images/toast_error.svg'; @@ -37,8 +36,6 @@ const Contact = () => { const [userProfileImage, setUserProfileImage] = useState(null); const [userProfileName, setUserProfileName] = useState(''); const navigation = useNavigation(); - const [contactmodalVisible, setContactModalVisible] = useState(false); - const [shareContact, setShareContact] = useState(false); const { translations } = useContext(LocalizationContext); const { contactText, concierge } = translations; const app: KeeperApp = dbManager.getObjectByIndex(RealmSchema.KeeperApp); @@ -67,14 +64,6 @@ const Contact = () => { } }, [app.profilePicture, app.appName]); - const contactShareLink = useMemo(() => { - if (app?.contactsKey?.secretKey) { - const pubKey = ChatEncryptionManager.derivePublicKey(app?.contactsKey?.secretKey); - return `keeper://contact/${app.contactsKey.publicKey}/${pubKey}/${app.appName}`; - } - return ''; - }, [app.contactsKey.publicKey, app.appName]); - const onRefresh = () => { setRefreshing(true); chatManager.loadPendingMessages(lastBlock); @@ -148,8 +137,6 @@ const Contact = () => { userProfileImage={app.profilePicture} userProfileName={app.appName} setCreateProfile={setCreateProfile} - setContactModalVisible={setContactModalVisible} - setShareContact={setShareContact} /> @@ -164,7 +151,7 @@ const Contact = () => { { - navigation.navigate('keeperSupport'); + navigation.dispatch(CommonActions.navigate({ name: 'keeperSupport' })); }} > @@ -196,6 +183,8 @@ const Contact = () => { { + const pubKey = ChatEncryptionManager.derivePublicKey(app?.contactsKey?.secretKey); + const contactShareLink = `keeper://contact/${app.contactsKey.publicKey}/${pubKey}/${app.appName}`; navigation.dispatch( CommonActions.navigate({ name: 'ScanQR', @@ -243,24 +232,6 @@ const Contact = () => { /> )} /> - setContactModalVisible(false)} - title={shareContact ? contactText.shareContact : contactText.addNewContact} - subTitle={contactText.chooseHowToAdd} - textColor={`${colorMode}.textGreen`} - subTitleColor={`${colorMode}.modalSubtitleBlack`} - modalBackground={`${colorMode}.modalWhiteBackground`} - Content={() => ( - - )} - /> ); }; diff --git a/src/screens/Home/components/Contact/component/ContactHeader.tsx b/src/screens/Home/components/Contact/component/ContactHeader.tsx index bd104591b..fd1964160 100644 --- a/src/screens/Home/components/Contact/component/ContactHeader.tsx +++ b/src/screens/Home/components/Contact/component/ContactHeader.tsx @@ -2,7 +2,6 @@ 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 ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; 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'; @@ -12,17 +11,9 @@ type Props = { userProfileImage: string | null; userProfileName: string; setCreateProfile: (visible: boolean) => void; - setContactModalVisible: (visible: boolean) => void; - setShareContact: (share: boolean) => void; }; -const ContactHeader = ({ - userProfileImage, - userProfileName, - setCreateProfile, - setContactModalVisible, - setShareContact, -}: Props) => { +const ContactHeader = ({ userProfileImage, userProfileName, setCreateProfile }: Props) => { const { colorMode } = useColorMode(); const { translations } = useContext(LocalizationContext); const { contactText } = translations; From 8852d18a031b37502dcddc4c693ce6a005ee1d77 Mon Sep 17 00:00:00 2001 From: cakesoft-vaibhav Date: Thu, 7 Aug 2025 16:17:57 +0530 Subject: [PATCH 78/80] feat:EnabledContactAddByText --- src/screens/Home/components/Contact/component/Contact.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 1e507f91a..7e3322c3d 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -206,6 +206,7 @@ const Contact = () => { importOptions: false, isSingning: true, contactShareData: contactShareLink, + isPSBT: true, }, }) ); From d11a51f0847ced5e7b7315f8c2204ed917d62eac Mon Sep 17 00:00:00 2001 From: Raheel1258 Date: Thu, 7 Aug 2025 17:28:43 +0500 Subject: [PATCH 79/80] fix issues --- src/assets/images/chat-plaaceholde-image.svg | 12 +++++++ .../concierge-query-illustration-dark.svg | 32 ++++++++---------- .../concierge-query-illustration-light.svg | 32 ++++++++---------- src/assets/images/keeper-new-logo-image.png | Bin 0 -> 10456 bytes src/components/KeeperQRCode.tsx | 2 +- src/context/Localization/language/en.json | 2 +- src/context/Localization/language/es.json | 2 +- src/screens/Advisors/Advisors.tsx | 10 +++--- .../components/Contact/component/ChatList.tsx | 19 ++++++----- .../components/Contact/component/ChatRoom.tsx | 15 ++------ .../Contact/component/ChatRoomHeader.tsx | 15 +++----- .../components/Contact/component/Contact.tsx | 5 +-- src/screens/Recieve/ReceiveAddress.tsx | 2 +- 13 files changed, 69 insertions(+), 79 deletions(-) create mode 100644 src/assets/images/chat-plaaceholde-image.svg create mode 100644 src/assets/images/keeper-new-logo-image.png 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/keeper-new-logo-image.png b/src/assets/images/keeper-new-logo-image.png new file mode 100644 index 0000000000000000000000000000000000000000..1aaddc59af7f12cdf53ac325a3996d393319b8f5 GIT binary patch literal 10456 zcmdsd`9IWM*#DU^vS-T{MKqzTH(9dGRH#U!knNVOkhPGV85D`A$PzIULP&(Nha}3L zt&EVZB+FQ4eUAJ4eLa7~^TWL6b3W%>*E#3xwtKaE6C-KPLcy=iFI?O8_9@ zBNDLhfqxspLlpSOapSCwKLA{UyFUbYk}V2fBK$9%IR(nUNG!qz%I&1-NdPL7xoA$T z0LXctGdO7xj9C8eotQHmBKueEh_?`%h1H=;{}z9+OmF{M!`c&h_iO6|UiFI#!+Xk% z&5l08KH^mry6{{;!^!T9-!p7!N19_jderoi!td($qKfR(M=MQVJR7EGQOeUnk~I2p0b=|JD# zJphidF1tl~`M#xf=+y@#Hg&VqkeRe-$ozx?Kea5&@DAmZlCZaUh)~H!@c7aE+IAar zk)DA6XOYCxqAAUtsKcJ92n>Ajetv7d14FU;U@F{&2Pwk~1`8ON3PwTAVc>Ykg##&4 zz)$)9Icw!?N=mXNQ+fmqh?14cKI@;}csfL^w#PqwDj|A~75i2a#7bY_3HR7HOvtkW zg}Ib13(4|xphfDkm2d9L5(M*-B}?RkSa)|J&=?6spO(CCqURpO)BLZWjTHceCJgGM zbsA7HR>e%6Zgc10mH>q!%oVgC4LC}~3Ly7z0@We6&{fSoHt=Eo@gm==S#zYxULcGI z?nBr^j^_|9)4ZVU)hyqu1)%bM9h0WO0DXJ-k>$3(a|qWGcDmIxXf`wi@xbNwaS6{2 zXGWeOE2yUt@%S6zoAq7~fz6HjAF-Q_+fqMLRm2@Ijo=pyyV@oHxP($IN{>`Su$dSbH6s zcey275vVZ`Ql9;s+n-B>{(kbpYq@a|ZD}sDKbBBn*1aRV2&Hs3Eg~b5!Uq1)9EokIwFJ6!7HsW#NiN-$_r(9->}()^l~tydtsezB3T3x6h>&+vvLm*_zkk<;`Gmz^EmzW3zb_<`rw;3pGhy)CME#g1d> zkAn1poT9zY-I5NPDAP6yPywWNp)3v4Bg7Qve$p!+d&R#b47V++PY(&MddZ_1C$IBk zHQB*~Jg3xcwgN*CA_$}e^A%QkjT69{48SHFzc?+KDvX#t2?)X-r{nQvkk|(astvMC zXfXrQgiU@xJgJ@bAMvsjz_PqE`;S1~1Q1jg{r)3Ri9~`#v-tmJYYAotDoA5m%%C)( zUjR6sP2E0oCIQ6a00AMlnw%?);En^pgxF5fRYZy=0uXU&+E-Z-X|0O@b9wE#l8VTM zWB{tnrhDPo9!PfGnD!m6+*cNusiPTd?))wL5Cm!Y)qF$jA`0BgYw!KHnqBT8z!u4` zR$1GkQcnS5O6s;4+?Bm7=o2%hCEkONhrxs6=*SPI350S!@bQrV#&l3xDhLV2%%+RV zs@Xq62Bq?=DdLJqzeAwU*qAnZA%RdM2;6zlk?)KX2pR&wJyifx&@W9?M1lQz?YPBi zcK0WsWmImp?Kgak1}$ghSKFrD`BjvVKqjx9H>O|OaR>!sjp8MZPA3p9@&VDi0+_^f zVMLoWcy-vA)~BF|+=B*vn&?P(Sw&>pDd2d;nD#(K5lKM~LA<29jAFDd2T+6zW(^Xg zB#~GX`BfI}L1}jkf@lb-lm?{TWf4RtNOi87ef%_VhtrzEMNadI{2{MnUzmFrKj9;W=I8`z=*e87ds-VF0jEDCjdE>?)D~07NYG!2eH| zIH1x>{ofeWry~$B291*~o020FS)nLd=i?sDI9ck|fE~4q@3umz`8>4c8SJeU?YTKE+SyqtgPSB1iNR=RJ+nv{{I}Wq0-nv(jem_zLqX-sGC-P4+ z{>n7Uny9gica23_O9%n$+o7(6G;F=r1aE>YXpt6^`KI(`vu(f~(IQ~_U0^Hx9aXK? zgF{`aMCE3D@=U}l^`k(AAgA3qIG-nTMsQ}v3&VW@nmNP8o${q~7AB4x&iM|hG#8^b zPq*G9j?h?%K62;C5K}n;x!&LaDhA{p;Bq^zdn{5^KMOd0baut2zDU*!lK#HBnl*@C zvCXmG??!bBumFw*OZPo&ktT48#54e8uDZ4iz-8tau>u!9aTGp)B742N6XWXIqMN-O zx5^J3&+nDg9-run+;a0Z{q?o{T(+_XW%*_FqnQYmgSJ4$=;A=qKuubU@{k*K<~2G2 zlL(D2kBXY(y()U21cGqk(~kvvdmg!4uGkB1)RD3Xd~`WKSa^PaIGQ0l8wcggWWK#C!D;_gPzRQ5Vb0W$PIS03=pN{>mBbn>*q^qbAn{?&l zs_3hY>tz*QrqEhYP|zy(*{lw2F5nw9zX!GzrpAOq8GNXhE2^SvMr>0yK6~2@{w%$W zUO3tb78`J+I(d7lZ~4+w<=!70I>s}V+Dg$TBCdO818OjvN#ToXwS}G)SJH{U;K@Cu z+hu4nl|S+lr+xQ+)M~ZFNh!CmB0F(y1%_(Yp&W`LBX#=OR=G{paaE0eT*KA3-A+&V za<_tSI_%^GS#b^ELen>^ZD`Dit?x0{ou#j7YRPG+Glog@2;Wz|y`*rpH#WLtX1^q@ zK48AcXSHgKxE;CTK`kw-26A6Mcd%hC!uSyQPKOa2Q>PA{nh-4#70_Fv1 z(Y7loTIyD&-mms2&DdD5JR3SFlz1e@Bzw8nDG6!|sU{>PPhTYl0JR};l*`Qb6vdU= z&ho{=|5n3X>%MI2O;rCP@PAtrfE{Jqe z{p#LnmdMmvS_*Vhr6{&+Kb~4TialmORmq`Rve(;zHsdKAB{zOT z7@zSJfc1~d`cdxoiPHwBtHqq3IqbC(nOI<>!w7`8a!ko5wmWaX*~%wOz^wcJ*nhul zb{aLqnQ~})FB=j%_h9n%LRkls%Dl8JH9R|ttu)accfiU8+B@iG~vJsTC8 z>_#Szsp;f^lj0hbjuH12bGA@_zGq}UMRr7z(j^%2FR3k)2k{LVoyhR+^csyCZRH{= z>It7X-Fe8*JONMFTklIOP5KokRDEMX8d{nso=f)G{>dU6g+6b(7xklVW@`6W$gk2> zMiyq+AI2ru;I#x4(u6@ryvRn~2&1#8_qon~>wqOHYPxRfOGI)lqi#y>cw@hE&hH?7 z4a>E-0T{v`E1ZVT&1*?IbI+%>o4MGH442$fdt-uQ<>7{FLd^4Px1PR4PC<`HqIf1b zSwNk&)f1{HUX18pNdg1ybM1u&3)d-2liQ)a-(-;JO9+B-;@|tS@2&4bY*q~A+xmT;sKxj2 zb3nCPP90qs+4M}xlE|mSexf;u^2wpfV6lz~@Gis)IHFrq579qj<3%5ND){VX% zMUmfRZ!8a6U8_2&qK~UAu9W|>c^Yp4wdvx$=2ib7jT$f_j+zMn_toUW$n3PFxYkTD zX_^BfK_Z80_E`dqu9^M3xwzcT}O$-Rtnc zRB9eF+WF9R=Sb&{QP|-oB`g!FJ^C(>>rG=~vFdy8Ivg>`@{gOZ_r}s>rjztzb)0vN_}^O}%bgXM#5dsvoxUwmMjJ7xC*ZRWe->9?9}h|zZt#)~Qu(&rEB`e_-$YvpuRXGTTEqIU87mDKT7q z8+!?=J=$pcN*LMF9QRx-bqt#AMw5_#I1$=Umy>&c?V3JwOh-SGsCCOkD%~9EEh_V^ z0?rT@n%>-zD^Lu~p1X6jAE^+Hsdw4R`C5xQ)x*7D%J(wo@`N?d-9SU=Au?wqM-GrS z9_pTwN{r?Nh7-Y`*{JW(6^cvWH{y~cOuZjL$x)_d5-i3?^MGy|i~J6ESm8CVOx!68)ds6oGW9@&4C2+EdIE`IBldreDk- z=Svs)c_SV-3Q-ATh6-B|_;5b+m}I}eT{~8n$i_@W$kS!sz@7P@Jlws$2}5^T;r&*xRJC$fJCER7J{e{& zGuyxKJfA1TFctjUja{AFVRdL!fx!_bRvF>SF#m zdRKlYs4W5Bf<%=S398hcEN1VY*}d@{vN@;ljJYhERONg$U%^+AQWd_(i9r?H!;^!N zUZRPl-l#`-%?Rou%i>Qf2h+r4ZQxSaGptAc9KBK|Txd_-bgRsiF99<-2er6w-dXq7 z^jRAsoqYREdJJs~T!e9sf%SprFcy4gqau5&3lp8z(VW$wl#~nl2qXA8@y61nEY_KG zkAK|73bh*ldRZSCKsoPBs|Ne|+AN*(i1pLw$ZOq^GRxn#>c)6k%+{1+_!mF(f&Ei* zj$+fUty*EE`r|lu>r}a&-H@+{acWk@JU;=_-?;R)R1_*kk?MQXdfIDvYVPziePcPX zEWZO4af$qq4#NVYO<~C%ExM_+?trz#xMxS$0gqOQ?b}j=n?8CuKD*Iw*15150W?!b zKhoc~5cyLT^+L=R@R%Xwtg2Ddp-zfX4lhg?!c0GH);?|o@z5#k*fTU=(ARW*H7R2T z|K03+^64GT+DPo_B>k=MXI?j>Ocf#}75{c=8Z#Uw{OViv<(QghA@q;}Zc|)aAK4<| z$Fi-WojVFl1;GAitS#sJ1wJmiwieub#H_9Hwp^Rfc9FF>oh1#_+s^@|eF@GAN%meG zJqkN*=aV`#I#pX@4OpdO9oo5MhM;)dISev&)3aDb1P1w57{AMMC?Q$aG|SUj`N%$^ zi)=QlTcG`0n?5D{oD1qk6^gJ&o1-OsDGi&X$z^oym@rhxITxwRRZyIV>8~HSa|~&6 z{TXePO-Lg=!qC_Dk17a362_@!IF*Dg(wLzD<-5zK8`i@oBKHtxWW?Iu`^fpX(nnRG-w!M;iX^xdpcnN=~N_LN*>~k{c$}P|e zZ})T;^a)DYWLKJV|NRJrX*Fu;QL`P7O-YK|?W$?PyrNplly zSMEteCMgEDjv@$~ak`jQijt~+ZmN5bQ<@)PV*WlE*^9(-jdHiCw3FZGM;>in+*%Vi zHlWsl7YBAJ_PSUrU15AtHx{X{k4ALR@M>n`CxLtXuJX1LjUrxdWK>OXFTBt(vxTS; zM<+sTX!Cn=BF*)UYzgt*)*=f>nlZCk?;C5M0Qn|%goW3t+R8cE(8^9hxoN|1h5ke4 z%%G=R=lV(={l}S5NdWPVhBp)9p4Fm|9gaBLrV&33slRQq8c7dduhv}11oHkE0TDgIC#K`gL9 z5R_Ds^Bdvn$JbLseqkBhTAs?V!&!~^TI@lU78SF^RbqVtLF@?p6Q+xp{WRAcC$s8)i%!U$<6nRmNZ>78aWRLneh8w2 zLcm?E;$jFt_~#jCU$e>N8(qZA3|St7#JV2-&3FlKAE>rhYkKLINjOh|^{|1y*T308 z=EK)i|D6)c#T>=jtt$;q#naUJDCNn_3Kk>;%Gs;3B_QXlaLtdUTaC3TZeK)= z-#_b^ZeF{DcrE#XF?SApG_z;rLsA5Xfup*#V;H43MHpN7n-z>ZQrp4=LIl7+*OWn@ zdc|poYX4Kn3DmDo#MBGy!1uyk)V_!L9FE_5XuY}M!P^6t%+Gs+{A%OSyQTIzayu_17cqVKzzJuZ|X_WV1Nj{rFFDHiJ}%GfU5I`ZxuSa>n|Qt+A%q2Ll_ zMFT->I~W>(uLx&*W!*Ty%r|D*9{~Z@=h*{%M6B;EP@4n4#y^7r)QJnnfb~nQb>C@+ zEe^oi#|bFMEh#~B%_t-mQH>--zWrS!fy5qyk9Xwg>4p$Ez6?}E3Ycr`fVH0!%n4NR z7&EW&1I~OQ@UL7+j1T2lGB6`9^J1pU0kQKX(~kq(p}gOu+KY4FNf-fDjBYg8_vuY* z%a=}xonx86>{Xq{=tD_4bBUb^YAPLzhA6cN6(PZM`?c2E89vxJ!{UMgW=RXwV{{qt z;6jdeN}$DKK%|DUv??$TBM8?wS+HA%B+^5S2NKMW@`CR|m`^(_^rV zy$7_9_Nd81Frp(IWFZgXH0YIlur;=iJ)}gFzDQM*gcxE6*GGGu^PIP{!+-wLQDi8b z1&${xMOvO?Kc4q(k)9?mTC}KVV!wQlqwX zsFEEC5~i<$Y8>tS1E;d8+8;!+CY6sSH;Q`o=W4MSB0+uuq_#)O%uK7~%Y=xxyQ zC|lS6!z$K2VK60OE)ArcCTvbtF z1R-yHbE+Jo(wQi3xW3M01zz^-M4YZq0x$+a7c9_?jk=BeaDV4+S4|rz({-q z(;MwK1Y}~I7MD6mL`fq<;CS^mu->15nUMl2mrenNq>kL1kVg9upt!8MWuw4{Q|yEt z8=9CVf>6nibPN>6KY>Fpp90yv(>Qb3e}SDK4_`tQko^#0z=n4H3BbDC2FBM`i+Es? z1&uO;?7d+=M(YsaQajp47(WiPJ3`Z}t7-MM?+hP&dA&WTfdpk29nj$mANx`E0;EA7 za=teT*rHFhKoAIr(_Qz9R=T_Lb33O7jumMP`uwDf!*=fPLl+3@72NV1Is9?>KYtO3 zc|^Pq34bm4_i`bw9usChrEbc)A43o>3nD-EZ!Q%xeVN@U9OLH!kt~gB*dFL!EIGb{ z+l6{kiDg&9X1pi~zTwA~sef8g-djm1vEb>0QHqc>Wf2Yl+C%%g9{yF`ZNo_HYTb`)Gy zPU6VXT~FDFBJFs0qQr_v7n$?SMU1$msq82VR#u`sw6cXI+LsOkk}3B zdS|heTQBw|9-&?Yir}83pA=VoB-0^$#+OW$j|JF^zqzJPk#FQ`=^@d(*SAepvr;NO zmTD#~1n%uyM^1+aSs;42-2plfN0(S>$o!U{LRc>hh=&gJ@TH$&5lCT z6b{bXwE>es7KaA4vhd!5_&+dE6e%f+l!-XW3PPWuZ`3WZG;isxTqwmyTzeKa{V#Lb zp>c#ClW_#JR6~f1GtfMhwn7&_LvuU=ftn5oP8WpLd@Dykcq_N#OQk58(;bB&M3caz zFO}LNGfPbvMGnD-5HNk&B2kT6E12cw8blKGwa^dP>S=lSqo6M?^hRI370n|Q-tGjJ z&3QK~yCo?wH=Fw`TY`$b@=`dX^y|x;=~$C?1!gm)zFBjWP52xaNW+>C4>;$O;0Xv> zibKN`2>NbX%VZJnsMIKNo5mbt!GEXPf&>T&v0UO2Rfd@aEtpBb`Up$pQi@3s(8z{3 ziWx~k6av2YrgqZ}qB)}rTRY7S%J2-I6sJ3$-NhGE+#&~bwJy~Fr zk%Ab@uqIc@9_~W@$=aD8dN~v)6dcVzOejd;iOgjtP(!_i9M!Xt1l3WP#R{0<$;#em zjnyTugnQw~sY6ky#57Kj80FFOUYFb!Tnt(=iuYvqnq$;Y1Hzv6zr+cjZ$n%x=!64| z%C25H2T=?H?6>eZE|o)RUL!xX1X^hXS9Tc54wN8NKl{$-QwKd=w@>GzVBlG2vGAw& z+xVCVL5$G{bFtMuMWr6tYSwpG=bMU6BXcNudT>~tJJ&ZD<_3h!o!K*U9S=hXueBIq z0YL2V`+j)AU9vhC0LAo)BPX+HQoJyoE*;$hPfgXEVL}RoAeZa6MrYYF`GAy~B=s+O zk+-C_28H+;`7s3mF%J_vSb?&J2G|czJh%)p@$*fckEw@TkTA}uyTE1e7T@{j3{2eV zW)T`VKp+2Zu0((H;jc6Q7|01TeDU)HC;h%daCFbBEw;K!UKkM&l0r!^nDVsqaqzRl z=1RUZg|F;>GYECnvJ_hR(j6ZN+wpO+M!oB0_T^IrVcr+)JG{x}_3f2@#74}oURH!S zjd&iud@(7vJ#C_Fvz=$Qn|O7%L4tcSyGdDT(C`Ql9yLhAM#yf4_OpR;ku=CQh8mb= zdj5R&1%TB)B8!J%5-6~#InM>NxdFP!mR49kF}wYGCRtCo2wZ>#IjAD%mZcNGqts@zV#riMp=fGaC`_-Bk^So1*lC;Qw&f_F#M?4aoD7F2?`&F{HVXw6~0PmKw$ZUdY_*xGZiPA~?Od!>-I{%63 z{D*IYBCz225&Y!lc>q3+c;T2%V=wXA1vt1Dt|r|H-!J=tw7f;U(-~L_b9`hFZ>`ab zsXeaE55U_KkLQ9Zyds~;Y|1%Wt9Yw&d#dtqoa8xxyoyAfrikDpvj0O90%HGf3-s9W)lM9AsVJ<1EdJoa}7f%Mo?b2w{V0MR~Xvx04e$@d0?$aC8=6@Aw6N7v_df@hC6CsuHK`|P< z3blOiCb!eP+^looObDf~ZO>!<6c8fs&lckgo0M2=OGI$lT1|e-3xFzUpJT^EhrWy%?!&h8B1-;Z706g-e&TCE`8r}Yq)+?CD>bF4_F|^ zi)LuPS-@y4F(NUx{J`}dl@(UAwVGN=-Q9wcx#}9)im{H^Zc~4=kZY_s@DAL_JIdFN z%{#I0W%*?J0=;$iYR~?=7_}Shgd+|A@LSE`&KqzrEy#hPl!%4{UM1C}3MTK#v!)(5 UuCOv;0R=c`c)_6jlw { // // } /> - + {/* - + */} - + {/* {concierge.meetExperts} - + */} - profile + {item.image ? ( + profile + ) : ( + + )} + {item.name} - {item.lastMessage} + {item.lastMessage.length > 25 + ? item.lastMessage.slice(0, 25) + '...' + : item.lastMessage} diff --git a/src/screens/Home/components/Contact/component/ChatRoom.tsx b/src/screens/Home/components/Contact/component/ChatRoom.tsx index 43c2e6164..1fc37e6ef 100644 --- a/src/screens/Home/components/Contact/component/ChatRoom.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoom.tsx @@ -13,7 +13,6 @@ 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 PlaceHolderImage from 'src/assets/images/contact-placeholder-image.png'; // 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'; @@ -23,6 +22,7 @@ import { ChatEncryptionManager } from 'src/utils/service-utilities/ChatEncryptio 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 = {}; @@ -156,11 +156,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community style={styles.avatar} /> ) : ( - placeHolder + ))} @@ -213,12 +209,7 @@ const ChatRoom = ({ userProfileImage, receiverProfileImage, messages, community marginBottom={hp(20)} /> ) : ( - placeHolder + ))} ); diff --git a/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx index 58c582f0d..f6bb30b85 100644 --- a/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx +++ b/src/screens/Home/components/Contact/component/ChatRoomHeader.tsx @@ -4,7 +4,7 @@ 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/contact-placeholder-image.png'; +import ChatPlaceHolderIcon from 'src/assets/images/chat-plaaceholde-image.svg'; import { StatusBar } from 'react-native'; import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg'; @@ -24,10 +24,7 @@ const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEdit - setOpenEditModal(true)} - > + {receiverProfileImage ? ( ) : ( - placeHolder + )} @@ -50,7 +43,7 @@ const ChatRoomHeader = ({ receiverProfileImage, receiverProfileName, setOpenEdit {receiverProfileName} - + diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 05416b048..08a1ffee8 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -133,7 +133,8 @@ const Contact = () => { return ( - { {contactText.recentChats} - + */} } showsVerticalScrollIndicator={false} diff --git a/src/screens/Recieve/ReceiveAddress.tsx b/src/screens/Recieve/ReceiveAddress.tsx index c844642f7..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} Date: Mon, 11 Aug 2025 12:48:00 +0530 Subject: [PATCH 80/80] add loading indicator and connection handling --- .../Home/components/Contact/component/Contact.tsx | 5 +++++ src/services/p2p/ChatPeerManager.ts | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/screens/Home/components/Contact/component/Contact.tsx b/src/screens/Home/components/Contact/component/Contact.tsx index 05416b048..af6e5efe3 100644 --- a/src/screens/Home/components/Contact/component/Contact.tsx +++ b/src/screens/Home/components/Contact/component/Contact.tsx @@ -28,6 +28,7 @@ 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(); @@ -44,13 +45,16 @@ const Contact = () => { 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', ); @@ -133,6 +137,7 @@ const Contact = () => { return ( + { 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) { @@ -59,6 +71,8 @@ export default class ChatPeerManager { 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)); } @@ -82,6 +96,7 @@ export default class ChatPeerManager { } } } + await connectionPromise; return true; } catch (error) { console.error('Error initializing chat peer manager:', error);