pear-mobile 0.0.1-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ module.exports = "89278\n{\"version\":0,\"id\":\"4a4dffb2fd0f7a8eb7ef0bc369083fbbed38dfd8c345dfaecb905acdbdd0ae5e\",\"main\":\"/lib/pear.js\",\"imports\":{},\"resolutions\":{\"/lib/pear.js\":{\"#package\":\"/package.json\",\"bare-path\":\"/node_modules/bare-path/index.js\",\"bare-rpc\":\"/node_modules/bare-rpc/index.js\",\"corestore\":\"/node_modules/corestore/index.js\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\",\"hyperdrive\":\"/node_modules/hyperdrive/index.js\",\"hyperswarm\":\"/node_modules/hyperswarm/index.js\",\"localdrive\":\"/node_modules/localdrive/index.js\",\"pear-link\":\"/node_modules/pear-link/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\"},\"/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/adaptive-timeout/index.js\":{\"#package\":\"/node_modules/adaptive-timeout/package.json\",\"xache\":\"/node_modules/xache/index.js\"},\"/node_modules/adaptive-timeout/package.json\":{},\"/node_modules/b4a/index.js\":{\"#package\":\"/node_modules/b4a/package.json\"},\"/node_modules/b4a/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-fs/binding.js\":{\"#package\":\"/node_modules/bare-fs/package.json\",\".\":{\"ios\":\"linked:bare-fs.4.5.4.framework/bare-fs.4.5.4\",\"android\":\"linked:libbare-fs.4.5.4.so\"}},\"/node_modules/bare-fs/index.js\":{\"#package\":\"/node_modules/bare-fs/package.json\",\"./binding\":\"/node_modules/bare-fs/binding.js\",\"./lib/constants\":\"/node_modules/bare-fs/lib/constants.js\",\"./lib/errors\":\"/node_modules/bare-fs/lib/errors.js\",\"./promises\":\"/node_modules/bare-fs/promises.js\",\"bare-events\":\"/node_modules/bare-events/index.js\",\"bare-path\":\"/node_modules/bare-path/index.js\",\"bare-stream\":\"/node_modules/bare-stream/index.js\",\"bare-url\":\"/node_modules/bare-url/index.js\",\"fast-fifo\":\"/node_modules/fast-fifo/index.js\"},\"/node_modules/bare-fs/lib/constants.js\":{\"#package\":\"/node_modules/bare-fs/package.json\",\"../binding\":\"/node_modules/bare-fs/binding.js\"},\"/node_modules/bare-fs/lib/errors.js\":{\"#package\":\"/node_modules/bare-fs/package.json\",\"bare-os\":\"/node_modules/bare-os/index.js\"},\"/node_modules/bare-fs/package.json\":{},\"/node_modules/bare-fs/promises.js\":{\"#package\":\"/node_modules/bare-fs/package.json\",\".\":\"/node_modules/bare-fs/index.js\",\"bare-events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/bare-os/binding.js\":{\"#package\":\"/node_modules/bare-os/package.json\",\".\":{\"ios\":\"linked:bare-os.3.6.2.framework/bare-os.3.6.2\",\"android\":\"linked:libbare-os.3.6.2.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-event\":\"/node_modules/bare-rpc/lib/incoming-event.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-event\":\"/node_modules/bare-rpc/lib/outgoing-event.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-event.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\",\"compact-encoding\":\"/node_modules/compact-encoding/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-event.js\":{\"#package\":\"/node_modules/bare-rpc/package.json\",\"./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\",\"compact-encoding\":\"/node_modules/compact-encoding/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-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.3.2.framework/bare-url.2.3.2\",\"android\":\"linked:libbare-url.2.3.2.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\",\"./lib/url-search-params\":\"/node_modules/bare-url/lib/url-search-params.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/lib/url-search-params.js\":{\"#package\":\"/node_modules/bare-url/package.json\"},\"/node_modules/bare-url/package.json\":{},\"/node_modules/big-sparse-array/index.js\":{\"#package\":\"/node_modules/big-sparse-array/package.json\"},\"/node_modules/big-sparse-array/package.json\":{},\"/node_modules/binary-stream-equals/index.js\":{\"#package\":\"/node_modules/binary-stream-equals/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/binary-stream-equals/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/codecs/index.js\":{\"#package\":\"/node_modules/codecs/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/codecs/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/corestore/index.js\":{\"#package\":\"/node_modules/corestore/package.json\",\"./lib/audit.js\":\"/node_modules/corestore/lib/audit.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"hypercore\":\"/node_modules/hypercore/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\",\"which-runtime\":\"/node_modules/which-runtime/index.js\"},\"/node_modules/corestore/lib/audit.js\":{\"#package\":\"/node_modules/corestore/package.json\"},\"/node_modules/corestore/package.json\":{},\"/node_modules/debounceify/index.js\":{\"#package\":\"/node_modules/debounceify/package.json\"},\"/node_modules/debounceify/package.json\":{},\"/node_modules/device-file/index.js\":{\"#package\":\"/node_modules/device-file/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"fd-lock\":\"/node_modules/fd-lock/index.js\",\"fs\":\"/node_modules/bare-fs/index.js\",\"fs-native-extensions\":\"/node_modules/fs-native-extensions/index.js\",\"path\":\"/node_modules/bare-path/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\"},\"/node_modules/device-file/package.json\":{},\"/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/health\":\"/node_modules/dht-rpc/lib/health.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/health.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\",\"adaptive-timeout\":\"/node_modules/adaptive-timeout/index.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/events-universal/bare.js\":{\"#package\":\"/node_modules/events-universal/package.json\",\"bare-events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/events-universal/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/fd-lock/index.js\":{\"#package\":\"/node_modules/fd-lock/package.json\",\"fs\":\"/node_modules/bare-fs/index.js\",\"fs-native-extensions\":\"/node_modules/fs-native-extensions/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"resource-on-exit\":\"/node_modules/resource-on-exit/index.js\"},\"/node_modules/fd-lock/package.json\":{},\"/node_modules/flat-tree/index.js\":{\"#package\":\"/node_modules/flat-tree/package.json\"},\"/node_modules/flat-tree/package.json\":{},\"/node_modules/fs-native-extensions/binding.js\":{\"#package\":\"/node_modules/fs-native-extensions/package.json\",\".\":{\"ios\":\"linked:fs-native-extensions.1.4.5.framework/fs-native-extensions.1.4.5\",\"android\":\"linked:libfs-native-extensions.1.4.5.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.js\"},\"/node_modules/fs-native-extensions/index.js\":{\"#package\":\"/node_modules/fs-native-extensions/package.json\",\"./binding\":\"/node_modules/fs-native-extensions/binding.js\",\"which-runtime\":\"/node_modules/which-runtime/index.js\"},\"/node_modules/fs-native-extensions/package.json\":{},\"/node_modules/hyperbee/index.js\":{\"#package\":\"/node_modules/hyperbee/package.json\",\"./iterators/diff\":\"/node_modules/hyperbee/iterators/diff.js\",\"./iterators/history\":\"/node_modules/hyperbee/iterators/history.js\",\"./iterators/local\":\"/node_modules/hyperbee/iterators/local.js\",\"./iterators/range\":\"/node_modules/hyperbee/iterators/range.js\",\"./lib/extension\":\"/node_modules/hyperbee/lib/extension.js\",\"./lib/messages\":\"/node_modules/hyperbee/lib/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"codecs\":\"/node_modules/codecs/index.js\",\"debounceify\":\"/node_modules/debounceify/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"mutexify/promise\":\"/node_modules/mutexify/promise.js\",\"rache\":\"/node_modules/rache/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"resolve-reject-promise\":\"/node_modules/resolve-reject-promise/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"streamx\":\"/node_modules/streamx/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hyperbee/iterators/diff.js\":{\"#package\":\"/node_modules/hyperbee/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hyperbee/iterators/history.js\":{\"#package\":\"/node_modules/hyperbee/package.json\"},\"/node_modules/hyperbee/iterators/local.js\":{\"#package\":\"/node_modules/hyperbee/package.json\"},\"/node_modules/hyperbee/iterators/range.js\":{\"#package\":\"/node_modules/hyperbee/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hyperbee/lib/extension.js\":{\"#package\":\"/node_modules/hyperbee/package.json\",\"./messages\":\"/node_modules/hyperbee/lib/messages.js\"},\"/node_modules/hyperbee/lib/messages.js\":{\"#package\":\"/node_modules/hyperbee/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"protocol-buffers-encodings\":\"/node_modules/protocol-buffers-encodings/index.js\"},\"/node_modules/hyperbee/package.json\":{},\"/node_modules/hyperblobs/index.js\":{\"#package\":\"/node_modules/hyperblobs/package.json\",\"./lib/monitor\":\"/node_modules/hyperblobs/lib/monitor.js\",\"./lib/streams\":\"/node_modules/hyperblobs/lib/streams.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"mutexify\":\"/node_modules/mutexify/index.js\"},\"/node_modules/hyperblobs/lib/monitor.js\":{\"#package\":\"/node_modules/hyperblobs/package.json\",\"events\":\"/node_modules/bare-events/index.js\",\"speedometer\":\"/node_modules/speedometer/index.js\"},\"/node_modules/hyperblobs/lib/prefetcher.js\":{\"#package\":\"/node_modules/hyperblobs/package.json\"},\"/node_modules/hyperblobs/lib/streams.js\":{\"#package\":\"/node_modules/hyperblobs/package.json\",\"./prefetcher\":\"/node_modules/hyperblobs/lib/prefetcher.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hyperblobs/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-errors/index.js\":{\"#package\":\"/node_modules/hypercore-errors/package.json\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\"},\"/node_modules/hypercore-errors/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/hypercore-storage/index.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"./lib/keys.js\":\"/node_modules/hypercore-storage/lib/keys.js\",\"./lib/streams.js\":\"/node_modules/hypercore-storage/lib/streams.js\",\"./lib/tx.js\":\"/node_modules/hypercore-storage/lib/tx.js\",\"./lib/view.js\":\"/node_modules/hypercore-storage/lib/view.js\",\"./migrations/0\":\"/node_modules/hypercore-storage/migrations/0/index.js\",\"device-file\":\"/node_modules/device-file/index.js\",\"fs\":\"/node_modules/bare-fs/index.js\",\"path\":\"/node_modules/bare-path/index.js\",\"resolve-reject-promise\":\"/node_modules/resolve-reject-promise/index.js\",\"rocksdb-native\":\"/node_modules/rocksdb-native/index.js\",\"scope-lock\":\"/node_modules/scope-lock/index.js\"},\"/node_modules/hypercore-storage/lib/block-dependency-stream.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"./keys\":\"/node_modules/hypercore-storage/lib/keys.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hypercore-storage/lib/close-error-stream.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hypercore-storage/lib/keys.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"index-encoder\":\"/node_modules/index-encoder/index.js\"},\"/node_modules/hypercore-storage/lib/streams.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"../spec/hyperschema\":\"/node_modules/hypercore-storage/spec/hyperschema/index.js\",\"./block-dependency-stream.js\":\"/node_modules/hypercore-storage/lib/block-dependency-stream.js\",\"./keys.js\":\"/node_modules/hypercore-storage/lib/keys.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hypercore-storage/lib/tx.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"../spec/hyperschema\":\"/node_modules/hypercore-storage/spec/hyperschema/index.js\",\"./keys.js\":\"/node_modules/hypercore-storage/lib/keys.js\",\"./view.js\":\"/node_modules/hypercore-storage/lib/view.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\"},\"/node_modules/hypercore-storage/lib/view.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"./close-error-stream.js\":\"/node_modules/hypercore-storage/lib/close-error-stream.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hypercore-storage/migrations/0/index.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"../../lib/tx.js\":\"/node_modules/hypercore-storage/lib/tx.js\",\"../../lib/view.js\":\"/node_modules/hypercore-storage/lib/view.js\",\"./messages.js\":\"/node_modules/hypercore-storage/migrations/0/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"fs\":\"/node_modules/bare-fs/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"path\":\"/node_modules/bare-path/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hypercore-storage/migrations/0/messages.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/hypercore-storage/package.json\":{},\"/node_modules/hypercore-storage/spec/hyperschema/index.js\":{\"#package\":\"/node_modules/hypercore-storage/package.json\",\"hyperschema/runtime\":\"/node_modules/hyperschema/runtime.cjs\"},\"/node_modules/hypercore/index.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./lib/caps\":\"/node_modules/hypercore/lib/caps.js\",\"./lib/core\":\"/node_modules/hypercore/lib/core.js\",\"./lib/default-encryption\":\"/node_modules/hypercore/lib/default-encryption.js\",\"./lib/download\":\"/node_modules/hypercore/lib/download.js\",\"./lib/info\":\"/node_modules/hypercore/lib/info.js\",\"./lib/inspect\":\"/node_modules/hypercore/lib/inspect.js\",\"./lib/merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"./lib/replicator\":\"/node_modules/hypercore/lib/replicator.js\",\"./lib/streams\":\"/node_modules/hypercore/lib/streams.js\",\"./lib/verifier\":\"/node_modules/hypercore/lib/verifier.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\",\"events\":\"/node_modules/bare-events/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\",\"hypercore-storage\":\"/node_modules/hypercore-storage/index.js\",\"is-options\":\"/node_modules/is-options/index.js\",\"protomux\":\"/node_modules/protomux/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hypercore/lib/audit.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\"},\"/node_modules/hypercore/lib/bit-interlude.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./compat\":\"/node_modules/hypercore/lib/compat.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hypercore/lib/bitfield.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./compat\":\"/node_modules/hypercore/lib/compat.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"big-sparse-array\":\"/node_modules/big-sparse-array/index.js\"},\"/node_modules/hypercore/lib/caps.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"sodium-universal\":\"/node_modules/sodium-universal/index.js\"},\"/node_modules/hypercore/lib/compat.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"quickbit-universal\":\"/node_modules/quickbit-universal/index.js\",\"quickbit-universal/fallback\":\"/node_modules/quickbit-universal/fallback.js\"},\"/node_modules/hypercore/lib/copy-prologue.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./bitfield\":\"/node_modules/hypercore/lib/bitfield.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"quickbit-universal\":\"/node_modules/quickbit-universal/index.js\"},\"/node_modules/hypercore/lib/core.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./audit\":\"/node_modules/hypercore/lib/audit.js\",\"./bit-interlude\":\"/node_modules/hypercore/lib/bit-interlude.js\",\"./bitfield\":\"/node_modules/hypercore/lib/bitfield.js\",\"./copy-prologue\":\"/node_modules/hypercore/lib/copy-prologue.js\",\"./merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"./mutex\":\"/node_modules/hypercore/lib/mutex.js\",\"./remote-bitfield\":\"/node_modules/hypercore/lib/remote-bitfield.js\",\"./replicator\":\"/node_modules/hypercore/lib/replicator.js\",\"./session-state\":\"/node_modules/hypercore/lib/session-state.js\",\"./verifier\":\"/node_modules/hypercore/lib/verifier.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"unslab\":\"/node_modules/unslab/index.js\",\"z32\":\"/node_modules/z32/index.js\"},\"/node_modules/hypercore/lib/default-encryption.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./caps\":\"/node_modules/hypercore/lib/caps.js\",\"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/lib/download.js\":{\"#package\":\"/node_modules/hypercore/package.json\"},\"/node_modules/hypercore/lib/hotswap-queue.js\":{\"#package\":\"/node_modules/hypercore/package.json\"},\"/node_modules/hypercore/lib/info.js\":{\"#package\":\"/node_modules/hypercore/package.json\"},\"/node_modules/hypercore/lib/inspect.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/hypercore/lib/merkle-tree.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./caps\":\"/node_modules/hypercore/lib/caps.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hypercore/lib/messages.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./caps\":\"/node_modules/hypercore/lib/caps.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hypercore/lib/multisig.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"./messages\":\"/node_modules/hypercore/lib/messages.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\"},\"/node_modules/hypercore/lib/mutex.js\":{\"#package\":\"/node_modules/hypercore/package.json\"},\"/node_modules/hypercore/lib/receiver-queue.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"fast-fifo\":\"/node_modules/fast-fifo/index.js\"},\"/node_modules/hypercore/lib/remote-bitfield.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./compat\":\"/node_modules/hypercore/lib/compat.js\",\"big-sparse-array\":\"/node_modules/big-sparse-array/index.js\"},\"/node_modules/hypercore/lib/replicator.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./caps\":\"/node_modules/hypercore/lib/caps.js\",\"./hotswap-queue\":\"/node_modules/hypercore/lib/hotswap-queue.js\",\"./merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"./messages\":\"/node_modules/hypercore/lib/messages.js\",\"./mutex\":\"/node_modules/hypercore/lib/mutex.js\",\"./receiver-queue\":\"/node_modules/hypercore/lib/receiver-queue.js\",\"./remote-bitfield\":\"/node_modules/hypercore/lib/remote-bitfield.js\",\"./wants\":\"/node_modules/hypercore/lib/wants.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"random-array-iterator\":\"/node_modules/random-array-iterator/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\"},\"/node_modules/hypercore/lib/session-state.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./bitfield\":\"/node_modules/hypercore/lib/bitfield.js\",\"./merkle-tree\":\"/node_modules/hypercore/lib/merkle-tree.js\",\"./mutex\":\"/node_modules/hypercore/lib/mutex.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"nanoassert\":\"/node_modules/nanoassert/index.js\",\"quickbit-universal\":\"/node_modules/quickbit-universal/index.js\"},\"/node_modules/hypercore/lib/streams.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/hypercore/lib/verifier.js\":{\"#package\":\"/node_modules/hypercore/package.json\",\"./caps\":\"/node_modules/hypercore/lib/caps.js\",\"./messages\":\"/node_modules/hypercore/lib/messages.js\",\"./multisig\":\"/node_modules/hypercore/lib/multisig.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"flat-tree\":\"/node_modules/flat-tree/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"unslab\":\"/node_modules/unslab/index.js\"},\"/node_modules/hypercore/lib/wants.js\":{\"#package\":\"/node_modules/hypercore/package.json\"},\"/node_modules/hypercore/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\",\"xache\":\"/node_modules/xache/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/hyperdrive/index.js\":{\"#package\":\"/node_modules/hyperdrive/package.json\",\"./lib/download\":\"/node_modules/hyperdrive/lib/download.js\",\"./lib/monitor\":\"/node_modules/hyperdrive/lib/monitor.js\",\"hyperbee\":\"/node_modules/hyperbee/index.js\",\"hyperblobs\":\"/node_modules/hyperblobs/index.js\",\"hypercore\":\"/node_modules/hypercore/index.js\",\"hypercore-crypto\":\"/node_modules/hypercore-crypto/index.js\",\"hypercore-errors\":\"/node_modules/hypercore-errors/index.js\",\"is-options\":\"/node_modules/is-options/index.js\",\"mirror-drive\":\"/node_modules/mirror-drive/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"streamx\":\"/node_modules/streamx/index.js\",\"sub-encoder\":\"/node_modules/sub-encoder/index.js\",\"unix-path-resolve\":\"/node_modules/unix-path-resolve/index.js\"},\"/node_modules/hyperdrive/lib/download.js\":{\"#package\":\"/node_modules/hyperdrive/package.json\",\"ready-resource\":\"/node_modules/ready-resource/index.js\"},\"/node_modules/hyperdrive/lib/monitor.js\":{\"#package\":\"/node_modules/hyperdrive/package.json\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"safety-catch\":\"/node_modules/safety-catch/index.js\",\"speedometer\":\"/node_modules/speedometer/index.js\"},\"/node_modules/hyperdrive/package.json\":{},\"/node_modules/hyperschema/package.json\":{},\"/node_modules/hyperschema/runtime.cjs\":{\"#package\":\"/node_modules/hyperschema/package.json\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/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\",\"streamx\":\"/node_modules/streamx/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/index-encoder/index.js\":{\"#package\":\"/node_modules/index-encoder/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/index-encoder/package.json\":{},\"/node_modules/is-options/index.js\":{\"#package\":\"/node_modules/is-options/package.json\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/is-options/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/localdrive/index.js\":{\"#package\":\"/node_modules/localdrive/package.json\",\"./streams.js\":\"/node_modules/localdrive/streams.js\",\"b4a\":\"/node_modules/b4a/index.js\",\"fs\":\"/node_modules/bare-fs/index.js\",\"fs/promises\":\"/node_modules/bare-fs/promises.js\",\"mirror-drive\":\"/node_modules/mirror-drive/index.js\",\"mutexify/promise\":\"/node_modules/mutexify/promise.js\",\"path\":\"/node_modules/bare-path/index.js\",\"unix-path-resolve\":\"/node_modules/unix-path-resolve/index.js\"},\"/node_modules/localdrive/package.json\":{},\"/node_modules/localdrive/streams.js\":{\"#package\":\"/node_modules/localdrive/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"fs\":\"/node_modules/bare-fs/index.js\",\"fs/promises\":\"/node_modules/bare-fs/promises.js\",\"path\":\"/node_modules/bare-path/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/mirror-drive/index.js\":{\"#package\":\"/node_modules/mirror-drive/package.json\",\"binary-stream-equals\":\"/node_modules/binary-stream-equals/index.js\",\"events\":\"/node_modules/bare-events/index.js\",\"same-data\":\"/node_modules/same-data/index.js\",\"speedometer\":\"/node_modules/speedometer/index.js\",\"streamx\":\"/node_modules/streamx/index.js\",\"unix-path-resolve\":\"/node_modules/unix-path-resolve/index.js\"},\"/node_modules/mirror-drive/package.json\":{},\"/node_modules/mutexify/index.js\":{\"#package\":\"/node_modules/mutexify/package.json\",\"queue-tick\":\"/node_modules/queue-tick/process-next-tick.js\"},\"/node_modules/mutexify/package.json\":{},\"/node_modules/mutexify/promise.js\":{\"#package\":\"/node_modules/mutexify/package.json\",\".\":\"/node_modules/mutexify/index.js\"},\"/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/pear-aliases/index.js\":{\"#package\":\"/node_modules/pear-aliases/package.json\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\"},\"/node_modules/pear-aliases/package.json\":{},\"/node_modules/pear-errors/index.js\":{\"#package\":\"/node_modules/pear-errors/package.json\"},\"/node_modules/pear-errors/package.json\":{},\"/node_modules/pear-link/index.js\":{\"#package\":\"/node_modules/pear-link/package.json\",\"hypercore-id-encoding\":\"/node_modules/hypercore-id-encoding/index.js\",\"path\":\"/node_modules/bare-path/index.js\",\"pear-aliases\":\"/node_modules/pear-aliases/index.js\",\"pear-errors\":\"/node_modules/pear-errors/index.js\"},\"/node_modules/pear-link/package.json\":{},\"/node_modules/protocol-buffers-encodings/index.js\":{\"#package\":\"/node_modules/protocol-buffers-encodings/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"signed-varint\":\"/node_modules/signed-varint/index.js\",\"varint\":\"/node_modules/varint/index.js\"},\"/node_modules/protocol-buffers-encodings/package.json\":{},\"/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/quickbit-native/binding.js\":{\"#package\":\"/node_modules/quickbit-native/package.json\",\".\":{\"ios\":\"linked:quickbit-native.2.4.8.framework/quickbit-native.2.4.8\",\"android\":\"linked:libquickbit-native.2.4.8.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.js\"},\"/node_modules/quickbit-native/index.js\":{\"#package\":\"/node_modules/quickbit-native/package.json\",\"./binding\":\"/node_modules/quickbit-native/binding.js\"},\"/node_modules/quickbit-native/package.json\":{},\"/node_modules/quickbit-universal/fallback.js\":{\"#package\":\"/node_modules/quickbit-universal/package.json\",\"simdle-universal\":\"/node_modules/simdle-universal/index.js\"},\"/node_modules/quickbit-universal/index.js\":{\"#package\":\"/node_modules/quickbit-universal/package.json\",\"./fallback\":\"/node_modules/quickbit-universal/fallback.js\",\"quickbit-native\":\"/node_modules/quickbit-native/index.js\"},\"/node_modules/quickbit-universal/package.json\":{},\"/node_modules/rache/index.js\":{\"#package\":\"/node_modules/rache/package.json\"},\"/node_modules/rache/package.json\":{},\"/node_modules/random-array-iterator/index.js\":{\"#package\":\"/node_modules/random-array-iterator/package.json\"},\"/node_modules/random-array-iterator/package.json\":{},\"/node_modules/ready-resource/index.js\":{\"#package\":\"/node_modules/ready-resource/package.json\",\"events\":\"/node_modules/bare-events/index.js\"},\"/node_modules/ready-resource/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/refcounter/index.js\":{\"#package\":\"/node_modules/refcounter/package.json\"},\"/node_modules/refcounter/package.json\":{},\"/node_modules/require-addon/lib/bare.js\":{\"#package\":\"/node_modules/require-addon/package.json\"},\"/node_modules/require-addon/package.json\":{},\"/node_modules/resolve-reject-promise/index.js\":{\"#package\":\"/node_modules/resolve-reject-promise/package.json\"},\"/node_modules/resolve-reject-promise/package.json\":{},\"/node_modules/resource-on-exit/index.js\":{\"#package\":\"/node_modules/resource-on-exit/package.json\"},\"/node_modules/resource-on-exit/package.json\":{},\"/node_modules/rocksdb-native/binding.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\".\":{\"ios\":\"linked:rocksdb-native.3.13.0.framework/rocksdb-native.3.13.0\",\"android\":\"linked:librocksdb-native.3.13.0.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.js\"},\"/node_modules/rocksdb-native/index.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"./lib/column-family\":\"/node_modules/rocksdb-native/lib/column-family.js\",\"./lib/constants\":\"/node_modules/rocksdb-native/lib/constants.js\",\"./lib/filter-policy\":\"/node_modules/rocksdb-native/lib/filter-policy.js\",\"./lib/iterator\":\"/node_modules/rocksdb-native/lib/iterator.js\",\"./lib/snapshot\":\"/node_modules/rocksdb-native/lib/snapshot.js\",\"./lib/state\":\"/node_modules/rocksdb-native/lib/state.js\"},\"/node_modules/rocksdb-native/lib/batch.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"../binding\":\"/node_modules/rocksdb-native/binding.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\"},\"/node_modules/rocksdb-native/lib/column-family.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"../binding\":\"/node_modules/rocksdb-native/binding.js\",\"./constants\":\"/node_modules/rocksdb-native/lib/constants.js\",\"./filter-policy\":\"/node_modules/rocksdb-native/lib/filter-policy.js\"},\"/node_modules/rocksdb-native/lib/constants.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\"},\"/node_modules/rocksdb-native/lib/filter-policy.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\"},\"/node_modules/rocksdb-native/lib/iterator.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"../binding\":\"/node_modules/rocksdb-native/binding.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"streamx\":\"/node_modules/streamx/index.js\"},\"/node_modules/rocksdb-native/lib/snapshot.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"../binding\":\"/node_modules/rocksdb-native/binding.js\"},\"/node_modules/rocksdb-native/lib/state.js\":{\"#package\":\"/node_modules/rocksdb-native/package.json\",\"../binding\":\"/node_modules/rocksdb-native/binding.js\",\"./batch\":\"/node_modules/rocksdb-native/lib/batch.js\",\"./column-family\":\"/node_modules/rocksdb-native/lib/column-family.js\",\"./constants\":\"/node_modules/rocksdb-native/lib/constants.js\",\"compact-encoding\":\"/node_modules/compact-encoding/index.js\",\"ready-resource\":\"/node_modules/ready-resource/index.js\",\"refcounter\":\"/node_modules/refcounter/index.js\",\"resolve-reject-promise\":\"/node_modules/resolve-reject-promise/index.js\",\"signal-promise\":\"/node_modules/signal-promise/index.js\"},\"/node_modules/rocksdb-native/package.json\":{},\"/node_modules/safety-catch/index.js\":{\"#package\":\"/node_modules/safety-catch/package.json\"},\"/node_modules/safety-catch/package.json\":{},\"/node_modules/same-data/index.js\":{\"#package\":\"/node_modules/same-data/package.json\"},\"/node_modules/same-data/package.json\":{},\"/node_modules/scope-lock/index.js\":{\"#package\":\"/node_modules/scope-lock/package.json\"},\"/node_modules/scope-lock/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/signed-varint/index.js\":{\"#package\":\"/node_modules/signed-varint/package.json\",\"varint\":\"/node_modules/varint/index.js\"},\"/node_modules/signed-varint/package.json\":{},\"/node_modules/simdle-native/binding.js\":{\"#package\":\"/node_modules/simdle-native/package.json\",\".\":{\"ios\":\"linked:simdle-native.1.3.9.framework/simdle-native.1.3.9\",\"android\":\"linked:libsimdle-native.1.3.9.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.js\"},\"/node_modules/simdle-native/index.js\":{\"#package\":\"/node_modules/simdle-native/package.json\",\"./binding\":\"/node_modules/simdle-native/binding.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/simdle-native/package.json\":{},\"/node_modules/simdle-universal/fallback.js\":{\"#package\":\"/node_modules/simdle-universal/package.json\",\"./scalar\":\"/node_modules/simdle-universal/scalar.js\",\"b4a\":\"/node_modules/b4a/index.js\"},\"/node_modules/simdle-universal/index.js\":{\"#package\":\"/node_modules/simdle-universal/package.json\",\"./fallback\":\"/node_modules/simdle-universal/fallback.js\",\"simdle-native\":\"/node_modules/simdle-native/index.js\"},\"/node_modules/simdle-universal/package.json\":{},\"/node_modules/simdle-universal/scalar.js\":{\"#package\":\"/node_modules/simdle-universal/package.json\"},\"/node_modules/sodium-native/binding.js\":{\"#package\":\"/node_modules/sodium-native/package.json\",\".\":{\"ios\":\"linked:sodium-native.5.0.10.framework/sodium-native.5.0.10\",\"android\":\"linked:libsodium-native.5.0.10.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.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/speedometer/index.js\":{\"#package\":\"/node_modules/speedometer/package.json\"},\"/node_modules/speedometer/package.json\":{},\"/node_modules/streamx/index.js\":{\"#package\":\"/node_modules/streamx/package.json\",\"events-universal\":\"/node_modules/events-universal/bare.js\",\"fast-fifo\":\"/node_modules/fast-fifo/index.js\",\"text-decoder\":\"/node_modules/text-decoder/index.js\"},\"/node_modules/streamx/package.json\":{},\"/node_modules/sub-encoder/index.js\":{\"#package\":\"/node_modules/sub-encoder/package.json\",\"b4a\":\"/node_modules/b4a/index.js\",\"codecs\":\"/node_modules/codecs/index.js\"},\"/node_modules/sub-encoder/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.19.2.framework/udx-native.1.19.2\",\"android\":\"linked:libudx-native.1.19.2.so\"},\"require-addon\":\"/node_modules/require-addon/lib/bare.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/unix-path-resolve/index.js\":{\"#package\":\"/node_modules/unix-path-resolve/package.json\"},\"/node_modules/unix-path-resolve/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/varint/decode.js\":{\"#package\":\"/node_modules/varint/package.json\"},\"/node_modules/varint/encode.js\":{\"#package\":\"/node_modules/varint/package.json\"},\"/node_modules/varint/index.js\":{\"#package\":\"/node_modules/varint/package.json\",\"./decode.js\":\"/node_modules/varint/decode.js\",\"./encode.js\":\"/node_modules/varint/encode.js\",\"./length.js\":\"/node_modules/varint/length.js\"},\"/node_modules/varint/length.js\":{\"#package\":\"/node_modules/varint/package.json\"},\"/node_modules/varint/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\":{}},\"addons\":[\"linked:bare-fs.4.5.4.framework/bare-fs.4.5.4\",\"linked:bare-os.3.6.2.framework/bare-os.3.6.2\",\"linked:bare-url.2.3.2.framework/bare-url.2.3.2\",\"linked:fs-native-extensions.1.4.5.framework/fs-native-extensions.1.4.5\",\"linked:libbare-fs.4.5.4.so\",\"linked:libbare-os.3.6.2.so\",\"linked:libbare-url.2.3.2.so\",\"linked:libfs-native-extensions.1.4.5.so\",\"linked:libquickbit-native.2.4.8.so\",\"linked:librocksdb-native.3.13.0.so\",\"linked:libsimdle-native.1.3.9.so\",\"linked:libsodium-native.5.0.10.so\",\"linked:libudx-native.1.19.2.so\",\"linked:quickbit-native.2.4.8.framework/quickbit-native.2.4.8\",\"linked:rocksdb-native.3.13.0.framework/rocksdb-native.3.13.0\",\"linked:simdle-native.1.3.9.framework/simdle-native.1.3.9\",\"linked:sodium-native.5.0.10.framework/sodium-native.5.0.10\",\"linked:udx-native.1.19.2.framework/udx-native.1.19.2\"],\"assets\":[],\"files\":{\"/lib/pear.js\":{\"offset\":0,\"length\":3774,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/index.js\":{\"offset\":3774,\"length\":16722,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/lib/bridge.js\":{\"offset\":20496,\"length\":1273,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/lib/handshake.js\":{\"offset\":21769,\"length\":1950,\"mode\":420},\"/node_modules/@hyperswarm/secret-stream/package.json\":{\"offset\":23719,\"length\":1132,\"mode\":420},\"/node_modules/adaptive-timeout/index.js\":{\"offset\":24851,\"length\":2424,\"mode\":420},\"/node_modules/adaptive-timeout/package.json\":{\"offset\":27275,\"length\":972,\"mode\":420},\"/node_modules/b4a/index.js\":{\"offset\":28247,\"length\":4054,\"mode\":420},\"/node_modules/b4a/package.json\":{\"offset\":32301,\"length\":1140,\"mode\":420},\"/node_modules/bare-events/index.js\":{\"offset\":33441,\"length\":8033,\"mode\":420},\"/node_modules/bare-events/lib/errors.js\":{\"offset\":41474,\"length\":671,\"mode\":420},\"/node_modules/bare-events/package.json\":{\"offset\":42145,\"length\":1399,\"mode\":420},\"/node_modules/bare-fs/binding.js\":{\"offset\":43544,\"length\":33,\"mode\":420},\"/node_modules/bare-fs/index.js\":{\"offset\":43577,\"length\":48349,\"mode\":420},\"/node_modules/bare-fs/lib/constants.js\":{\"offset\":91926,\"length\":1494,\"mode\":420},\"/node_modules/bare-fs/lib/errors.js\":{\"offset\":93420,\"length\":1163,\"mode\":420},\"/node_modules/bare-fs/package.json\":{\"offset\":94583,\"length\":1546,\"mode\":420},\"/node_modules/bare-fs/promises.js\":{\"offset\":96129,\"length\":1866,\"mode\":420},\"/node_modules/bare-os/binding.js\":{\"offset\":97995,\"length\":33,\"mode\":420},\"/node_modules/bare-os/index.js\":{\"offset\":98028,\"length\":2433,\"mode\":420},\"/node_modules/bare-os/lib/constants.js\":{\"offset\":100461,\"length\":113,\"mode\":420},\"/node_modules/bare-os/lib/errors.js\":{\"offset\":100574,\"length\":479,\"mode\":420},\"/node_modules/bare-os/package.json\":{\"offset\":101053,\"length\":1023,\"mode\":420},\"/node_modules/bare-path/index.js\":{\"offset\":102076,\"length\":306,\"mode\":420},\"/node_modules/bare-path/lib/constants.js\":{\"offset\":102382,\"length\":247,\"mode\":420},\"/node_modules/bare-path/lib/posix.js\":{\"offset\":102629,\"length\":5991,\"mode\":420},\"/node_modules/bare-path/lib/shared.js\":{\"offset\":108620,\"length\":1888,\"mode\":420},\"/node_modules/bare-path/lib/win32.js\":{\"offset\":110508,\"length\":13427,\"mode\":420},\"/node_modules/bare-path/package.json\":{\"offset\":123935,\"length\":796,\"mode\":420},\"/node_modules/bare-rpc/index.js\":{\"offset\":124731,\"length\":10741,\"mode\":420},\"/node_modules/bare-rpc/lib/command-router.js\":{\"offset\":135472,\"length\":1073,\"mode\":420},\"/node_modules/bare-rpc/lib/constants.js\":{\"offset\":136545,\"length\":270,\"mode\":420},\"/node_modules/bare-rpc/lib/errors.js\":{\"offset\":136815,\"length\":597,\"mode\":420},\"/node_modules/bare-rpc/lib/incoming-event.js\":{\"offset\":137412,\"length\":151,\"mode\":420},\"/node_modules/bare-rpc/lib/incoming-request.js\":{\"offset\":137563,\"length\":1241,\"mode\":420},\"/node_modules/bare-rpc/lib/incoming-stream.js\":{\"offset\":138804,\"length\":1263,\"mode\":420},\"/node_modules/bare-rpc/lib/messages.js\":{\"offset\":140067,\"length\":3934,\"mode\":420},\"/node_modules/bare-rpc/lib/outgoing-event.js\":{\"offset\":144001,\"length\":595,\"mode\":420},\"/node_modules/bare-rpc/lib/outgoing-request.js\":{\"offset\":144596,\"length\":1670,\"mode\":420},\"/node_modules/bare-rpc/lib/outgoing-stream.js\":{\"offset\":146266,\"length\":2374,\"mode\":420},\"/node_modules/bare-rpc/package.json\":{\"offset\":148640,\"length\":1207,\"mode\":420},\"/node_modules/bare-stream/index.js\":{\"offset\":149847,\"length\":7658,\"mode\":420},\"/node_modules/bare-stream/package.json\":{\"offset\":157505,\"length\":1307,\"mode\":420},\"/node_modules/bare-url/binding.js\":{\"offset\":158812,\"length\":33,\"mode\":420},\"/node_modules/bare-url/index.js\":{\"offset\":158845,\"length\":9261,\"mode\":420},\"/node_modules/bare-url/lib/errors.js\":{\"offset\":168106,\"length\":778,\"mode\":420},\"/node_modules/bare-url/lib/url-search-params.js\":{\"offset\":168884,\"length\":3867,\"mode\":420},\"/node_modules/bare-url/package.json\":{\"offset\":172751,\"length\":1105,\"mode\":420},\"/node_modules/big-sparse-array/index.js\":{\"offset\":173856,\"length\":2471,\"mode\":420},\"/node_modules/big-sparse-array/package.json\":{\"offset\":176327,\"length\":636,\"mode\":420},\"/node_modules/binary-stream-equals/index.js\":{\"offset\":176963,\"length\":2018,\"mode\":420},\"/node_modules/binary-stream-equals/package.json\":{\"offset\":178981,\"length\":687,\"mode\":420},\"/node_modules/bits-to-bytes/index.js\":{\"offset\":179668,\"length\":3083,\"mode\":420},\"/node_modules/bits-to-bytes/package.json\":{\"offset\":182751,\"length\":708,\"mode\":420},\"/node_modules/blind-relay/index.js\":{\"offset\":183459,\"length\":10851,\"mode\":420},\"/node_modules/blind-relay/lib/errors.js\":{\"offset\":194310,\"length\":1043,\"mode\":420},\"/node_modules/blind-relay/package.json\":{\"offset\":195353,\"length\":1046,\"mode\":420},\"/node_modules/bogon/index.js\":{\"offset\":196399,\"length\":4052,\"mode\":420},\"/node_modules/bogon/package.json\":{\"offset\":200451,\"length\":657,\"mode\":420},\"/node_modules/codecs/index.js\":{\"offset\":201108,\"length\":2416,\"mode\":420},\"/node_modules/codecs/package.json\":{\"offset\":203524,\"length\":655,\"mode\":420},\"/node_modules/compact-encoding-bitfield/index.js\":{\"offset\":204179,\"length\":1963,\"mode\":420},\"/node_modules/compact-encoding-bitfield/package.json\":{\"offset\":206142,\"length\":827,\"mode\":420},\"/node_modules/compact-encoding-net/index.js\":{\"offset\":206969,\"length\":4109,\"mode\":420},\"/node_modules/compact-encoding-net/package.json\":{\"offset\":211078,\"length\":734,\"mode\":420},\"/node_modules/compact-encoding/endian.js\":{\"offset\":211812,\"length\":105,\"mode\":420},\"/node_modules/compact-encoding/index.js\":{\"offset\":211917,\"length\":23634,\"mode\":420},\"/node_modules/compact-encoding/lexint.js\":{\"offset\":235551,\"length\":2851,\"mode\":420},\"/node_modules/compact-encoding/package.json\":{\"offset\":238402,\"length\":801,\"mode\":420},\"/node_modules/compact-encoding/raw.js\":{\"offset\":239203,\"length\":4093,\"mode\":420},\"/node_modules/corestore/index.js\":{\"offset\":243296,\"length\":17910,\"mode\":420},\"/node_modules/corestore/lib/audit.js\":{\"offset\":261206,\"length\":475,\"mode\":420},\"/node_modules/corestore/package.json\":{\"offset\":261681,\"length\":1219,\"mode\":420},\"/node_modules/debounceify/index.js\":{\"offset\":262900,\"length\":585,\"mode\":420},\"/node_modules/debounceify/package.json\":{\"offset\":263485,\"length\":567,\"mode\":420},\"/node_modules/device-file/index.js\":{\"offset\":264052,\"length\":6394,\"mode\":420},\"/node_modules/device-file/package.json\":{\"offset\":270446,\"length\":1190,\"mode\":420},\"/node_modules/dht-rpc/index.js\":{\"offset\":271636,\"length\":29145,\"mode\":420},\"/node_modules/dht-rpc/lib/commands.js\":{\"offset\":300781,\"length\":107,\"mode\":420},\"/node_modules/dht-rpc/lib/errors.js\":{\"offset\":300888,\"length\":719,\"mode\":420},\"/node_modules/dht-rpc/lib/health.js\":{\"offset\":301607,\"length\":2345,\"mode\":420},\"/node_modules/dht-rpc/lib/io.js\":{\"offset\":303952,\"length\":16605,\"mode\":420},\"/node_modules/dht-rpc/lib/peer.js\":{\"offset\":320557,\"length\":631,\"mode\":420},\"/node_modules/dht-rpc/lib/query.js\":{\"offset\":321188,\"length\":9899,\"mode\":420},\"/node_modules/dht-rpc/lib/session.js\":{\"offset\":331087,\"length\":1103,\"mode\":420},\"/node_modules/dht-rpc/package.json\":{\"offset\":332190,\"length\":1390,\"mode\":420},\"/node_modules/events-universal/bare.js\":{\"offset\":333580,\"length\":40,\"mode\":420},\"/node_modules/events-universal/package.json\":{\"offset\":333620,\"length\":908,\"mode\":420},\"/node_modules/fast-fifo/fixed-size.js\":{\"offset\":334528,\"length\":875,\"mode\":420},\"/node_modules/fast-fifo/index.js\":{\"offset\":335403,\"length\":972,\"mode\":420},\"/node_modules/fast-fifo/package.json\":{\"offset\":336375,\"length\":682,\"mode\":420},\"/node_modules/fd-lock/index.js\":{\"offset\":337057,\"length\":1661,\"mode\":420},\"/node_modules/fd-lock/package.json\":{\"offset\":338718,\"length\":1099,\"mode\":420},\"/node_modules/flat-tree/index.js\":{\"offset\":339817,\"length\":8164,\"mode\":420},\"/node_modules/flat-tree/package.json\":{\"offset\":347981,\"length\":631,\"mode\":420},\"/node_modules/fs-native-extensions/binding.js\":{\"offset\":348612,\"length\":90,\"mode\":420},\"/node_modules/fs-native-extensions/index.js\":{\"offset\":348702,\"length\":5718,\"mode\":420},\"/node_modules/fs-native-extensions/package.json\":{\"offset\":354420,\"length\":1699,\"mode\":420},\"/node_modules/hyperbee/index.js\":{\"offset\":356119,\"length\":46445,\"mode\":420},\"/node_modules/hyperbee/iterators/diff.js\":{\"offset\":402564,\"length\":5358,\"mode\":420},\"/node_modules/hyperbee/iterators/history.js\":{\"offset\":407922,\"length\":1631,\"mode\":420},\"/node_modules/hyperbee/iterators/local.js\":{\"offset\":409553,\"length\":1170,\"mode\":420},\"/node_modules/hyperbee/iterators/range.js\":{\"offset\":410723,\"length\":5869,\"mode\":420},\"/node_modules/hyperbee/lib/extension.js\":{\"offset\":416592,\"length\":3488,\"mode\":420},\"/node_modules/hyperbee/lib/messages.js\":{\"offset\":420080,\"length\":28162,\"mode\":420},\"/node_modules/hyperbee/package.json\":{\"offset\":448242,\"length\":1585,\"mode\":420},\"/node_modules/hyperblobs/index.js\":{\"offset\":449827,\"length\":5327,\"mode\":420},\"/node_modules/hyperblobs/lib/monitor.js\":{\"offset\":455154,\"length\":2393,\"mode\":420},\"/node_modules/hyperblobs/lib/prefetcher.js\":{\"offset\":457547,\"length\":1638,\"mode\":420},\"/node_modules/hyperblobs/lib/streams.js\":{\"offset\":459185,\"length\":3595,\"mode\":420},\"/node_modules/hyperblobs/package.json\":{\"offset\":462780,\"length\":1056,\"mode\":420},\"/node_modules/hypercore-crypto/index.js\":{\"offset\":463836,\"length\":5040,\"mode\":420},\"/node_modules/hypercore-crypto/package.json\":{\"offset\":468876,\"length\":771,\"mode\":420},\"/node_modules/hypercore-errors/index.js\":{\"offset\":469647,\"length\":4896,\"mode\":420},\"/node_modules/hypercore-errors/package.json\":{\"offset\":474543,\"length\":688,\"mode\":420},\"/node_modules/hypercore-id-encoding/index.js\":{\"offset\":475231,\"length\":923,\"mode\":420},\"/node_modules/hypercore-id-encoding/package.json\":{\"offset\":476154,\"length\":799,\"mode\":420},\"/node_modules/hypercore-storage/index.js\":{\"offset\":476953,\"length\":27551,\"mode\":420},\"/node_modules/hypercore-storage/lib/block-dependency-stream.js\":{\"offset\":504504,\"length\":2557,\"mode\":420},\"/node_modules/hypercore-storage/lib/close-error-stream.js\":{\"offset\":507061,\"length\":276,\"mode\":420},\"/node_modules/hypercore-storage/lib/keys.js\":{\"offset\":507337,\"length\":7454,\"mode\":420},\"/node_modules/hypercore-storage/lib/streams.js\":{\"offset\":514791,\"length\":4820,\"mode\":420},\"/node_modules/hypercore-storage/lib/tx.js\":{\"offset\":519611,\"length\":8416,\"mode\":420},\"/node_modules/hypercore-storage/lib/view.js\":{\"offset\":528027,\"length\":8378,\"mode\":420},\"/node_modules/hypercore-storage/migrations/0/index.js\":{\"offset\":536405,\"length\":20125,\"mode\":420},\"/node_modules/hypercore-storage/migrations/0/messages.js\":{\"offset\":556530,\"length\":26210,\"mode\":420},\"/node_modules/hypercore-storage/package.json\":{\"offset\":582740,\"length\":1248,\"mode\":420},\"/node_modules/hypercore-storage/spec/hyperschema/index.js\":{\"offset\":583988,\"length\":13108,\"mode\":420},\"/node_modules/hypercore/index.js\":{\"offset\":597096,\"length\":34157,\"mode\":420},\"/node_modules/hypercore/lib/audit.js\":{\"offset\":631253,\"length\":3834,\"mode\":420},\"/node_modules/hypercore/lib/bit-interlude.js\":{\"offset\":635087,\"length\":4257,\"mode\":420},\"/node_modules/hypercore/lib/bitfield.js\":{\"offset\":639344,\"length\":12115,\"mode\":420},\"/node_modules/hypercore/lib/caps.js\":{\"offset\":651459,\"length\":1537,\"mode\":420},\"/node_modules/hypercore/lib/compat.js\":{\"offset\":652996,\"length\":477,\"mode\":420},\"/node_modules/hypercore/lib/copy-prologue.js\":{\"offset\":653473,\"length\":5999,\"mode\":420},\"/node_modules/hypercore/lib/core.js\":{\"offset\":659472,\"length\":24419,\"mode\":420},\"/node_modules/hypercore/lib/default-encryption.js\":{\"offset\":683891,\"length\":3453,\"mode\":420},\"/node_modules/hypercore/lib/download.js\":{\"offset\":687344,\"length\":1278,\"mode\":420},\"/node_modules/hypercore/lib/hotswap-queue.js\":{\"offset\":688622,\"length\":1498,\"mode\":420},\"/node_modules/hypercore/lib/info.js\":{\"offset\":690120,\"length\":1517,\"mode\":420},\"/node_modules/hypercore/lib/inspect.js\":{\"offset\":691637,\"length\":1983,\"mode\":420},\"/node_modules/hypercore/lib/merkle-tree.js\":{\"offset\":693620,\"length\":33977,\"mode\":420},\"/node_modules/hypercore/lib/messages.js\":{\"offset\":727597,\"length\":28353,\"mode\":420},\"/node_modules/hypercore/lib/multisig.js\":{\"offset\":755950,\"length\":2851,\"mode\":420},\"/node_modules/hypercore/lib/mutex.js\":{\"offset\":758801,\"length\":1028,\"mode\":420},\"/node_modules/hypercore/lib/receiver-queue.js\":{\"offset\":759829,\"length\":1413,\"mode\":420},\"/node_modules/hypercore/lib/remote-bitfield.js\":{\"offset\":761242,\"length\":8124,\"mode\":420},\"/node_modules/hypercore/lib/replicator.js\":{\"offset\":769366,\"length\":86085,\"mode\":420},\"/node_modules/hypercore/lib/session-state.js\":{\"offset\":855451,\"length\":32225,\"mode\":420},\"/node_modules/hypercore/lib/streams.js\":{\"offset\":887676,\"length\":3034,\"mode\":420},\"/node_modules/hypercore/lib/verifier.js\":{\"offset\":890710,\"length\":9678,\"mode\":420},\"/node_modules/hypercore/lib/wants.js\":{\"offset\":900388,\"length\":7031,\"mode\":420},\"/node_modules/hypercore/package.json\":{\"offset\":907419,\"length\":2197,\"mode\":420},\"/node_modules/hyperdht/index.js\":{\"offset\":909616,\"length\":16291,\"mode\":420},\"/node_modules/hyperdht/lib/announcer.js\":{\"offset\":925907,\"length\":7533,\"mode\":420},\"/node_modules/hyperdht/lib/connect.js\":{\"offset\":933440,\"length\":23669,\"mode\":420},\"/node_modules/hyperdht/lib/connection-pool.js\":{\"offset\":957109,\"length\":3050,\"mode\":420},\"/node_modules/hyperdht/lib/constants.js\":{\"offset\":960159,\"length\":1211,\"mode\":420},\"/node_modules/hyperdht/lib/crypto.js\":{\"offset\":961370,\"length\":632,\"mode\":420},\"/node_modules/hyperdht/lib/encode.js\":{\"offset\":962002,\"length\":446,\"mode\":420},\"/node_modules/hyperdht/lib/errors.js\":{\"offset\":962448,\"length\":3712,\"mode\":420},\"/node_modules/hyperdht/lib/holepuncher.js\":{\"offset\":966160,\"length\":10003,\"mode\":420},\"/node_modules/hyperdht/lib/messages.js\":{\"offset\":976163,\"length\":11266,\"mode\":420},\"/node_modules/hyperdht/lib/nat.js\":{\"offset\":987429,\"length\":4714,\"mode\":420},\"/node_modules/hyperdht/lib/noise-wrap.js\":{\"offset\":992143,\"length\":1669,\"mode\":420},\"/node_modules/hyperdht/lib/persistent.js\":{\"offset\":993812,\"length\":8086,\"mode\":420},\"/node_modules/hyperdht/lib/raw-stream-set.js\":{\"offset\":1001898,\"length\":1511,\"mode\":420},\"/node_modules/hyperdht/lib/router.js\":{\"offset\":1003409,\"length\":6978,\"mode\":420},\"/node_modules/hyperdht/lib/secure-payload.js\":{\"offset\":1010387,\"length\":1491,\"mode\":420},\"/node_modules/hyperdht/lib/semaphore.js\":{\"offset\":1011878,\"length\":1577,\"mode\":420},\"/node_modules/hyperdht/lib/server.js\":{\"offset\":1013455,\"length\":21156,\"mode\":420},\"/node_modules/hyperdht/lib/sleeper.js\":{\"offset\":1034611,\"length\":690,\"mode\":420},\"/node_modules/hyperdht/lib/socket-pool.js\":{\"offset\":1035301,\"length\":4343,\"mode\":420},\"/node_modules/hyperdht/package.json\":{\"offset\":1039644,\"length\":2011,\"mode\":420},\"/node_modules/hyperdrive/index.js\":{\"offset\":1041655,\"length\":18796,\"mode\":420},\"/node_modules/hyperdrive/lib/download.js\":{\"offset\":1060451,\"length\":1784,\"mode\":420},\"/node_modules/hyperdrive/lib/monitor.js\":{\"offset\":1062235,\"length\":4334,\"mode\":420},\"/node_modules/hyperdrive/package.json\":{\"offset\":1066569,\"length\":1760,\"mode\":420},\"/node_modules/hyperschema/package.json\":{\"offset\":1068329,\"length\":1556,\"mode\":420},\"/node_modules/hyperschema/runtime.cjs\":{\"offset\":1069885,\"length\":54,\"mode\":420},\"/node_modules/hyperswarm/index.js\":{\"offset\":1069939,\"length\":18429,\"mode\":420},\"/node_modules/hyperswarm/lib/bulk-timer.js\":{\"offset\":1088368,\"length\":728,\"mode\":420},\"/node_modules/hyperswarm/lib/connection-set.js\":{\"offset\":1089096,\"length\":823,\"mode\":420},\"/node_modules/hyperswarm/lib/peer-discovery.js\":{\"offset\":1089919,\"length\":9286,\"mode\":420},\"/node_modules/hyperswarm/lib/peer-info.js\":{\"offset\":1099205,\"length\":2688,\"mode\":420},\"/node_modules/hyperswarm/lib/retry-timer.js\":{\"offset\":1101893,\"length\":1889,\"mode\":420},\"/node_modules/hyperswarm/package.json\":{\"offset\":1103782,\"length\":1228,\"mode\":420},\"/node_modules/index-encoder/index.js\":{\"offset\":1105010,\"length\":6762,\"mode\":420},\"/node_modules/index-encoder/package.json\":{\"offset\":1111772,\"length\":729,\"mode\":420},\"/node_modules/is-options/index.js\":{\"offset\":1112501,\"length\":140,\"mode\":420},\"/node_modules/is-options/package.json\":{\"offset\":1112641,\"length\":605,\"mode\":420},\"/node_modules/kademlia-routing-table/index.js\":{\"offset\":1113246,\"length\":4145,\"mode\":420},\"/node_modules/kademlia-routing-table/package.json\":{\"offset\":1117391,\"length\":967,\"mode\":420},\"/node_modules/localdrive/index.js\":{\"offset\":1118358,\"length\":9204,\"mode\":420},\"/node_modules/localdrive/package.json\":{\"offset\":1127562,\"length\":1138,\"mode\":420},\"/node_modules/localdrive/streams.js\":{\"offset\":1128700,\"length\":5155,\"mode\":420},\"/node_modules/mirror-drive/index.js\":{\"offset\":1133855,\"length\":11229,\"mode\":420},\"/node_modules/mirror-drive/package.json\":{\"offset\":1145084,\"length\":1468,\"mode\":420},\"/node_modules/mutexify/index.js\":{\"offset\":1146552,\"length\":536,\"mode\":420},\"/node_modules/mutexify/package.json\":{\"offset\":1147088,\"length\":679,\"mode\":420},\"/node_modules/mutexify/promise.js\":{\"offset\":1147767,\"length\":324,\"mode\":420},\"/node_modules/nanoassert/index.js\":{\"offset\":1148091,\"length\":438,\"mode\":420},\"/node_modules/nanoassert/package.json\":{\"offset\":1148529,\"length\":647,\"mode\":420},\"/node_modules/nat-sampler/index.js\":{\"offset\":1149176,\"length\":1550,\"mode\":420},\"/node_modules/nat-sampler/package.json\":{\"offset\":1150726,\"length\":608,\"mode\":420},\"/node_modules/noise-curve-ed/index.js\":{\"offset\":1151334,\"length\":1642,\"mode\":420},\"/node_modules/noise-curve-ed/package.json\":{\"offset\":1152976,\"length\":808,\"mode\":420},\"/node_modules/noise-handshake/cipher.js\":{\"offset\":1153784,\"length\":2817,\"mode\":420},\"/node_modules/noise-handshake/dh.js\":{\"offset\":1156601,\"length\":1439,\"mode\":420},\"/node_modules/noise-handshake/hkdf.js\":{\"offset\":1158040,\"length\":1092,\"mode\":420},\"/node_modules/noise-handshake/hmac.js\":{\"offset\":1159132,\"length\":1273,\"mode\":420},\"/node_modules/noise-handshake/noise.js\":{\"offset\":1160405,\"length\":6437,\"mode\":420},\"/node_modules/noise-handshake/package.json\":{\"offset\":1166842,\"length\":638,\"mode\":420},\"/node_modules/noise-handshake/symmetric-state.js\":{\"offset\":1167480,\"length\":2206,\"mode\":420},\"/node_modules/pear-aliases/index.js\":{\"offset\":1169686,\"length\":953,\"mode\":420},\"/node_modules/pear-aliases/package.json\":{\"offset\":1170639,\"length\":755,\"mode\":420},\"/node_modules/pear-errors/index.js\":{\"offset\":1171394,\"length\":3811,\"mode\":420},\"/node_modules/pear-errors/package.json\":{\"offset\":1175205,\"length\":427,\"mode\":420},\"/node_modules/pear-link/index.js\":{\"offset\":1175632,\"length\":4986,\"mode\":420},\"/node_modules/pear-link/package.json\":{\"offset\":1180618,\"length\":1068,\"mode\":420},\"/node_modules/protocol-buffers-encodings/index.js\":{\"offset\":1181686,\"length\":6489,\"mode\":420},\"/node_modules/protocol-buffers-encodings/package.json\":{\"offset\":1188175,\"length\":719,\"mode\":420},\"/node_modules/protomux/index.js\":{\"offset\":1188894,\"length\":18996,\"mode\":420},\"/node_modules/protomux/package.json\":{\"offset\":1207890,\"length\":816,\"mode\":420},\"/node_modules/queue-tick/package.json\":{\"offset\":1208706,\"length\":669,\"mode\":420},\"/node_modules/queue-tick/process-next-tick.js\":{\"offset\":1209375,\"length\":160,\"mode\":420},\"/node_modules/queue-tick/queue-microtask.js\":{\"offset\":1209535,\"length\":108,\"mode\":420},\"/node_modules/quickbit-native/binding.js\":{\"offset\":1209643,\"length\":90,\"mode\":420},\"/node_modules/quickbit-native/index.js\":{\"offset\":1209733,\"length\":4683,\"mode\":420},\"/node_modules/quickbit-native/package.json\":{\"offset\":1214416,\"length\":1104,\"mode\":420},\"/node_modules/quickbit-universal/fallback.js\":{\"offset\":1215520,\"length\":10204,\"mode\":420},\"/node_modules/quickbit-universal/index.js\":{\"offset\":1225724,\"length\":442,\"mode\":420},\"/node_modules/quickbit-universal/package.json\":{\"offset\":1226166,\"length\":908,\"mode\":420},\"/node_modules/rache/index.js\":{\"offset\":1227074,\"length\":2463,\"mode\":420},\"/node_modules/rache/package.json\":{\"offset\":1229537,\"length\":602,\"mode\":420},\"/node_modules/random-array-iterator/index.js\":{\"offset\":1230139,\"length\":1001,\"mode\":420},\"/node_modules/random-array-iterator/package.json\":{\"offset\":1231140,\"length\":700,\"mode\":420},\"/node_modules/ready-resource/index.js\":{\"offset\":1231840,\"length\":1091,\"mode\":420},\"/node_modules/ready-resource/package.json\":{\"offset\":1232931,\"length\":812,\"mode\":420},\"/node_modules/record-cache/index.js\":{\"offset\":1233743,\"length\":3668,\"mode\":420},\"/node_modules/record-cache/package.json\":{\"offset\":1237411,\"length\":612,\"mode\":420},\"/node_modules/refcounter/index.js\":{\"offset\":1238023,\"length\":611,\"mode\":420},\"/node_modules/refcounter/package.json\":{\"offset\":1238634,\"length\":451,\"mode\":420},\"/node_modules/require-addon/lib/bare.js\":{\"offset\":1239085,\"length\":45,\"mode\":420},\"/node_modules/require-addon/package.json\":{\"offset\":1239130,\"length\":1449,\"mode\":420},\"/node_modules/resolve-reject-promise/index.js\":{\"offset\":1240579,\"length\":443,\"mode\":420},\"/node_modules/resolve-reject-promise/package.json\":{\"offset\":1241022,\"length\":581,\"mode\":420},\"/node_modules/resource-on-exit/index.js\":{\"offset\":1241603,\"length\":361,\"mode\":420},\"/node_modules/resource-on-exit/package.json\":{\"offset\":1241964,\"length\":496,\"mode\":420},\"/node_modules/rocksdb-native/binding.js\":{\"offset\":1242460,\"length\":90,\"mode\":420},\"/node_modules/rocksdb-native/index.js\":{\"offset\":1242550,\"length\":4867,\"mode\":420},\"/node_modules/rocksdb-native/lib/batch.js\":{\"offset\":1247417,\"length\":8895,\"mode\":420},\"/node_modules/rocksdb-native/lib/column-family.js\":{\"offset\":1256312,\"length\":2849,\"mode\":420},\"/node_modules/rocksdb-native/lib/constants.js\":{\"offset\":1259161,\"length\":248,\"mode\":420},\"/node_modules/rocksdb-native/lib/filter-policy.js\":{\"offset\":1259409,\"length\":437,\"mode\":420},\"/node_modules/rocksdb-native/lib/iterator.js\":{\"offset\":1259846,\"length\":4311,\"mode\":420},\"/node_modules/rocksdb-native/lib/snapshot.js\":{\"offset\":1264157,\"length\":503,\"mode\":420},\"/node_modules/rocksdb-native/lib/state.js\":{\"offset\":1264660,\"length\":10125,\"mode\":420},\"/node_modules/rocksdb-native/package.json\":{\"offset\":1274785,\"length\":1827,\"mode\":420},\"/node_modules/safety-catch/index.js\":{\"offset\":1276612,\"length\":506,\"mode\":420},\"/node_modules/safety-catch/package.json\":{\"offset\":1277118,\"length\":547,\"mode\":420},\"/node_modules/same-data/index.js\":{\"offset\":1277665,\"length\":1126,\"mode\":420},\"/node_modules/same-data/package.json\":{\"offset\":1278791,\"length\":629,\"mode\":420},\"/node_modules/scope-lock/index.js\":{\"offset\":1279420,\"length\":1821,\"mode\":420},\"/node_modules/scope-lock/package.json\":{\"offset\":1281241,\"length\":611,\"mode\":420},\"/node_modules/shuffled-priority-queue/index.js\":{\"offset\":1281852,\"length\":2607,\"mode\":420},\"/node_modules/shuffled-priority-queue/package.json\":{\"offset\":1284459,\"length\":691,\"mode\":420},\"/node_modules/signal-promise/index.js\":{\"offset\":1285150,\"length\":1252,\"mode\":420},\"/node_modules/signal-promise/package.json\":{\"offset\":1286402,\"length\":503,\"mode\":420},\"/node_modules/signed-varint/index.js\":{\"offset\":1286905,\"length\":435,\"mode\":420},\"/node_modules/signed-varint/package.json\":{\"offset\":1287340,\"length\":523,\"mode\":420},\"/node_modules/simdle-native/binding.js\":{\"offset\":1287863,\"length\":90,\"mode\":420},\"/node_modules/simdle-native/index.js\":{\"offset\":1287953,\"length\":3474,\"mode\":420},\"/node_modules/simdle-native/package.json\":{\"offset\":1291427,\"length\":1115,\"mode\":420},\"/node_modules/simdle-universal/fallback.js\":{\"offset\":1292542,\"length\":5137,\"mode\":420},\"/node_modules/simdle-universal/index.js\":{\"offset\":1297679,\"length\":103,\"mode\":420},\"/node_modules/simdle-universal/package.json\":{\"offset\":1297782,\"length\":879,\"mode\":420},\"/node_modules/simdle-universal/scalar.js\":{\"offset\":1298661,\"length\":469,\"mode\":420},\"/node_modules/sodium-native/binding.js\":{\"offset\":1299130,\"length\":89,\"mode\":420},\"/node_modules/sodium-native/index.js\":{\"offset\":1299219,\"length\":66328,\"mode\":420},\"/node_modules/sodium-native/package.json\":{\"offset\":1365547,\"length\":1356,\"mode\":420},\"/node_modules/sodium-secretstream/index.js\":{\"offset\":1366903,\"length\":2257,\"mode\":420},\"/node_modules/sodium-secretstream/package.json\":{\"offset\":1369160,\"length\":657,\"mode\":420},\"/node_modules/sodium-universal/index.js\":{\"offset\":1369817,\"length\":42,\"mode\":420},\"/node_modules/sodium-universal/package.json\":{\"offset\":1369859,\"length\":1217,\"mode\":420},\"/node_modules/speedometer/index.js\":{\"offset\":1371076,\"length\":906,\"mode\":420},\"/node_modules/speedometer/package.json\":{\"offset\":1371982,\"length\":333,\"mode\":420},\"/node_modules/streamx/index.js\":{\"offset\":1372315,\"length\":33350,\"mode\":420},\"/node_modules/streamx/package.json\":{\"offset\":1405665,\"length\":836,\"mode\":420},\"/node_modules/sub-encoder/index.js\":{\"offset\":1406501,\"length\":1968,\"mode\":420},\"/node_modules/sub-encoder/package.json\":{\"offset\":1408469,\"length\":908,\"mode\":420},\"/node_modules/text-decoder/index.js\":{\"offset\":1409377,\"length\":1371,\"mode\":420},\"/node_modules/text-decoder/lib/pass-through-decoder.js\":{\"offset\":1410748,\"length\":269,\"mode\":420},\"/node_modules/text-decoder/lib/utf8-decoder.js\":{\"offset\":1411017,\"length\":2599,\"mode\":420},\"/node_modules/text-decoder/package.json\":{\"offset\":1413616,\"length\":1224,\"mode\":420},\"/node_modules/time-ordered-set/index.js\":{\"offset\":1414840,\"length\":1444,\"mode\":420},\"/node_modules/time-ordered-set/package.json\":{\"offset\":1416284,\"length\":666,\"mode\":420},\"/node_modules/timeout-refresh/browser.js\":{\"offset\":1416950,\"length\":1098,\"mode\":420},\"/node_modules/timeout-refresh/index.js\":{\"offset\":1418048,\"length\":184,\"mode\":420},\"/node_modules/timeout-refresh/node.js\":{\"offset\":1418232,\"length\":928,\"mode\":420},\"/node_modules/timeout-refresh/package.json\":{\"offset\":1419160,\"length\":619,\"mode\":420},\"/node_modules/udx-native/binding.js\":{\"offset\":1419779,\"length\":90,\"mode\":420},\"/node_modules/udx-native/lib/ip.js\":{\"offset\":1419869,\"length\":2204,\"mode\":420},\"/node_modules/udx-native/lib/network-interfaces.js\":{\"offset\":1422073,\"length\":1341,\"mode\":420},\"/node_modules/udx-native/lib/socket.js\":{\"offset\":1423414,\"length\":7557,\"mode\":420},\"/node_modules/udx-native/lib/stream.js\":{\"offset\":1430971,\"length\":12706,\"mode\":420},\"/node_modules/udx-native/lib/udx.js\":{\"offset\":1443677,\"length\":2868,\"mode\":420},\"/node_modules/udx-native/package.json\":{\"offset\":1446545,\"length\":1615,\"mode\":420},\"/node_modules/unix-path-resolve/index.js\":{\"offset\":1448160,\"length\":1356,\"mode\":420},\"/node_modules/unix-path-resolve/package.json\":{\"offset\":1449516,\"length\":651,\"mode\":420},\"/node_modules/unordered-set/index.js\":{\"offset\":1450167,\"length\":677,\"mode\":420},\"/node_modules/unordered-set/package.json\":{\"offset\":1450844,\"length\":654,\"mode\":420},\"/node_modules/unslab/index.js\":{\"offset\":1451498,\"length\":913,\"mode\":420},\"/node_modules/unslab/package.json\":{\"offset\":1452411,\"length\":613,\"mode\":420},\"/node_modules/varint/decode.js\":{\"offset\":1453024,\"length\":508,\"mode\":420},\"/node_modules/varint/encode.js\":{\"offset\":1453532,\"length\":452,\"mode\":420},\"/node_modules/varint/index.js\":{\"offset\":1453984,\"length\":134,\"mode\":420},\"/node_modules/varint/length.js\":{\"offset\":1454118,\"length\":471,\"mode\":420},\"/node_modules/varint/package.json\":{\"offset\":1454589,\"length\":511,\"mode\":420},\"/node_modules/which-runtime/index.js\":{\"offset\":1455100,\"length\":1231,\"mode\":420},\"/node_modules/which-runtime/package.json\":{\"offset\":1456331,\"length\":602,\"mode\":420},\"/node_modules/xache/index.js\":{\"offset\":1456933,\"length\":2378,\"mode\":420},\"/node_modules/xache/package.json\":{\"offset\":1459311,\"length\":584,\"mode\":420},\"/node_modules/z32/index.js\":{\"offset\":1459895,\"length\":2654,\"mode\":420},\"/node_modules/z32/package.json\":{\"offset\":1462549,\"length\":701,\"mode\":420},\"/package.json\":{\"offset\":1463250,\"length\":1649,\"mode\":420}}}\n/* global BareKit */\n\nconst Hyperswarm = require('hyperswarm')\nconst Hyperdrive = require('hyperdrive')\nconst Corestore = require('corestore')\nconst path = require('bare-path')\nconst ReadyResource = require('ready-resource')\nconst RPC = require('bare-rpc')\nconst plink = require('pear-link')\nconst hid = require('hypercore-id-encoding')\nconst Localdrive = require('localdrive')\n\nclass PearRuntime extends ReadyResource {\n constructor(config) {\n super()\n const { drive: upgrade } = plink.parse(config.upgrade)\n this.dir = config.dir\n this.version = config.version || 0\n this.app = config.app\n this.name = this.app && path.basename(this.app)\n this.key = hid.decode(upgrade.key)\n this.length = upgrade.length || 0\n this.fork = upgrade.fork || 0\n this.link = plink.serialize({ drive: { fork: this.fork, length: this.length, key: this.key }})\n this.bundled = config.bundled || !!this.app\n this.isDev = config.isDev\n this.store = config.store || new Corestore(path.join(this.dir, 'corestore'))\n this.drive = new Hyperdrive(this.store, this.key)\n this.swarm = config.swarm || null\n this.next = null\n this.checkout = null\n\n this.updating = false\n this.updated = false\n this.applied = false\n\n this.ready().catch(noop)\n }\n\n run(entrypoint, args = [], opts = {}) {\n return new Sidecar(entrypoint, args, opts)\n }\n\n async _open() {\n await this.drive.ready()\n log(`this.bundles is ${this.bundled}`)\n if (true) {\n // await fs.rm(path.join(this.dir, 'upgrade'), { recursive: true, force: true })\n // await fs.mkdir(path.join(this.dir, 'upgrade'))\n log('removed bundle')\n if (!this.swarm) {\n const keyPair = await this.store.createKeyPair('pear-container')\n this.swarm = new Hyperswarm({ keyPair })\n }\n\n this.swarm.on('connection', (connection) => this.store.replicate(connection))\n this.swarm.join(this.drive.core.discoveryKey, { client: true, server: false })\n\n this._updateBackground()\n this.drive.core.on('append', () => this._updateBackground())\n }\n }\n\n async _close() {\n await this.drive.close()\n if (this.checkout) await this.checkout.close()\n await this.store.destroy()\n await this.swarm.destroy()\n }\n\n _updateBackground() {\n this._update().catch(noop)\n }\n\n async _update() {\n if (this.updating) return\n this.updating = true\n\n const length = this.drive.core.length\n const id = length + '.' + this.drive.core.fork\n const next = path.join(this.dir, 'upgrade')\n const co = this.drive.checkout(length)\n\n this.checkout = co\n\n const manifest = await co.get('/package.json')\n const version = manifest && JSON.parse(manifest).version\n log(`manifest version: ${version} ... current version: ${this.version}`)\n if (!manifest || version === this.version) {\n this.updating = false\n this.checkout = null\n await co.close()\n return\n }\n\n const local = new Localdrive(next)\n\n this.emit('updating')\n\n for await (const data of co.mirror(local)) {\n this.emit('updating-delta', data)\n }\n\n await co.close()\n await local.close()\n\n this.checkout = null\n this.length = length\n this.next = next\n\n this.updating = false\n this.updated = true\n this.emit('updated', version)\n\n if (this.drive.core.length > length) this._updateBackground()\n }\n}\n\nfunction noop() {}\n\nconst rpc = new RPC(BareKit.IPC, (req) => {})\n\nconst config = JSON.parse(Bare.argv[0])\nconst app = new PearRuntime(config)\napp.on('updated', (version) => {\n const req = rpc.request(0)\n req.send(version)\n})\n\napp.on('updating-delta', (data) =>{\n const req = rpc.request(1)\n req.send(JSON.stringify(data))\n})\n\nfunction log (string){\n const req = rpc.request(2)\n req.send(string)\n}\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) => {\n openedDone = resolve\n })\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)\n 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 ? [\n [NS_INITIATOR, NS_SEND],\n [NS_RESPONDER, NS_SEND]\n ]\n : [\n [NS_RESPONDER, NS_SEND],\n [NS_INITIATOR, NS_SEND]\n ]\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(\n new Error(\n 'Message is too large for an atomic write. Max size is ' + MAX_ATOMIC_WRITE + ' bytes.'\n )\n )\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.9.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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"udx-native\": \"^1.13.2\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check .\",\n \"test\": \"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}\nconst Cache = require('xache')\n\nmodule.exports = class AdaptiveTimeout {\n constructor(opts = {}) {\n this._cache = new Cache({\n maxSize: opts.maxSize || 65536,\n maxAge: opts.maxAge || 10 * 60 * 1000 // 10 minutes\n })\n this._fallback = opts.fallback || AdaptiveTimeout.TimeoutExponential\n this._min = opts.min ?? 300\n this._max = opts.max || 4000\n this._jitter = opts.jitter ?? 256\n }\n\n // Default - aggressive ramp\n static TimeoutAggressive = [500, 750, 1000, 1500, 2000]\n\n // Linear - steady increase\n static TimeoutLinear = [500, 1000, 1500, 2000, 2500]\n\n // Exponential - slow start, rapid backoff\n static TimeoutExponential = [250, 500, 1000, 2000, 4000]\n\n // Gentle - conservative, patient\n static TimeoutGentle = [1000, 1250, 1500, 1750, 2000]\n\n // Fast - rapid fire retries\n static TimeoutFast = [200, 400, 600, 800, 1000]\n\n // U-shape - long, short, long\n static TimeoutUShape = [1500, 750, 500, 750, 1500]\n\n // Inverse U - short, long, short\n static TimeoutInverseU = [500, 1000, 1500, 1000, 500]\n\n // Sawtooth - alternating fast/slow\n static TimeoutSawtooth = [500, 1500, 500, 1500, 500]\n\n // Plateau - quick ramp then steady\n static TimeoutPlateau = [500, 1000, 2000, 2000, 2000]\n\n // Logarithmic - diminishing increases\n static TimeoutLogarithmic = [500, 1000, 1300, 1500, 1600]\n\n getValue(key) {\n return this._cache.get(key)\n }\n\n put(key, value) {\n let p = this._cache.get(key)\n\n if (!p) {\n p = { avg: value, variance: value >> 1 }\n } else {\n // blend 75% old + 25% new deviation\n p.variance += (Math.abs(p.avg - value) - p.variance) >> 2\n // blend 87.5% old + 12.5% new sample\n p.avg += (value - p.avg) >> 3\n }\n\n this._cache.set(key, p)\n\n return p\n }\n\n get(key, attempt = 1) {\n const p = this._cache.get(key)\n const jitter = (Math.random() * this._jitter) | 0\n\n if (p) {\n // known - use adaptive with linear backoff\n const base = p.avg + (p.variance << 1)\n const backoff = base * attempt\n return Math.min(Math.max(backoff + jitter, this._min), this._max)\n } else {\n // unknown - use aggressive fallback\n const base = this._fallback[Math.min(attempt - 1, this._fallback.length - 1)]\n return base + jitter\n }\n }\n\n has(key) {\n return this._cache.has(key)\n }\n\n delete(key) {\n return this._cache.delete(key)\n }\n\n clear() {\n this._cache.clear()\n }\n}\n{\n \"name\": \"adaptive-timeout\",\n \"version\": \"1.0.1\",\n \"description\": \"adaptive-timeout\",\n \"main\": \"index.js\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n }\n },\n \"files\": [\n \"package.json\",\n \"index.js\",\n \"index.d.ts\"\n ],\n \"devDependencies\": {\n \"brittle\": \"^3.19.0\",\n \"lunte\": \"^1.2.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier . --write\",\n \"test\": \"prettier . --check && lunte && brittle-bare test/index.js\",\n \"lint\": \"lunte\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/adaptive-timeout.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/adaptive-timeout/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/adaptive-timeout\",\n \"dependencies\": {\n \"xache\": \"^1.2.1\"\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 readDoubleBE(buffer, offset) {\n return toBuffer(buffer).readDoubleBE(offset)\n}\n\nfunction readDoubleLE(buffer, offset) {\n return toBuffer(buffer).readDoubleLE(offset)\n}\n\nfunction readFloatBE(buffer, offset) {\n return toBuffer(buffer).readFloatBE(offset)\n}\n\nfunction readFloatLE(buffer, offset) {\n return toBuffer(buffer).readFloatLE(offset)\n}\n\nfunction readInt32BE(buffer, offset) {\n return toBuffer(buffer).readInt32BE(offset)\n}\n\nfunction readInt32LE(buffer, offset) {\n return toBuffer(buffer).readInt32LE(offset)\n}\n\nfunction readUInt32BE(buffer, offset) {\n return toBuffer(buffer).readUInt32BE(offset)\n}\n\nfunction readUInt32LE(buffer, offset) {\n return toBuffer(buffer).readUInt32LE(offset)\n}\n\nfunction writeDoubleBE(buffer, value, offset) {\n return toBuffer(buffer).writeDoubleBE(value, offset)\n}\n\nfunction writeDoubleLE(buffer, value, offset) {\n return toBuffer(buffer).writeDoubleLE(value, offset)\n}\n\nfunction writeFloatBE(buffer, value, offset) {\n return toBuffer(buffer).writeFloatBE(value, offset)\n}\n\nfunction writeFloatLE(buffer, value, offset) {\n return toBuffer(buffer).writeFloatLE(value, offset)\n}\n\nfunction writeInt32BE(buffer, value, offset) {\n return toBuffer(buffer).writeInt32BE(value, offset)\n}\n\nfunction writeInt32LE(buffer, value, offset) {\n return toBuffer(buffer).writeInt32LE(value, offset)\n}\n\nfunction writeUInt32BE(buffer, value, offset) {\n return toBuffer(buffer).writeUInt32BE(value, offset)\n}\n\nfunction writeUInt32LE(buffer, value, offset) {\n return toBuffer(buffer).writeUInt32LE(value, 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 readDoubleBE,\n readDoubleLE,\n readFloatBE,\n readFloatLE,\n readInt32BE,\n readInt32LE,\n readUInt32BE,\n readUInt32LE,\n writeDoubleBE,\n writeDoubleLE,\n writeFloatBE,\n writeFloatLE,\n writeInt32BE,\n writeInt32LE,\n writeUInt32BE,\n writeUInt32LE\n}\n{\n \"name\": \"b4a\",\n \"version\": \"1.7.4\",\n \"description\": \"Bridging the gap between buffers and typed arrays\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"react-native\": \"./react-native.js\",\n \"browser\": \"./browser.js\",\n \"default\": \"./index.js\"\n }\n },\n \"files\": [\n \"browser.js\",\n \"index.js\",\n \"react-native.js\",\n \"lib\"\n ],\n \"scripts\": {\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:bare\": \"bare test.mjs\",\n \"test:node\": \"node test.mjs\",\n \"lint\": \"prettier . --check\"\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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\n },\n \"peerDependencies\": {\n \"react-native-b4a\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"react-native-b4a\": {\n \"optional\": true\n }\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 Reflect.apply(l[0], ctx, args)\n }\n\n return list.length > 0\n }\n}\n\nfunction appendListener(ctx, name, fn, once) {\n if (ctx._events === undefined) ctx._events = Object.create(null)\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 if (ctx._events === undefined) ctx._events = Object.create(null)\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 if (ctx._events === undefined) return ctx\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 !== undefined && this._events.error === undefined) {\n throwUnhandledError(...args)\n }\n\n if (this._events === undefined) return false\n const e = this._events[name]\n return e === undefined ? false : e.emit(this, name, ...args)\n }\n\n listeners(name) {\n if (this._events === undefined) return []\n const e = this._events[name]\n return e === undefined ? [] : [...e.list]\n }\n\n listenerCount(name) {\n if (this._events === undefined) return 0\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 if (name !== 'error') emitter.on('error', onerror)\n\n if (signal) signal.addEventListener('abort', onabort)\n\n emitter.on(name, onevent)\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) => promises.push({ resolve, reject }))\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 emitter.off(name, onevent).off('error', onerror)\n\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 signal.removeEventListener('abort', onabort)\n\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 return Promise.reject(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, onevent)\n\n function onevent(...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(name, onevent)\n\n if (name !== 'error') 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 if (typeof emitter.getMaxListeners === 'function') {\n return emitter.getMaxListeners()\n }\n\n return exports.defaultMaxListeners\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 if (typeof emitter.setMaxListeners === 'function') {\n emitter.setMaxListeners(n)\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(msg, 'OPERATION_ABORTED', EventEmitterError.OPERATION_ABORTED, {\n cause\n })\n }\n\n static UNHANDLED_ERROR(cause, msg = 'Unhandled error') {\n return new EventEmitterError(msg, 'UNHANDLED_ERROR', EventEmitterError.UNHANDLED_ERROR, {\n cause\n })\n }\n}\n{\n \"name\": \"bare-events\",\n \"version\": \"2.8.2\",\n \"description\": \"Event emitters 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 \"./web\": {\n \"types\": \"./web.d.ts\",\n \"default\": \"./web.js\"\n },\n \"./errors\": \"./lib/errors.js\"\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"global.js\",\n \"global.d.ts\",\n \"web.js\",\n \"web.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 \"bare-abort-controller\": \"^1.0.0\",\n \"brittle\": \"^3.3.2\",\n \"prettier\": \"^3.4.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"uncaughts\": \"^1.1.1\"\n },\n \"peerDependencies\": {\n \"bare-abort-controller\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-abort-controller\": {\n \"optional\": true\n }\n }\n}\nmodule.exports = require.addon()\nconst FIFO = require('fast-fifo')\nconst EventEmitter = require('bare-events')\nconst path = require('bare-path')\nconst { fileURLToPath } = require('bare-url')\nconst { Readable, Writable } = require('bare-stream')\nconst binding = require('./binding')\nconst constants = require('./lib/constants')\nconst FileError = require('./lib/errors')\n\nconst isWindows = Bare.platform === 'win32'\n\nexports.constants = constants\n\nclass FileRequest {\n static _free = []\n\n static borrow() {\n if (this._free.length > 0) return this._free.pop()\n return new FileRequest()\n }\n\n static return(req) {\n if (this._free.length < 32) this._free.push(req.reset())\n else req.destroy()\n }\n\n constructor() {\n this._reset()\n this._handle = binding.requestInit(this, this._onresult)\n }\n\n get handle() {\n return this._handle\n }\n\n retain(value) {\n this._retain = value // Tie the lifetime of `value` to the lifetime of `this`\n }\n\n reset() {\n if (this._handle === null) return this\n\n binding.requestReset(this._handle)\n\n this._reset()\n\n return this\n }\n\n destroy() {\n if (this._handle === null) return this\n\n binding.requestDestroy(this._handle)\n\n this._reset()\n this._handle = null\n\n return this\n }\n\n then(resolve, reject) {\n return this._promise.then(resolve, reject)\n }\n\n return() {\n if (this._handle === null) return this\n\n FileRequest.return(this)\n\n return this\n }\n\n [Symbol.dispose]() {\n this.return()\n }\n\n _reset() {\n const { promise, resolve, reject } = Promise.withResolvers()\n\n this._promise = promise\n this._resolve = resolve\n this._reject = reject\n this._retain = null\n }\n\n _onresult(err, status) {\n if (err) this._reject(err)\n else this._resolve(status)\n }\n}\n\nfunction ok(result, cb) {\n if (typeof result === 'function') {\n cb = result\n result = undefined\n }\n\n if (cb) cb(null, result)\n else return result\n}\n\nfunction fail(err, cb) {\n if (cb) cb(err)\n else throw err\n}\n\nfunction done(err, result, cb) {\n if (typeof result === 'function') {\n cb = result\n result = undefined\n }\n\n if (err) fail(err, cb)\n else return ok(result, cb)\n}\n\nasync function open(filepath, flags = 'r', mode = 0o666, cb) {\n if (typeof flags === 'function') {\n cb = flags\n flags = 'r'\n mode = 0o666\n } else if (typeof mode === 'function') {\n cb = mode\n mode = 0o666\n }\n\n if (typeof flags === 'string') flags = toFlags(flags)\n if (typeof mode === 'string') mode = toMode(mode)\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let fd\n let err = null\n try {\n binding.open(req.handle, filepath, flags, mode)\n\n fd = await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'open',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, fd, cb)\n}\n\nfunction openSync(filepath, flags = 'r', mode = 0o666) {\n if (typeof flags === 'string') flags = toFlags(flags)\n if (typeof mode === 'string') mode = toMode(mode)\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n return binding.openSync(req.handle, filepath, flags, mode)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'open',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function close(fd, cb) {\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.close(req.handle, fd)\n\n await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'close', code: e.code, fd })\n }\n\n return done(err, cb)\n}\n\nfunction closeSync(fd) {\n using req = FileRequest.borrow()\n\n try {\n binding.closeSync(req.handle, fd)\n } catch (e) {\n throw new FileError(e.message, { operation: 'close', code: e.code, fd })\n }\n}\n\nasync function access(filepath, mode = constants.F_OK, cb) {\n if (typeof mode === 'function') {\n cb = mode\n mode = constants.F_OK\n }\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.access(req.handle, filepath, mode)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'access',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction accessSync(filepath, mode = constants.F_OK) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.accessSync(req.handle, filepath, mode)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'access',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function exists(filepath, cb) {\n let ok = true\n try {\n await access(filepath)\n } catch {\n ok = false\n }\n\n return done(null, ok, cb)\n}\n\nfunction existsSync(filepath) {\n try {\n accessSync(filepath)\n } catch {\n return false\n }\n\n return true\n}\n\nasync function read(fd, buffer, offset = 0, len = buffer.byteLength - offset, pos = -1, cb) {\n if (typeof offset === 'function') {\n cb = offset\n offset = 0\n len = buffer.byteLength\n pos = -1\n } else if (typeof len === 'function') {\n cb = len\n len = buffer.byteLength - offset\n pos = -1\n } else if (typeof pos === 'function') {\n cb = pos\n pos = -1\n }\n\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n let bytes\n let err = null\n try {\n binding.read(req.handle, fd, buffer, offset, len, pos)\n\n bytes = await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'read', code: e.code, fd })\n }\n\n return done(err, bytes, cb)\n}\n\nfunction readSync(fd, buffer, offset = 0, len = buffer.byteLength - offset, pos = -1) {\n using req = FileRequest.borrow()\n\n try {\n return binding.readSync(req.handle, fd, buffer, offset, len, pos)\n } catch (e) {\n throw new FileError(e.message, { operation: 'read', code: e.code, fd })\n }\n}\n\nasync function readv(fd, buffers, pos = -1, cb) {\n if (typeof pos === 'function') {\n cb = pos\n pos = -1\n }\n\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n let bytes\n let err = null\n try {\n binding.readv(req.handle, fd, buffers, pos)\n\n bytes = await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'readv', code: e.code, fd })\n }\n\n return done(err, bytes, cb)\n}\n\nfunction readvSync(fd, buffers, pos = -1) {\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n try {\n return binding.readvSync(req.handle, fd, buffers, pos)\n } catch (e) {\n throw new FileError(e.message, { operation: 'readv', code: e.code, fd })\n }\n}\n\nasync function write(fd, data, offset, len, pos = -1, cb) {\n if (typeof data === 'string') {\n let encoding = len\n cb = pos\n pos = offset\n\n if (typeof pos === 'function') {\n cb = pos\n pos = -1\n encoding = 'utf8'\n } else if (typeof encoding === 'function') {\n cb = encoding\n encoding = 'utf8'\n }\n\n if (typeof pos === 'string') {\n encoding = pos\n pos = -1\n }\n\n data = Buffer.from(data, encoding)\n offset = 0\n len = data.byteLength\n } else if (typeof offset === 'function') {\n cb = offset\n offset = 0\n len = data.byteLength\n pos = -1\n } else if (typeof len === 'function') {\n cb = len\n len = data.byteLength - offset\n pos = -1\n } else if (typeof pos === 'function') {\n cb = pos\n pos = -1\n }\n\n if (typeof offset !== 'number') offset = 0\n if (typeof len !== 'number') len = data.byteLength - offset\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n let bytes\n let err = null\n try {\n binding.write(req.handle, fd, data, offset, len, pos)\n\n bytes = await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'write', code: e.code, fd })\n }\n\n return done(err, bytes, cb)\n}\n\nfunction writeSync(fd, data, offset, len, pos = -1) {\n if (typeof data === 'string') {\n let encoding = len\n pos = offset\n\n if (typeof pos === 'string') {\n encoding = pos\n pos = -1\n }\n\n data = Buffer.from(data, encoding)\n offset = 0\n len = data.byteLength\n }\n\n if (typeof offset !== 'number') offset = 0\n if (typeof len !== 'number') len = data.byteLength - offset\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n try {\n return binding.writeSync(req.handle, fd, data, offset, len, pos)\n } catch (e) {\n throw new FileError(e.message, { operation: 'write', code: e.code, fd })\n }\n}\n\nasync function writev(fd, buffers, pos = -1, cb) {\n if (typeof pos === 'function') {\n cb = pos\n pos = -1\n }\n\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n let bytes\n let err = null\n try {\n binding.writev(req.handle, fd, buffers, pos)\n\n bytes = await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'writev', code: e.code, fd })\n }\n\n return done(err, bytes, cb)\n}\n\nfunction writevSync(fd, buffers, pos = -1) {\n if (typeof pos !== 'number') pos = -1\n\n using req = FileRequest.borrow()\n\n try {\n return binding.writevSync(req.handle, fd, buffers, pos)\n } catch (e) {\n throw new FileError(e.message, { operation: 'writev', code: e.code, fd })\n }\n}\n\nasync function stat(filepath, cb) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let st\n let err = null\n try {\n binding.stat(req.handle, filepath)\n\n await req\n\n st = new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'stat',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, st, cb)\n}\n\nfunction statSync(filepath) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.statSync(req.handle, filepath)\n\n return new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'stat',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function lstat(filepath, cb) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let st\n let err = null\n try {\n binding.lstat(req.handle, filepath)\n\n await req\n\n st = new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'lstat',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, st, cb)\n}\n\nfunction lstatSync(filepath) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.lstatSync(req.handle, filepath)\n\n return new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'lstat',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function fstat(fd, cb) {\n using req = FileRequest.borrow()\n\n let st\n let err = null\n try {\n binding.fstat(req.handle, fd)\n\n await req\n\n st = new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n err = new FileError(e.message, { operation: 'fstat', code: e.code, fd })\n }\n\n return done(err, st, cb)\n}\n\nfunction fstatSync(fd) {\n using req = FileRequest.borrow()\n\n try {\n binding.fstatSync(req.handle, fd)\n\n return new Stats(...binding.requestResultStat(req.handle))\n } catch (e) {\n throw new FileError(e.message, { operation: 'fstat', code: e.code, fd })\n }\n}\n\nasync function ftruncate(fd, len = 0, cb) {\n if (typeof len === 'function') {\n cb = len\n len = 0\n }\n\n if (typeof len !== 'number') len = 0\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.ftruncate(req.handle, fd, len)\n\n await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'ftruncate', code: e.code, fd })\n }\n\n return done(err, cb)\n}\n\nfunction ftruncateSync(fd, len = 0) {\n if (typeof len !== 'number') len = 0\n\n using req = FileRequest.borrow()\n\n try {\n binding.ftruncateSync(req.handle, fd, len)\n } catch (e) {\n throw new FileError(e.message, { operation: 'ftruncate', code: e.code, fd })\n }\n}\n\nasync function chmod(filepath, mode, cb) {\n if (typeof mode === 'string') mode = toMode(mode)\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.chmod(req.handle, filepath, mode)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'chmod',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction chmodSync(filepath, mode) {\n if (typeof mode === 'string') mode = toMode(mode)\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.chmodSync(req.handle, filepath, mode)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'chmod',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function fchmod(fd, mode, cb) {\n if (typeof mode === 'string') mode = toMode(mode)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.fchmod(req.handle, fd, mode)\n\n await req\n } catch (e) {\n err = new FileError(e.message, { operation: 'fchmod', code: e.code, fd })\n }\n\n return done(err, cb)\n}\n\nfunction fchmodSync(fd, mode) {\n if (typeof mode === 'string') mode = toMode(mode)\n\n using req = FileRequest.borrow()\n\n try {\n binding.fchmodSync(req.handle, fd, mode)\n } catch (e) {\n throw new FileError(e.message, { operation: 'fchmod', code: e.code, fd })\n }\n}\n\nasync function utimes(filepath, atime, mtime, cb) {\n if (typeof atime !== 'number') atime = atime.getTime() / 1000\n if (typeof mtime !== 'number') mtime = mtime.getTime() / 1000\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.utimes(req.handle, filepath, atime, mtime)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'utimes',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction utimesSync(filepath, atime, mtime) {\n if (typeof atime !== 'number') atime = atime.getTime() / 1000\n if (typeof mtime !== 'number') mtime = mtime.getTime() / 1000\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.utimesSync(req.handle, filepath, atime, mtime)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'utimes',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function mkdir(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = { mode: 0o777 }\n }\n\n if (typeof opts === 'number') opts = { mode: opts }\n else if (!opts) opts = {}\n\n const mode = typeof opts.mode === 'number' ? opts.mode : 0o777\n\n filepath = toNamespacedPath(filepath)\n\n if (opts.recursive) {\n let err = null\n try {\n try {\n await mkdir(filepath, { mode })\n } catch (err) {\n if (err.code !== 'ENOENT') {\n if (!(await stat(filepath)).isDirectory()) throw err\n } else {\n while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1)\n const i = filepath.lastIndexOf(path.sep)\n if (i <= 0) throw err\n\n await mkdir(filepath.slice(0, i), { mode, recursive: true })\n\n try {\n await mkdir(filepath, { mode })\n } catch (err) {\n if (!(await stat(filepath)).isDirectory()) throw err\n }\n }\n }\n } catch (e) {\n err = e\n }\n\n return done(err, cb)\n }\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.mkdir(req.handle, filepath, mode)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'mkdir',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction mkdirSync(filepath, opts) {\n if (typeof opts === 'number') opts = { mode: opts }\n else if (!opts) opts = {}\n\n const mode = typeof opts.mode === 'number' ? opts.mode : 0o777\n\n filepath = toNamespacedPath(filepath)\n\n if (opts.recursive) {\n try {\n mkdirSync(filepath, { mode })\n } catch (err) {\n if (err.code !== 'ENOENT') {\n if (!statSync(filepath).isDirectory()) throw err\n } else {\n while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1)\n const i = filepath.lastIndexOf(path.sep)\n if (i <= 0) throw err\n\n mkdirSync(filepath.slice(0, i), { mode, recursive: true })\n\n try {\n mkdirSync(filepath, { mode })\n } catch (err) {\n if (!statSync(filepath).isDirectory()) throw err\n }\n }\n }\n\n return\n }\n\n using req = FileRequest.borrow()\n\n try {\n binding.mkdirSync(req.handle, filepath, mode)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'mkdir',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function rmdir(filepath, cb) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.rmdir(req.handle, filepath)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'rmdir',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction rmdirSync(filepath) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.rmdirSync(req.handle, filepath)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'rmdir',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function rm(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (!opts) opts = {}\n\n filepath = toNamespacedPath(filepath)\n\n let err = null\n try {\n const st = await lstat(filepath)\n\n if (st.isDirectory()) {\n if (opts.recursive) {\n try {\n await rmdir(filepath)\n } catch (err) {\n if (err.code !== 'ENOTEMPTY') throw err\n\n const files = await readdir(filepath)\n\n for (const file of files) {\n await rm(filepath + path.sep + file, opts)\n }\n\n await rmdir(filepath)\n }\n } else {\n throw new FileError('is a directory', {\n operation: 'rm',\n code: 'EISDIR',\n path: filepath\n })\n }\n } else {\n await unlink(filepath)\n }\n } catch (e) {\n if (e.code !== 'ENOENT' || !opts.force) err = e\n }\n\n return done(err, cb)\n}\n\nfunction rmSync(filepath, opts) {\n if (!opts) opts = {}\n\n filepath = toNamespacedPath(filepath)\n\n try {\n const st = lstatSync(filepath)\n\n if (st.isDirectory()) {\n if (opts.recursive) {\n try {\n rmdirSync(filepath)\n } catch (err) {\n if (err.code !== 'ENOTEMPTY') throw err\n\n const files = readdirSync(filepath)\n\n for (const file of files) {\n rmSync(filepath + path.sep + file, opts)\n }\n\n rmdirSync(filepath)\n }\n } else {\n throw new FileError('is a directory', {\n operation: 'rm',\n code: 'EISDIR',\n path: filepath\n })\n }\n } else {\n unlinkSync(filepath)\n }\n } catch (err) {\n if (err.code !== 'ENOENT' || !opts.force) throw err\n }\n}\n\nasync function unlink(filepath, cb) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.unlink(req.handle, filepath)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'unlink',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction unlinkSync(filepath) {\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.unlinkSync(req.handle, filepath)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'unlink',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function rename(src, dst, cb) {\n src = toNamespacedPath(src)\n dst = toNamespacedPath(dst)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.rename(req.handle, src, dst)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'rename',\n code: e.code,\n path: src,\n destination: dst\n })\n }\n\n return done(err, cb)\n}\n\nfunction renameSync(src, dst) {\n src = toNamespacedPath(src)\n dst = toNamespacedPath(dst)\n\n using req = FileRequest.borrow()\n\n try {\n binding.renameSync(req.handle, src, dst)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'rename',\n code: e.code,\n path: src,\n destination: dst\n })\n }\n}\n\nasync function copyFile(src, dst, mode = 0, cb) {\n if (typeof mode === 'function') {\n cb = mode\n mode = 0\n }\n\n src = toNamespacedPath(src)\n dst = toNamespacedPath(dst)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.copyfile(req.handle, src, dst, mode)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'copyfile',\n code: e.code,\n path: src,\n destination: dst\n })\n }\n\n return done(err, cb)\n}\n\nfunction copyFileSync(src, dst, mode = 0) {\n src = toNamespacedPath(src)\n dst = toNamespacedPath(dst)\n\n using req = FileRequest.borrow()\n\n try {\n binding.copyfileSync(req.handle, src, dst, mode)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'copyfile',\n code: e.code,\n path: src,\n destination: dst\n })\n }\n}\n\nasync function cp(src, dst, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (!opts) opts = {}\n\n src = toNamespacedPath(src)\n dst = toNamespacedPath(dst)\n\n let err = null\n try {\n const st = await lstat(src)\n\n if (st.isDirectory()) {\n if (opts.recursive !== true) {\n throw new FileError('is a directory', {\n operation: 'cp',\n code: 'EISDIR',\n path: src\n })\n }\n\n try {\n await lstat(dst)\n } catch (e) {\n if (e.code === 'ENOENT') {\n await mkdir(dst, { mode: st.mode, recursive: true })\n } else {\n throw e\n }\n }\n\n const dir = await opendir(src)\n for await (const { name } of dir) {\n await cp(path.join(src, name), path.join(dst, name), opts)\n }\n } else if (st.isFile()) {\n await copyFile(src, dst)\n await chmod(dst, st.mode)\n }\n } catch (e) {\n err = e\n }\n\n return done(err, cb)\n}\n\nasync function realpath(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'utf8' } = opts\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let res\n let err = null\n try {\n binding.realpath(req.handle, filepath)\n\n await req\n\n res = Buffer.from(binding.requestResultString(req.handle))\n\n if (encoding !== 'buffer') res = res.toString(encoding)\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'realpath',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, res, cb)\n}\n\nfunction realpathSync(filepath, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'utf8' } = opts\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.realpathSync(req.handle, filepath)\n\n let res = Buffer.from(binding.requestResultString(req.handle))\n\n if (encoding !== 'buffer') res = res.toString(encoding)\n\n return res\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'realpath',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function readlink(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'utf8' } = opts\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let res\n let err = null\n try {\n binding.readlink(req.handle, filepath)\n\n await req\n\n res = Buffer.from(binding.requestResultString(req.handle))\n\n if (encoding !== 'buffer') res = res.toString(encoding)\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'readlink',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, res, cb)\n}\n\nfunction readlinkSync(filepath, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'utf8' } = opts\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.readlinkSync(req.handle, filepath)\n\n let res = Buffer.from(binding.requestResultString(req.handle))\n\n if (encoding !== 'buffer') res = res.toString(encoding)\n\n return res\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'readlink',\n code: e.code,\n path: filepath\n })\n }\n}\n\nfunction normalizeSymlinkTarget(target, type, filepath) {\n if (isWindows) {\n if (type === 'junction') target = path.resolve(filepath, '..', target)\n\n if (path.isAbsolute(target)) return path.toNamespacedPath(target)\n\n return target.replace(/\\//g, path.sep)\n }\n\n return target\n}\n\nasync function symlink(target, filepath, type, cb) {\n if (typeof type === 'function') {\n cb = type\n type = null\n }\n\n filepath = toNamespacedPath(filepath)\n\n if (typeof type === 'string') {\n switch (type) {\n case 'file':\n default:\n type = 0\n break\n case 'dir':\n type = constants.UV_FS_SYMLINK_DIR\n break\n case 'junction':\n type = constants.UV_FS_SYMLINK_JUNCTION\n break\n }\n } else if (typeof type !== 'number') {\n if (isWindows) {\n target = path.resolve(filepath, '..', target)\n\n try {\n type = (await stat(target)).isDirectory()\n ? constants.UV_FS_SYMLINK_DIR\n : constants.UV_FS_SYMLINK_JUNCTION\n } catch {\n type = 0\n }\n } else {\n type = 0\n }\n }\n\n target = normalizeSymlinkTarget(target)\n\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.symlink(req.handle, target, filepath, type)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'symlink',\n code: e.code,\n path: target,\n destination: filepath\n })\n }\n\n return done(err, cb)\n}\n\nfunction symlinkSync(target, filepath, type) {\n filepath = toNamespacedPath(filepath)\n\n if (typeof type === 'string') {\n switch (type) {\n case 'file':\n default:\n type = 0\n break\n case 'dir':\n type = constants.UV_FS_SYMLINK_DIR\n break\n case 'junction':\n type = constants.UV_FS_SYMLINK_JUNCTION\n break\n }\n } else if (typeof type !== 'number') {\n if (isWindows) {\n target = path.resolve(filepath, '..', target)\n\n try {\n type = statSync(target).isDirectory()\n ? constants.UV_FS_SYMLINK_DIR\n : constants.UV_FS_SYMLINK_JUNCTION\n } catch {\n type = 0\n }\n } else {\n type = 0\n }\n }\n\n target = normalizeSymlinkTarget(target)\n\n using req = FileRequest.borrow()\n\n try {\n binding.symlinkSync(req.handle, target, filepath, type)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'symlink',\n code: e.code,\n path: target,\n destination: filepath\n })\n }\n}\n\nasync function opendir(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n let dir\n let err = null\n try {\n binding.opendir(req.handle, filepath)\n\n await req\n\n dir = new Dir(filepath, binding.requestResultDir(req.handle), opts)\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'opendir',\n code: e.code,\n path: filepath\n })\n }\n\n return done(err, dir, cb)\n}\n\nfunction opendirSync(filepath, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n filepath = toNamespacedPath(filepath)\n\n using req = FileRequest.borrow()\n\n try {\n binding.opendirSync(req.handle, filepath)\n\n return new Dir(filepath, binding.requestResultDir(req.handle), opts)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'opendir',\n code: e.code,\n path: filepath\n })\n }\n}\n\nasync function readdir(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { withFileTypes = false } = opts\n\n filepath = toNamespacedPath(filepath)\n\n let result = []\n let err = null\n try {\n const dir = await opendir(filepath)\n\n for await (const entry of dir) {\n result.push(withFileTypes ? entry : entry.name)\n }\n } catch (e) {\n result = []\n err = e\n }\n\n return done(err, result, cb)\n}\n\nfunction readdirSync(filepath, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { withFileTypes = false } = opts\n\n filepath = toNamespacedPath(filepath)\n\n const dir = opendirSync(filepath, opts)\n const result = []\n\n for (const entry of dir) {\n result.push(withFileTypes ? entry : entry.name)\n }\n\n return result\n}\n\nasync function readFile(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'buffer' } = opts\n\n let fd = -1\n let buffer = null\n let err = null\n try {\n fd = await open(filepath, opts.flag || 'r')\n\n const st = await fstat(fd)\n\n let len = 0\n\n if (st.size === 0) {\n const buffers = []\n\n while (true) {\n buffer = Buffer.allocUnsafe(8192)\n const r = await read(fd, buffer)\n len += r\n if (r === 0) break\n buffers.push(buffer.subarray(0, r))\n }\n\n buffer = Buffer.concat(buffers)\n } else {\n buffer = Buffer.allocUnsafe(st.size)\n\n while (true) {\n const r = await read(fd, len ? buffer.subarray(len) : buffer)\n len += r\n if (r === 0 || len === buffer.byteLength) break\n }\n\n if (len !== buffer.byteLength) buffer = buffer.subarray(0, len)\n }\n\n if (encoding !== 'buffer') buffer = buffer.toString(encoding)\n } catch (e) {\n err = e\n } finally {\n if (fd !== -1) await close(fd)\n }\n\n return done(err, buffer, cb)\n}\n\nfunction readFileSync(filepath, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n const { encoding = 'buffer' } = opts\n\n let fd = -1\n try {\n fd = openSync(filepath, opts.flag || 'r')\n\n const st = fstatSync(fd)\n\n let buffer\n let len = 0\n\n if (st.size === 0) {\n const buffers = []\n\n while (true) {\n buffer = Buffer.allocUnsafe(8192)\n const r = readSync(fd, buffer)\n len += r\n if (r === 0) break\n buffers.push(buffer.subarray(0, r))\n }\n\n buffer = Buffer.concat(buffers)\n } else {\n buffer = Buffer.allocUnsafe(st.size)\n\n while (true) {\n const r = readSync(fd, len ? buffer.subarray(len) : buffer)\n len += r\n if (r === 0 || len === buffer.byteLength) break\n }\n\n if (len !== buffer.byteLength) buffer = buffer.subarray(0, len)\n }\n\n if (encoding !== 'buffer') buffer = buffer.toString(encoding)\n\n return buffer\n } finally {\n if (fd !== -1) closeSync(fd)\n }\n}\n\nasync function writeFile(filepath, data, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n if (typeof data === 'string') data = Buffer.from(data, opts.encoding)\n\n let fd = -1\n let len = 0\n let err = null\n try {\n fd = await open(filepath, opts.flag || 'w', opts.mode || 0o666)\n\n while (true) {\n len += await write(fd, len ? data.subarray(len) : data)\n if (len === data.byteLength) break\n }\n } catch (e) {\n err = e\n } finally {\n if (fd !== -1) await close(fd)\n }\n\n return done(err, len, cb)\n}\n\nfunction writeFileSync(filepath, data, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n if (typeof data === 'string') data = Buffer.from(data, opts.encoding)\n\n let fd = -1\n try {\n fd = openSync(filepath, opts.flag || 'w', opts.mode || 0o666)\n\n let len = 0\n\n while (true) {\n len += writeSync(fd, len ? data.subarray(len) : data)\n if (len === data.byteLength) break\n }\n } finally {\n if (fd !== -1) closeSync(fd)\n }\n}\n\nfunction appendFile(filepath, data, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n if (!opts.flag) opts = { ...opts, flag: 'a' }\n\n return writeFile(filepath, data, opts, cb)\n}\n\nfunction appendFileSync(filepath, data, opts) {\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n if (!opts.flag) opts = { ...opts, flag: 'a' }\n\n return writeFileSync(filepath, data, opts)\n}\n\nfunction watch(filepath, opts, cb) {\n if (typeof opts === 'function') {\n cb = opts\n opts = {}\n }\n\n if (typeof opts === 'string') opts = { encoding: opts }\n else if (!opts) opts = {}\n\n filepath = toNamespacedPath(filepath)\n\n return new Watcher(filepath, opts, cb)\n}\n\nclass Stats {\n constructor(\n dev,\n mode,\n nlink,\n uid,\n gid,\n rdev,\n blksize,\n ino,\n size,\n blocks,\n atimeMs,\n mtimeMs,\n ctimeMs,\n birthtimeMs\n ) {\n this.dev = dev\n this.mode = mode\n this.nlink = nlink\n this.uid = uid\n this.gid = gid\n this.rdev = rdev\n this.blksize = blksize\n this.ino = ino\n this.size = size\n this.blocks = blocks\n this.atimeMs = atimeMs\n this.mtimeMs = mtimeMs\n this.ctimeMs = ctimeMs\n this.birthtimeMs = birthtimeMs\n this.atime = new Date(atimeMs)\n this.mtime = new Date(mtimeMs)\n this.ctime = new Date(ctimeMs)\n this.birthtime = new Date(birthtimeMs)\n }\n\n isDirectory() {\n return (this.mode & constants.S_IFMT) === constants.S_IFDIR\n }\n\n isFile() {\n return (this.mode & constants.S_IFMT) === constants.S_IFREG\n }\n\n isBlockDevice() {\n return (this.mode & constants.S_IFMT) === constants.S_IFBLK\n }\n\n isCharacterDevice() {\n return (this.mode & constants.S_IFCHR) === constants.S_IFCHR\n }\n\n isFIFO() {\n return (this.mode & constants.S_IFMT) === constants.S_IFIFO\n }\n\n isSymbolicLink() {\n return (this.mode & constants.S_IFMT) === constants.S_IFLNK\n }\n\n isSocket() {\n return (this.mode & constants.S_IFMT) === constants.S_IFSOCK\n }\n}\n\nclass Dir {\n constructor(path, handle, opts = {}) {\n const { encoding = 'utf8', bufferSize = 32 } = opts\n\n this.path = path\n\n this._encoding = encoding\n this._capacity = bufferSize\n this._buffer = new FIFO()\n this._ended = false\n this._handle = handle\n }\n\n async read(cb) {\n if (this._buffer.length) return ok(this._buffer.shift(), cb)\n if (this._ended) return ok(null, cb)\n\n using req = FileRequest.borrow()\n\n let entries\n let err = null\n try {\n req.retain(binding.readdir(req.handle, this._handle, this._capacity))\n\n await req\n\n entries = binding.requestResultDirents(req.handle)\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'readdir',\n code: e.code,\n path: this.path\n })\n }\n\n if (err) return fail(err, cb)\n\n if (entries.length === 0) {\n this._ended = true\n\n return ok(null, cb)\n }\n\n for (const entry of entries) {\n let name = Buffer.from(entry.name)\n\n if (this._encoding !== 'buffer') name = name.toString(this._encoding)\n\n this._buffer.push(new Dirent(this.path, name, entry.type))\n }\n\n return ok(this._buffer.shift(), cb)\n }\n\n readSync() {\n if (this._buffer.length) return this._buffer.shift()\n if (this._ended) return null\n\n using req = FileRequest.borrow()\n\n let entries\n try {\n req.retain(binding.readdirSync(req.handle, this._handle, this._capacity))\n\n entries = binding.requestResultDirents(req.handle)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'readdir',\n code: e.code,\n path: this.path\n })\n }\n\n if (entries.length === 0) {\n this._ended = true\n\n return null\n }\n\n for (const entry of entries) {\n let name = Buffer.from(entry.name)\n\n if (this._encoding !== 'buffer') name = name.toString(this._encoding)\n\n this._buffer.push(new Dirent(this.path, name, entry.type))\n }\n\n return this._buffer.shift()\n }\n\n async close(cb) {\n using req = FileRequest.borrow()\n\n let err = null\n try {\n binding.closedir(req.handle, this._handle)\n\n await req\n } catch (e) {\n err = new FileError(e.message, {\n operation: 'closedir',\n code: e.code,\n path: this.path\n })\n }\n\n this._handle = null\n\n return done(err, cb)\n }\n\n closeSync() {\n using req = FileRequest.borrow()\n\n try {\n binding.closedirSync(req.handle, this._handle)\n } catch (e) {\n throw new FileError(e.message, {\n operation: 'closedir',\n code: e.code,\n path: this.path\n })\n }\n\n this._handle = null\n }\n\n [Symbol.dispose]() {\n this.closeSync()\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n\n *[Symbol.iterator]() {\n while (true) {\n const entry = this.readSync()\n if (entry === null) break\n yield entry\n }\n\n this.closeSync()\n }\n\n async *[Symbol.asyncIterator]() {\n while (true) {\n const entry = await this.read()\n if (entry === null) break\n yield entry\n }\n\n await this.close()\n }\n}\n\nclass Dirent {\n constructor(parentPath, name, type) {\n this.parentPath = parentPath\n this.name = name\n this.type = type\n }\n\n isFile() {\n return this.type === constants.UV_DIRENT_FILE\n }\n\n isDirectory() {\n return this.type === constants.UV_DIRENT_DIR\n }\n\n isSymbolicLink() {\n return this.type === constants.UV_DIRENT_LINK\n }\n\n isFIFO() {\n return this.type === constants.UV_DIRENT_FIFO\n }\n\n isSocket() {\n return this.type === constants.UV_DIRENT_SOCKET\n }\n\n isCharacterDevice() {\n return this.type === constants.UV_DIRENT_CHAR\n }\n\n isBlockDevice() {\n return this.type === constants.UV_DIRENT_BLOCK\n }\n}\n\nclass FileReadStream extends Readable {\n constructor(path, opts = {}) {\n const { eagerOpen = true } = opts\n\n super({ eagerOpen, ...opts })\n\n this.path = path\n this.fd = typeof opts.fd === 'number' ? opts.fd : -1\n this.flags = opts.flags || 'r'\n this.mode = opts.mode || 0o666\n\n this._offset = opts.start || 0\n this._missing = 0\n\n if (opts.length) {\n this._missing = opts.length\n } else if (typeof opts.end === 'number') {\n this._missing = opts.end - this._offset + 1\n } else {\n this._missing = -1\n }\n }\n\n async _open(cb) {\n let err\n\n if (this.fd === -1) {\n err = null\n try {\n this.fd = await open(this.path, this.flags, this.mode)\n } catch (e) {\n err = e\n }\n\n if (err) return cb(err)\n }\n\n let st\n err = null\n try {\n st = await fstat(this.fd)\n } catch (e) {\n err = e\n }\n\n if (err) return cb(err)\n\n if (this._missing === -1) this._missing = st.size\n\n if (st.size < this._offset) {\n this._offset = st.size\n this._missing = 0\n } else if (st.size < this._offset + this._missing) {\n this._missing = st.size - this._offset\n }\n\n cb(null)\n }\n\n async _read(size) {\n if (this._missing <= 0) return this.push(null)\n\n const data = Buffer.allocUnsafe(Math.min(this._missing, size))\n\n let len\n let err = null\n try {\n len = await read(this.fd, data, 0, data.byteLength, this._offset)\n } catch (e) {\n err = e\n }\n\n if (err) return this.destroy(err)\n\n if (len === 0) return this.push(null)\n\n if (this._missing < len) len = this._missing\n\n this._missing -= len\n this._offset += len\n\n this.push(data.subarray(0, len))\n }\n\n async _destroy(err, cb) {\n if (this.fd === -1) return cb(err)\n\n err = null\n try {\n await close(this.fd)\n } catch (e) {\n err = e\n }\n\n cb(err)\n }\n}\n\nclass FileWriteStream extends Writable {\n constructor(path, opts = {}) {\n const { eagerOpen = true } = opts\n\n super({ eagerOpen, ...opts })\n\n this.path = path\n this.fd = typeof opts.fd === 'number' ? opts.fd : -1\n this.flags = opts.flags || 'w'\n this.mode = opts.mode || 0o666\n }\n\n async _open(cb) {\n if (this.fd !== -1) return cb(null)\n\n let err = null\n try {\n this.fd = await open(this.path, this.flags, this.mode)\n } catch (e) {\n err = e\n }\n\n cb(err)\n }\n\n async _writev(batch, cb) {\n let err = null\n try {\n await writev(\n this.fd,\n batch.map(({ chunk }) => chunk)\n )\n } catch (e) {\n err = e\n }\n\n cb(err)\n }\n\n async _destroy(err, cb) {\n if (this.fd === -1) return cb(err)\n\n err = null\n try {\n await close(this.fd)\n } catch (e) {\n err = e\n }\n\n cb(err)\n }\n}\n\nclass Watcher extends EventEmitter {\n constructor(path, opts, onchange) {\n if (typeof opts === 'function') {\n onchange = opts\n opts = {}\n }\n\n if (!opts) opts = {}\n\n const { persistent = true, recursive = false, encoding = 'utf8' } = opts\n\n super()\n\n this._closed = false\n this._encoding = encoding\n this._handle = binding.watcherInit(path, recursive, this, this._onevent, this._onclose)\n\n if (!persistent) this.unref()\n\n if (onchange) this.on('change', onchange)\n }\n\n close() {\n if (this._closed) return\n this._closed = true\n\n binding.watcherClose(this._handle)\n }\n\n ref() {\n if (this._handle) binding.watcherRef(this._handle)\n return this\n }\n\n unref() {\n if (this._handle) binding.watcherUnref(this._handle)\n return this\n }\n\n [Symbol.asyncIterator]() {\n const buffer = []\n let done = false\n let error = null\n let next = null\n\n this.on('change', (eventType, filename) => {\n if (next) {\n next.resolve({ done: false, value: { eventType, filename } })\n next = null\n } else {\n buffer.push({ eventType, filename })\n }\n })\n .on('error', (err) => {\n done = true\n error = err\n\n if (next) {\n next.reject(error)\n next = null\n }\n })\n .on('close', () => {\n done = true\n\n if (next) {\n next.resolve({ done })\n next = null\n }\n })\n\n return {\n next: () =>\n new Promise((resolve, reject) => {\n if (error) return reject(error)\n\n if (buffer.length) return resolve({ done: false, value: buffer.shift() })\n\n if (done) return resolve({ done })\n\n next = { resolve, reject }\n })\n }\n }\n\n _onevent(err, events, filename) {\n if (err) {\n this.close()\n this.emit('error', err)\n } else {\n const path =\n this._encoding === 'buffer'\n ? Buffer.from(filename)\n : Buffer.from(filename).toString(this._encoding)\n\n if (events & binding.UV_RENAME) {\n this.emit('change', 'rename', path)\n }\n\n if (events & binding.UV_CHANGE) {\n this.emit('change', 'change', path)\n }\n }\n }\n\n _onclose() {\n this._handle = null\n\n this.emit('close')\n }\n}\n\nexports.access = access\nexports.appendFile = appendFile\nexports.chmod = chmod\n// exports.chown = chown TODO\nexports.close = close\nexports.copyFile = copyFile\nexports.cp = cp\nexports.exists = exists\nexports.fchmod = fchmod\n// exports.fchown = fchown TODO\n// exports.fdatasync = fdatasync TODO\nexports.fstat = fstat\n// exports.fsync = fsync TODO\nexports.ftruncate = ftruncate\n// exports.futimes = futimes TODO\n// exports.lchmod = lchmod TODO\n// exports.lchown = lchown TODO\n// exports.lutimes = lutimes TODO\n// exports.link = link TODO\nexports.lstat = lstat\nexports.mkdir = mkdir\n// exports.mkdtemp = mkdtemp TODO\nexports.open = open\nexports.opendir = opendir\nexports.read = read\nexports.readFile = readFile\nexports.readdir = readdir\nexports.readlink = readlink\nexports.readv = readv\nexports.realpath = realpath\nexports.rename = rename\nexports.rm = rm\nexports.rmdir = rmdir\nexports.stat = stat\n// exports.statfs = statfs TODO\nexports.symlink = symlink\n// exports.truncate = truncate TODO\nexports.unlink = unlink\nexports.utimes = utimes\nexports.watch = watch\nexports.write = write\nexports.writeFile = writeFile\nexports.writev = writev\n\nexports.accessSync = accessSync\nexports.appendFileSync = appendFileSync\nexports.chmodSync = chmodSync\n// exports.chownSync = chownSync TODO\nexports.closeSync = closeSync\nexports.copyFileSync = copyFileSync\nexports.existsSync = existsSync\nexports.fchmodSync = fchmodSync\n// exports.fchownSync = fchownSync TODO\n// exports.fdatasyncSync = fdatasyncSync TODO\nexports.fstatSync = fstatSync\n// exports.fsyncSync = fsyncSync TODO\nexports.ftruncateSync = ftruncateSync\n// exports.futimesSync = futimesSync TODO\n// exports.lchmodSync = lchmodSync TODO\n// exports.lchownSync = lchownSync TODO\n// exports.lutimesSync = lutimesSync TODO\n// exports.linkSync = linkSync TODO\nexports.lstatSync = lstatSync\nexports.mkdirSync = mkdirSync\n// exports.mkdtempSync = mkdtempSync TODO\nexports.openSync = openSync\nexports.opendirSync = opendirSync\nexports.readFileSync = readFileSync\nexports.readSync = readSync\nexports.readdirSync = readdirSync\nexports.readlinkSync = readlinkSync\nexports.readvSync = readvSync\nexports.realpathSync = realpathSync\nexports.renameSync = renameSync\nexports.rmSync = rmSync\nexports.rmdirSync = rmdirSync\nexports.statSync = statSync\n// exports.statfsSync = statfsSync TODO\nexports.symlinkSync = symlinkSync\n// exports.truncateSync = truncateSync TODO\nexports.unlinkSync = unlinkSync\nexports.utimesSync = utimesSync\nexports.writeFileSync = writeFileSync\nexports.writeSync = writeSync\nexports.writevSync = writevSync\n\nexports.promises = require('./promises')\n\nexports.Stats = Stats\nexports.Dir = Dir\nexports.Dirent = Dirent\nexports.Watcher = Watcher\n\nexports.ReadStream = FileReadStream\n\nexports.createReadStream = function createReadStream(path, opts) {\n return new FileReadStream(path, opts)\n}\n\nexports.WriteStream = FileWriteStream\n\nexports.createWriteStream = function createWriteStream(path, opts) {\n return new FileWriteStream(path, opts)\n}\n\nfunction toNamespacedPath(filepath) {\n if (typeof filepath !== 'string') {\n if (URL.isURL(filepath)) filepath = fileURLToPath(filepath)\n else filepath = filepath.toString()\n }\n\n return path.toNamespacedPath(filepath)\n}\n\nfunction toFlags(flags) {\n switch (flags) {\n case 'r':\n return constants.O_RDONLY\n case 'rs': // Fall through.\n case 'sr':\n return constants.O_RDONLY | constants.O_SYNC\n case 'r+':\n return constants.O_RDWR\n case 'rs+': // Fall through.\n case 'sr+':\n return constants.O_RDWR | constants.O_SYNC\n\n case 'w':\n return constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY\n case 'wx': // Fall through.\n case 'xw':\n return constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY | constants.O_EXCL\n\n case 'w+':\n return constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR\n case 'wx+': // Fall through.\n case 'xw+':\n return constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR | constants.O_EXCL\n\n case 'a':\n return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY\n case 'ax': // Fall through.\n case 'xa':\n return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | constants.O_EXCL\n case 'as': // Fall through.\n case 'sa':\n return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | constants.O_SYNC\n\n case 'a+':\n return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR\n case 'ax+': // Fall through.\n case 'xa+':\n return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR | constants.O_EXCL\n case 'as+': // Fall through.\n case 'sa+':\n return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR | constants.O_SYNC\n default:\n return 0\n }\n}\n\nfunction toMode(mode) {\n return parseInt(mode, 8)\n}\nconst binding = require('../binding')\n\nmodule.exports = {\n O_RDWR: binding.O_RDWR,\n O_RDONLY: binding.O_RDONLY,\n O_WRONLY: binding.O_WRONLY,\n O_CREAT: binding.O_CREAT,\n O_TRUNC: binding.O_TRUNC,\n O_APPEND: binding.O_APPEND,\n\n F_OK: binding.F_OK || 0,\n R_OK: binding.R_OK || 0,\n W_OK: binding.W_OK || 0,\n X_OK: binding.X_OK || 0,\n\n S_IFMT: binding.S_IFMT,\n S_IFREG: binding.S_IFREG,\n S_IFDIR: binding.S_IFDIR,\n S_IFCHR: binding.S_IFCHR,\n S_IFLNK: binding.S_IFLNK,\n S_IFBLK: binding.S_IFBLK || 0,\n S_IFIFO: binding.S_IFIFO || 0,\n S_IFSOCK: binding.S_IFSOCK || 0,\n\n S_IRUSR: binding.S_IRUSR || 0,\n S_IWUSR: binding.S_IWUSR || 0,\n S_IXUSR: binding.S_IXUSR || 0,\n S_IRGRP: binding.S_IRGRP || 0,\n S_IWGRP: binding.S_IWGRP || 0,\n S_IXGRP: binding.S_IXGRP || 0,\n S_IROTH: binding.S_IROTH || 0,\n S_IWOTH: binding.S_IWOTH || 0,\n S_IXOTH: binding.S_IXOTH || 0,\n\n UV_DIRENT_UNKNOWN: binding.UV_DIRENT_UNKNOWN,\n UV_DIRENT_FILE: binding.UV_DIRENT_FILE,\n UV_DIRENT_DIR: binding.UV_DIRENT_DIR,\n UV_DIRENT_LINK: binding.UV_DIRENT_LINK,\n UV_DIRENT_FIFO: binding.UV_DIRENT_FIFO,\n UV_DIRENT_SOCKET: binding.UV_DIRENT_SOCKET,\n UV_DIRENT_CHAR: binding.UV_DIRENT_CHAR,\n UV_DIRENT_BLOCK: binding.UV_DIRENT_BLOCK,\n\n COPYFILE_EXCL: binding.UV_FS_COPYFILE_EXCL,\n COPYFILE_FICLONE: binding.UV_FS_COPYFILE_FICLONE,\n COPYFILE_FICLONE_FORCE: binding.UV_FS_COPYFILE_FICLONE_FORCE,\n UV_FS_SYMLINK_DIR: binding.UV_FS_SYMLINK_DIR,\n UV_FS_SYMLINK_JUNCTION: binding.UV_FS_SYMLINK_JUNCTION\n}\nconst os = require('bare-os')\n\nmodule.exports = class FileError extends Error {\n constructor(msg, opts = {}) {\n const { code, operation = null, path = null, destination = null, fd = -1 } = opts\n\n if (operation !== null) msg += describe(operation, opts)\n\n super(`${code}: ${msg}`)\n\n this.code = code\n\n if (operation !== null) this.operation = operation\n if (path !== null) this.path = path\n if (destination !== null) this.destination = destination\n if (fd !== -1) this.fd = fd\n }\n\n get name() {\n return 'FileError'\n }\n\n // For Node.js compatibility\n get errno() {\n return os.constants.errnos[this.code]\n }\n\n // For Node.js compatibility\n get syscall() {\n return this.operation\n }\n\n // For Node.js compatibility\n get dest() {\n return this.destination\n }\n}\n\nfunction describe(operation, opts) {\n const { path = null, destination = null, fd = -1 } = opts\n\n let result = `, ${operation}`\n\n if (path !== null) {\n result += ` ${JSON.stringify(path)}`\n\n if (destination !== null) {\n result += ` -> ${JSON.stringify(destination)}`\n }\n } else if (fd !== -1) {\n result += ` ${fd}`\n }\n\n return result\n}\n{\n \"name\": \"bare-fs\",\n \"version\": \"4.5.4\",\n \"description\": \"Native file system operations for Javascript\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n },\n \"./promises\": {\n \"types\": \"./promises.d.ts\",\n \"default\": \"./promises.js\"\n },\n \"./constants\": {\n \"types\": \"./lib/constants.d.ts\",\n \"default\": \"./lib/constants.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"promises.js\",\n \"promises.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-fs.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-fs/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-fs#readme\",\n \"engines\": {\n \"bare\": \">=1.16.0\"\n },\n \"dependencies\": {\n \"bare-events\": \"^2.5.4\",\n \"bare-path\": \"^3.0.0\",\n \"bare-stream\": \"^2.6.4\",\n \"bare-url\": \"^2.2.2\",\n \"fast-fifo\": \"^1.3.2\"\n },\n \"devDependencies\": {\n \"bare-buffer\": \"^3.0.2\",\n \"bare-crypto\": \"^1.11.2\",\n \"brittle\": \"^3.1.1\",\n \"cmake-bare\": \"^1.1.7\",\n \"prettier\": \"^3.4.1\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"peerDependencies\": {\n \"bare-buffer\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"bare-buffer\": {\n \"optional\": true\n }\n }\n}\nconst EventEmitter = require('bare-events')\nconst fs = require('.')\n\nclass FileHandle extends EventEmitter {\n constructor(fd) {\n this.fd = fd\n }\n\n async close() {\n await fs.close(fd)\n\n this.fd = -1\n this.emit('close')\n }\n\n async read(buffer, ...args) {\n return {\n bytesRead: await fs.read(this.fd, buffer, ...args),\n buffer\n }\n }\n\n async readv(buffers, ...args) {\n return {\n bytesRead: await fs.readv(this.fd, buffers, ...args),\n buffers\n }\n }\n\n async write(buffer, ...args) {\n return {\n bytesWritten: await fs.write(this.fd, buffer, ...args),\n buffer\n }\n }\n\n async writev(buffers, ...args) {\n return {\n bytesWritten: await fs.writev(this.fd, buffers, ...args),\n buffers\n }\n }\n\n async stat() {\n return fs.fstat(this.fd)\n }\n\n async chmod(mode) {\n await fs.fchmod(this.fd, mode)\n }\n\n createReadStream(opts) {\n return fs.createReadStream(null, { ...opts, fd: this.fd })\n }\n\n createWriteStream(opts) {\n return fs.createWriteStream(null, { ...opts, fd: this.fd })\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n}\n\nexports.open = async function open(filepath, flags, mode) {\n return new FileHandle(await fs.open(filepath, flags, mode))\n}\n\nexports.access = fs.access\nexports.appendFile = fs.appendFile\nexports.chmod = fs.chmod\nexports.constants = fs.constants\nexports.copyFile = fs.copyFile\nexports.cp = fs.cp\nexports.lstat = fs.lstat\nexports.mkdir = fs.mkdir\nexports.opendir = fs.opendir\nexports.readFile = fs.readFile\nexports.readdir = fs.readdir\nexports.readlink = fs.readlink\nexports.realpath = fs.realpath\nexports.rename = fs.rename\nexports.rm = fs.rm\nexports.rmdir = fs.rmdir\nexports.stat = fs.stat\nexports.symlink = fs.symlink\nexports.unlink = fs.unlink\nexports.utimes = fs.utimes\nexports.watch = fs.watch\nexports.writeFile = fs.writeFile\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.2\",\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}\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}\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 IncomingEvent = require('./lib/incoming-event')\nconst IncomingRequest = require('./lib/incoming-request')\nconst IncomingStream = require('./lib/incoming-stream')\nconst OutgoingEvent = require('./lib/outgoing-event')\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 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 this._draining = []\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._onerror = this._onerror.bind(this)\n this._ondata = this._ondata.bind(this)\n this._ondrain = this._ondrain.bind(this)\n\n this._stream\n .on('error', this._onerror)\n .on('data', this._ondata)\n .on('drain', this._ondrain)\n }\n\n event(command) {\n return new OutgoingEvent(this, command)\n }\n\n request(command) {\n return new OutgoingRequest(this, ++this._id, command)\n }\n\n _sendMessage(message, cb) {\n const header = c.encode(m.header, message)\n\n let flushed = this._stream.write(header)\n\n if (message.data) flushed = this._stream.write(message.data)\n\n if (cb) {\n if (flushed) cb(null)\n else this._draining.push(cb)\n }\n }\n\n _sendEvent(request, data = null) {\n this._sendMessage({\n type: t.REQUEST,\n id: 0,\n command: request.command,\n stream: 0,\n data\n })\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 _onerror(err) {\n this._ondrain(err)\n\n // TODO Destroy pending requests and responses\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 =\n message.id === 0\n ? new IncomingEvent(this, message.command, message.data)\n : 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 if (message.id) this._sendError(request, err)\n else this._stream.destroy(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 _ondrain(err = null) {\n const draining = this._draining\n\n this._draining = []\n\n for (const cb of draining) cb(err)\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}\nmodule.exports = class RPCIncomingEvent {\n constructor(rpc, command, data) {\n this.rpc = rpc\n this.command = command\n this.data = data\n }\n}\nconst c = require('compact-encoding')\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 encoding =\n encoding && encoding !== 'buffer'\n ? c.from(encoding)\n : typeof data === 'string'\n ? c.raw.utf8\n : null\n\n this.sent = true\n\n this.rpc._sendResponse(this, encoding ? c.encode(encoding, data) : data)\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 {\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.OPEN,\n error: null,\n data: null\n },\n cb\n )\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 {\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 cb\n )\n } else {\n this._rpc._sendMessage(\n {\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.DESTROY,\n error: null,\n data: null\n },\n cb\n )\n }\n }\n}\nconst c = require('compact-encoding')\nconst { type: t, stream: s } = require('./constants')\nconst errors = require('./errors')\n\nconst error = {\n preencode(state, m) {\n c.utf8.preencode(state, m.message)\n c.utf8.preencode(state, String(m.code) || '')\n c.int.preencode(state, Number(m.errno) || 0)\n },\n encode(state, m) {\n c.utf8.encode(state, m.message)\n c.utf8.encode(state, String(m.code) || '')\n c.int.encode(state, Number(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\nexports.header = {\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 let hasData = false\n\n switch (m.type) {\n case t.REQUEST:\n c.uint.preencode(state, m.command)\n c.uint.preencode(state, m.stream)\n if (m.stream === 0) hasData = true\n break\n\n case t.RESPONSE:\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) hasData = true\n break\n\n case t.STREAM:\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) hasData = true\n break\n }\n\n if (hasData) c.uint.preencode(state, m.data ? m.data.byteLength : 0)\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 let hasData = false\n\n switch (m.type) {\n case t.REQUEST:\n c.uint.encode(state, m.command)\n c.uint.encode(state, m.stream)\n if (m.stream === 0) hasData = true\n break\n\n case t.RESPONSE:\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) hasData = true\n break\n\n case t.STREAM:\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) hasData = true\n break\n }\n\n if (hasData) c.uint.encode(state, m.data ? m.data.byteLength : 0)\n\n const end = state.start\n\n state.start = frame\n\n c.uint32.encode(\n state,\n end - start + (hasData && m.data ? m.data.byteLength : 0)\n )\n\n state.start = end\n }\n}\n\nexports.message = {\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 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, id, command, stream, data }\n }\n\n case t.RESPONSE: {\n const err = c.bool.decode(state)\n const stream = c.uint.decode(state)\n\n if (err) {\n return { type, id, stream, error: error.decode(state), data: null }\n }\n\n if (stream === 0) {\n return { type, id, stream, error: null, data: c.buffer.decode(state) }\n }\n\n return { type, id, stream, error: null, data: null }\n }\n\n case t.STREAM:\n const stream = c.uint.decode(state)\n\n if (stream & s.ERROR) {\n return { type, id, stream, error: error.decode(state), data: null }\n }\n\n if (stream & s.DATA) {\n return { type, id, stream, error: null, data: c.buffer.decode(state) }\n }\n\n return { type, id, stream, error: null, data: null }\n\n default:\n throw errors.UNKNOWN_MESSAGE(`Unknown message '${type}'`)\n }\n }\n}\nconst c = require('compact-encoding')\nconst errors = require('./errors')\n\nmodule.exports = class RPCOutgoingEvent {\n constructor(rpc, command) {\n this.rpc = rpc\n this.command = command\n this.sent = false\n }\n\n send(data, encoding) {\n if (this.sent) {\n throw errors.ALREADY_SENT('Event has already been sent')\n }\n\n encoding =\n encoding && encoding !== 'buffer'\n ? c.from(encoding)\n : typeof data === 'string'\n ? c.raw.utf8\n : null\n\n this.sent = true\n\n this.rpc._sendEvent(this, encoding ? c.encode(encoding, data) : data)\n }\n}\nconst c = require('compact-encoding')\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 encoding =\n encoding && encoding !== 'buffer'\n ? c.from(encoding)\n : typeof data === 'string'\n ? c.raw.utf8\n : null\n\n this.sent = true\n\n this.rpc._sendRequest(this, encoding ? c.encode(encoding, data) : data)\n }\n\n reply(encoding) {\n if (this.received) {\n throw errors.ALREADY_RECEIVED('Response is already being received')\n }\n\n encoding = encoding && encoding !== 'buffer' ? c.from(encoding) : null\n\n this.received = true\n\n return encoding\n ? this._promise.then((data) => c.decode(encoding, data))\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 const onflushed = () => {\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 switch (this._type) {\n case t.REQUEST:\n pending = this._rpc._pendingRequests\n\n this._rpc._sendMessage(\n {\n type: t.REQUEST,\n id: this._request.id,\n command: this._request.command,\n stream: s.OPEN,\n data: null\n },\n onflushed\n )\n break\n\n case t.RESPONSE:\n pending = this._rpc._pendingResponses\n\n this._rpc._sendMessage(\n {\n type: t.RESPONSE,\n id: this._request.id,\n error: false,\n stream: s.OPEN,\n data: null\n },\n onflushed\n )\n break\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 {\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.DATA,\n error: null,\n data\n },\n cb\n )\n }\n\n _final(cb) {\n this._rpc._sendMessage(\n {\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.END,\n error: null,\n data: null\n },\n cb\n )\n }\n\n _destroy(err, cb) {\n if (err) {\n this._rpc._sendMessage(\n {\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 cb\n )\n } else {\n this._rpc._sendMessage(\n {\n type: t.STREAM,\n id: this._request.id,\n stream: this._mask | s.CLOSE,\n error: null,\n data: null\n },\n cb\n )\n }\n }\n}\n{\n \"name\": \"bare-rpc\",\n \"version\": \"1.1.0\",\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}\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('finish', () => 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('finish', () => 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('finish', () => 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.7.0\",\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\": {\n \"types\": \"./web.d.ts\",\n \"default\": \"./web.js\"\n },\n \"./global\": \"./global.js\"\n },\n \"files\": [\n \"index.js\",\n \"index.d.ts\",\n \"promises.js\",\n \"web.js\",\n \"web.d.ts\",\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}\nmodule.exports = require.addon()\nconst path = require('bare-path')\nconst binding = require('./binding')\nconst errors = require('./lib/errors')\nconst URLSearchParams = require('./lib/url-search-params')\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 = String(input)\n\n if (base !== undefined) base = String(base)\n\n this._components = new Uint32Array(8)\n\n this._parse(input, base, opts.throw !== false)\n\n if (this._href) this._params = new URLSearchParams(this.search, this)\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 this._params._parse(this.search)\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(this._replace(value.replace(/:+$/, ''), 0, this._components[0]))\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(this._replace(value, this._components[0] + 3 /* :// */, this._components[1]))\n }\n\n // https://url.spec.whatwg.org/#dom-url-password\n\n get password() {\n return this._href.slice(this._components[1] + 1 /* : */, this._components[2] - 1 /* @ */)\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(value, this._components[2], this._components[value.includes(':') ? 5 : 3])\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(this._replace(value, this._components[5], this._components[6] - 1 /* ? */))\n }\n\n // https://url.spec.whatwg.org/#dom-url-search\n\n get search() {\n return this._slice(this._components[6] - 1 /* ? */, this._components[7] - 1 /* # */)\n }\n\n set search(value) {\n if (value && value[0] !== '?') value = '?' + value\n\n this._update(\n this._replace(value, this._components[6] - 1 /* ? */, this._components[7] - 1 /* # */)\n )\n\n this._params._parse(this.search)\n }\n\n // https://url.spec.whatwg.org/#dom-url-searchparams\n\n get searchParams() {\n return this._params\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 searchParams: this.searchParams,\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(input, base, shouldThrow) {\n try {\n this._href = binding.parse(\n String(input),\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(`Invalid URL '${input}'`, input)\n }\n }\n\n _update(input) {\n try {\n this._parse(input, 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\nexports.URLSearchParams = URLSearchParams\n\nexports.errors = errors\n\nexports.isURL = function isURL(value) {\n if (value instanceof URL) return true\n\n return typeof value === 'object' && value !== null && value[kind] === URL[kind]\n}\n\n// https://url.spec.whatwg.org/#dom-url-parse\nexports.parse = function parse(input, base) {\n const url = new URL(input, base, { throw: false })\n return url._href ? url : null\n}\n\n// https://url.spec.whatwg.org/#dom-url-canparse\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(\"The file: URL host must be 'localhost' or empty\")\n }\n\n if (/%2f/i.test(url.pathname)) {\n throw errors.INVALID_FILE_URL_PATH('The file: URL path must not include encoded / characters')\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 (letter < 0x61 /* a */ || letter > 0x7a /* z */ || pathname.charCodeAt(2) !== 0x3a /* : */) {\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}\n\nexports.format = function format(parts) {\n const { protocol, auth, host, hostname, port, pathname, search, query, hash, slashes } = parts\n\n let result = ''\n\n if (typeof protocol === 'string') {\n result += protocol\n\n if (protocol[protocol.length - 1] !== ':') {\n result += ':'\n }\n\n if (slashes === true || /https?|ftp|gopher|file/.test(protocol)) {\n result += '//'\n }\n }\n\n if (typeof auth === 'string') {\n if (host || hostname) result += auth + '@'\n }\n\n if (typeof host === 'string') result += host\n else {\n result += hostname\n\n if (port) result += ':' + port\n }\n\n if (typeof pathname === 'string' && pathname !== '') {\n if (pathname[0] !== '/') result += '/'\n result += pathname\n }\n\n if (typeof search === 'string') {\n if (search[0] !== '?') result += '?'\n result += search\n } else if (typeof query === 'object' && query !== null) {\n result += '?' + new URLSearchParams(query)\n }\n\n if (typeof hash === 'string') {\n if (hash[0] !== '#') result += '#'\n result += hash\n }\n\n return result\n}\nmodule.exports = class URLError extends Error {\n constructor(msg, fn = URLError, code = fn.name) {\n super(`${code}: ${msg}`)\n\n this.code = code\n\n if (Error.captureStackTrace) Error.captureStackTrace(this, fn)\n }\n\n get name() {\n return 'URLError'\n }\n\n static INVALID_URL(msg, input) {\n const err = new URLError(msg, URLError.INVALID_URL)\n\n err.input = input\n\n return err\n }\n\n static INVALID_URL_SCHEME(msg = 'Invalid URL') {\n return new URLError(msg, URLError.INVALID_URL_SCHEME)\n }\n\n static INVALID_FILE_URL_HOST(msg = 'Invalid file: URL host') {\n return new URLError(msg, URLError.INVALID_FILE_URL_HOST)\n }\n\n static INVALID_FILE_URL_PATH(msg = 'Invalid file: URL path') {\n return new URLError(msg, URLError.INVALID_FILE_URL_PATH)\n }\n}\nmodule.exports = class URLSearchParams {\n static _urls = new WeakMap()\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams\n constructor(init, url = null) {\n this._params = new Map()\n\n if (url) URLSearchParams._urls.set(this, url)\n\n if (typeof init === 'string') {\n this._parse(init)\n } else if (init) {\n for (const [name, value] of typeof init[Symbol.iterator] === 'function'\n ? init\n : Object.entries(init)) {\n this.append(name, value)\n }\n }\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-size\n get size() {\n return this._params.length\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-append\n append(name, value = null) {\n if (value === null) return\n\n let list = this._params.get(name)\n\n if (list === undefined) {\n list = []\n this._params.set(name, list)\n }\n\n list.push(value)\n\n this._update()\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-delete\n delete(name, value = null) {\n if (value === null) this._params.delete(name)\n else {\n let list = this._params.get(name)\n\n if (list === undefined) return\n\n list = list.filter((found) => found !== value)\n\n if (list.length === 0) this._params.delete(name)\n else this._params.set(name, list)\n }\n\n this._update()\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-get\n get(name) {\n const list = this._params.get(name)\n\n if (list === undefined) return null\n\n return list[0]\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-getall\n getAll(name) {\n const list = this._params.get(name)\n\n if (list === undefined) return []\n\n return Array.from(list)\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-has\n has(name, value = null) {\n const list = this._params.get(name)\n\n if (list === undefined) return false\n\n if (value === null) return true\n\n return list.includes(value)\n }\n\n // https://url.spec.whatwg.org/#dom-urlsearchparams-set\n set(name, value = null) {\n if (value === null) this._params.delete(name)\n else this._params.set(name, [value])\n\n this._update()\n }\n\n toString() {\n return this._serialize()\n }\n\n toJSON() {\n return [...this]\n }\n\n *[Symbol.iterator]() {\n for (const [name, values] of this._params) {\n for (const value of values) yield [name, value]\n }\n }\n\n [Symbol.for('bare.inspect')]() {\n const object = {\n __proto__: { constructor: URLSearchParams }\n }\n\n for (const [name, values] of this._params) {\n if (values.length === 1) object[name] = values[0]\n else object[name] = values\n }\n\n return object\n }\n\n // https://url.spec.whatwg.org/#concept-urlsearchparams-update\n _update() {\n const url = URLSearchParams._urls.get(this)\n\n if (url === undefined) return\n\n url.search = this._serialize()\n }\n\n // https://url.spec.whatwg.org/#concept-urlencoded-parser\n _parse(input) {\n if (input[0] === '?') input = input.substring(1)\n\n this._params = new Map()\n\n for (const sequence of input.split('&')) {\n if (sequence.length === 0) continue\n\n let i = sequence.indexOf('=')\n if (i === -1) i = sequence.length\n\n const name = decodeURIComponent(sequence.substring(0, i))\n const value = decodeURIComponent(sequence.substring(i + 1, sequence.length))\n\n let list = this._params.get(name)\n\n if (list === undefined) {\n list = []\n this._params.set(name, list)\n }\n\n list.push(value)\n }\n }\n\n // https://url.spec.whatwg.org/#concept-urlencoded-serializer\n _serialize() {\n let output = ''\n\n for (let [name, values] of this._params) {\n name = encodeURIComponent(name)\n\n for (const value of values) {\n if (output) output += '&'\n\n output += name + '=' + encodeURIComponent(value)\n }\n }\n\n return output\n }\n}\n{\n \"name\": \"bare-url\",\n \"version\": \"2.3.2\",\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-holepunch\": \"^2.0.0\"\n }\n}\nconst FACTOR = new Uint16Array(8)\n\nfunction factor4096 (i, n) {\n while (n > 0) {\n const f = i & 4095\n FACTOR[--n] = f\n i = (i - f) / 4096\n }\n return FACTOR\n}\n\nmodule.exports = class BigSparseArray {\n constructor () {\n this.tiny = new TinyArray()\n this.maxLength = 4096\n this.factor = 1\n }\n\n set (index, val) {\n if (val !== undefined) {\n while (index >= this.maxLength) {\n this.maxLength *= 4096\n this.factor++\n if (!this.tiny.isEmptyish()) {\n const t = new TinyArray()\n t.set(0, this.tiny)\n this.tiny = t\n }\n }\n }\n\n const f = factor4096(index, this.factor)\n const last = this.factor - 1\n\n let tiny = this.tiny\n for (let i = 0; i < last; i++) {\n const next = tiny.get(f[i])\n if (next === undefined) {\n if (val === undefined) return\n tiny = tiny.set(f[i], new TinyArray())\n } else {\n tiny = next\n }\n }\n\n return tiny.set(f[last], val)\n }\n\n get (index) {\n if (index >= this.maxLength) return\n\n const f = factor4096(index, this.factor)\n const last = this.factor - 1\n\n let tiny = this.tiny\n for (let i = 0; i < last; i++) {\n tiny = tiny.get(f[i])\n if (tiny === undefined) return\n }\n\n return tiny.get(f[last])\n }\n}\n\nclass TinyArray {\n constructor () {\n this.s = 0\n this.b = new Array(1)\n this.f = new Uint16Array(1)\n }\n\n isEmptyish () {\n return this.b.length === 1 && this.b[0] === undefined\n }\n\n get (i) {\n if (this.s === 12) return this.b[i]\n const f = i >>> this.s\n const r = i & (this.b.length - 1)\n return this.f[r] === f ? this.b[r] : undefined\n }\n\n set (i, v) {\n while (this.s !== 12) {\n const f = i >>> this.s\n const r = i & (this.b.length - 1)\n const o = this.b[r]\n\n if (o === undefined || f === this.f[r]) {\n this.b[r] = v\n this.f[r] = f\n return v\n }\n\n this.grow()\n }\n\n this.b[i] = v\n return v\n }\n\n grow () {\n const os = this.s\n const ob = this.b\n const of = this.f\n\n this.s += 4\n this.b = new Array(this.b.length << 4)\n this.f = this.s === 12 ? null : new Uint8Array(this.b.length)\n\n const m = this.b.length - 1\n\n for (let or = 0; or < ob.length; or++) {\n if (ob[or] === undefined) continue\n\n const i = of[or] << os | or\n const f = i >>> this.s\n const r = i & m\n\n this.b[r] = ob[or]\n if (this.s !== 12) this.f[r] = f\n }\n }\n}\n{\n \"name\": \"big-sparse-array\",\n \"version\": \"1.0.3\",\n \"description\": \"A sparse array optimised for low memory whilst still being fast\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"brittle\": \"^3.1.1\",\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/mafintosh/big-sparse-array.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/big-sparse-array/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/big-sparse-array\"\n}\nconst b4a = require('b4a')\n\nmodule.exports = function (a, b) {\n return new Promise((resolve, reject) => binaryEquals(a, b, resolve, reject))\n}\n\nfunction binaryEquals (a, b, resolve, reject) {\n let aBuf = null\n let aEnded = false\n\n let bBuf = null\n let bEnded = false\n\n let closed = 0\n let done = false\n let error = null\n let equals = false\n\n a.on('readable', tick)\n a.on('end', onend)\n a.on('error', onerror)\n a.on('close', onclose)\n\n b.on('readable', tick)\n b.on('end', onend)\n b.on('error', onerror)\n b.on('close', onclose)\n\n function onerror (err) {\n error = err\n a.destroy()\n b.destroy()\n }\n\n function onclose () {\n if (++closed !== 2) return\n if (error !== null && done === false) reject(error)\n else resolve(equals)\n }\n\n function ondone (eq) {\n if (done) return\n done = true\n\n equals = eq\n\n a.destroy()\n b.destroy()\n }\n\n function onend () {\n if (this === a) aEnded = true\n else bEnded = true\n tick()\n }\n\n function tick () {\n while (done === false) {\n if (aBuf === null) aBuf = a.read()\n if (bBuf === null) bBuf = b.read()\n\n if (aBuf === null && bBuf === null && aEnded && bEnded) {\n ondone(true)\n return\n }\n\n if (aBuf !== null && (bBuf === null && bEnded)) {\n ondone(false)\n return\n }\n\n if (bBuf !== null && (aBuf === null && aEnded)) {\n ondone(false)\n return\n }\n\n if (aBuf === null || bBuf === null) return // read pending\n\n if (aBuf.byteLength === bBuf.byteLength) {\n if (b4a.equals(aBuf, bBuf)) {\n aBuf = bBuf = null\n continue\n }\n\n ondone(false)\n return\n }\n\n const min = Math.min(aBuf.byteLength, bBuf.byteLength)\n\n if (b4a.equals(aBuf.subarray(0, min), bBuf.subarray(0, min))) {\n aBuf = aBuf.byteLength === min ? null : aBuf.subarray(min)\n bBuf = bBuf.byteLength === min ? null : bBuf.subarray(min)\n continue\n }\n\n ondone(false)\n return\n }\n }\n}\n{\n \"name\": \"binary-stream-equals\",\n \"version\": \"1.0.0\",\n \"description\": \"Check if two binary streams have the same content\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.3.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^2.2.7\",\n \"standard\": \"^16.0.4\",\n \"streamx\": \"^2.12.4\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/binary-stream-equals.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/binary-stream-equals/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/binary-stream-equals\"\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ð <kasper@funktionel.co>\",\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}\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}\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\nexports.isReserved = function isReserved (ip) {\n return isReservedIP(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.2.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}\nconst b4a = require('b4a')\n\nmodule.exports = codecs\n\ncodecs.ascii = createString('ascii')\ncodecs.utf8 = createString('utf-8')\ncodecs.hex = createString('hex')\ncodecs.base64 = createString('base64')\ncodecs.ucs2 = createString('ucs2')\ncodecs.utf16le = createString('utf16le')\ncodecs.ndjson = createJSON(true)\ncodecs.json = createJSON(false)\ncodecs.binary = {\n name: 'binary',\n encode: function encodeBinary (obj) {\n return typeof obj === 'string'\n ? b4a.from(obj, 'utf-8')\n : b4a.toBuffer(obj)\n },\n decode: function decodeBinary (buf) {\n return b4a.toBuffer(buf)\n }\n}\n\nfunction isCompactEncoding (c) {\n return !!(c.encode && c.decode && c.preencode)\n}\n\nfunction fromCompactEncoding (c) {\n return {\n name: 'compact-encoding',\n encode: function encodeWithCompact (value) {\n const state = { start: 0, end: 0, buffer: null, cache: null }\n c.preencode(state, value)\n state.buffer = b4a.allocUnsafe(state.end)\n c.encode(state, value)\n return state.buffer\n },\n decode: function decodeWithCompact (buffer) {\n return c.decode({ start: 0, end: buffer.byteLength, buffer, cache: null })\n }\n }\n}\n\nfunction codecs (fmt, fallback) {\n if (typeof fmt === 'object' && fmt) {\n return isCompactEncoding(fmt) ? fromCompactEncoding(fmt) : fmt\n }\n\n switch (fmt) {\n case 'ndjson': return codecs.ndjson\n case 'json': return codecs.json\n case 'ascii': return codecs.ascii\n case 'utf-8':\n case 'utf8': return codecs.utf8\n case 'hex': return codecs.hex\n case 'base64': return codecs.base64\n case 'ucs-2':\n case 'ucs2': return codecs.ucs2\n case 'utf16-le':\n case 'utf16le': return codecs.utf16le\n }\n\n return fallback !== undefined ? fallback : codecs.binary\n}\n\nfunction createJSON (newline) {\n return {\n name: newline ? 'ndjson' : 'json',\n encode: newline ? encodeNDJSON : encodeJSON,\n decode: function decodeJSON (buf) {\n return JSON.parse(b4a.toString(buf))\n }\n }\n\n function encodeJSON (val) {\n return b4a.from(JSON.stringify(val))\n }\n\n function encodeNDJSON (val) {\n return b4a.from(JSON.stringify(val) + '\\n')\n }\n}\n\nfunction createString (type) {\n return {\n name: type,\n encode: function encodeString (val) {\n if (typeof val !== 'string') val = val.toString()\n return b4a.from(val, type)\n },\n decode: function decodeString (buf) {\n return b4a.toString(buf, type)\n }\n }\n}\n{\n \"name\": \"codecs\",\n \"version\": \"3.1.0\",\n \"description\": \"Create an binary encoder/decoder for json, utf-8 or custom types\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.6.3\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.2.1\",\n \"compact-encoding\": \"^2.11.0\",\n \"standard\": \"^17.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/codecs.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/codecs/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/codecs\"\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ð <kasper@funktionel.co>\",\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}\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ð <kasper@funktionel.co>\",\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}\nconst LE = (exports.LE =\n 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 }\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 state.buffer[state.start++] + state.buffer[state.start++] * 0x100\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8 * len\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8 * len\n )\n let n = 0n\n for (let i = len - 1; i >= 0; i--)\n 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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 4\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 4\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8\n )\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(\n state.buffer.buffer,\n state.start + state.buffer.byteOffset,\n 8\n )\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(\n state.buffer,\n encoding,\n state.start,\n (state.start += len)\n )\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(\n state.buffer,\n encoding,\n state.start,\n (state.start += n)\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\nconst port = (exports.port = 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 = (exports.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 (\n i < string.length &&\n (c = string.charCodeAt(i++)) !== /* . */ 0x2e\n ) {\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 '.' +\n state.buffer[state.start++] +\n '.' +\n state.buffer[state.start++] +\n '.' +\n state.buffer[state.start++]\n )\n }\n})\n\nexports.ipv4Address = address(ipv4, 4)\n\nconst ipv6 = (exports.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 (\n i < string.length &&\n (c = string.charCodeAt(i++)) !== /* : */ 0x3a\n ) {\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 (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16) +\n ':' +\n (\n state.buffer[state.start++] * 256 +\n state.buffer[state.start++]\n ).toString(16)\n )\n }\n})\n\nexports.ipv6Address = address(ipv6, 6)\n\nconst ip = (exports.ip = {\n preencode(state, string) {\n const family = string.includes(':') ? 6 : 4\n 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 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 = uint8.decode(state)\n if (family === 4) return ipv4.decode(state)\n else return ipv6.decode(state)\n }\n})\n\nexports.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 = 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\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':\n return raw.ascii\n case 'utf-8':\n case 'utf8':\n return raw.utf8\n case 'hex':\n return raw.hex\n case 'base64':\n return raw.base64\n case 'utf16-le':\n case 'utf16le':\n case 'ucs-2':\n case 'ucs2':\n return raw.ucs2\n case 'ndjson':\n return raw.ndjson\n case 'json':\n return raw.json\n case 'binary':\n default:\n 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 */)\n 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++] + max\n }\n\n if (flag < 253) {\n return (\n (state.buffer[state.start++] << 8) + state.buffer[state.start++] + max\n )\n }\n\n if (flag < 254) {\n return (\n (state.buffer[state.start++] << 16) +\n (state.buffer[state.start++] << 8) +\n state.buffer[state.start++] +\n max\n )\n }\n\n // << 24 result may be interpreted as negative\n if (flag < 255) {\n return (\n 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\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.18.0\",\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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier . --write\",\n \"test\": \"prettier . --check && 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}\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 b4a = require('b4a')\nconst Hypercore = require('hypercore')\nconst ReadyResource = require('ready-resource')\nconst sodium = require('sodium-universal')\nconst crypto = require('hypercore-crypto')\nconst ID = require('hypercore-id-encoding')\nconst { isAndroid } = require('which-runtime')\nconst { STORAGE_EMPTY, ASSERTION } = require('hypercore-errors')\n\nconst auditStore = require('./lib/audit.js')\n\nconst [NS] = crypto.namespace('corestore', 1)\nconst DEFAULT_NAMESPACE = b4a.alloc(32) // This is meant to be 32 0-bytes\n\nclass StreamTracker {\n constructor() {\n this.records = []\n }\n\n add(stream, isExternal) {\n const record = { index: 0, stream, isExternal }\n record.index = this.records.push(record) - 1\n return record\n }\n\n remove(record) {\n const popped = this.records.pop()\n if (popped === record) return\n this.records[(popped.index = record.index)] = popped\n }\n\n attachAll(core) {\n for (let i = 0; i < this.records.length; i++) {\n const record = this.records[i]\n const muxer = record.stream.noiseStream.userData\n if (!core.replicator.attached(muxer)) core.replicator.attachTo(muxer)\n }\n }\n\n destroy() {\n // reverse is safer cause we delete mb\n for (let i = this.records.length - 1; i >= 0; i--) {\n const record = this.records[i]\n if (!record.isExternal) record.stream.destroy()\n }\n }\n}\n\nclass SessionTracker {\n constructor() {\n this.map = new Map()\n }\n\n get size() {\n return this.map.size\n }\n\n get(id) {\n const existing = this.map.get(id)\n if (existing !== undefined) return existing\n const fresh = []\n this.map.set(id, fresh)\n return fresh\n }\n\n gc(id) {\n this.map.delete(id)\n }\n\n list(id) {\n return id ? this.map.get(id) || [] : [...this]\n }\n\n *[Symbol.iterator]() {\n for (const sessions of this.map.values()) {\n yield* sessions[Symbol.iterator]()\n }\n }\n}\n\nclass CoreTracker {\n constructor() {\n this.map = new Map()\n this.watching = []\n\n this._gcing = new Set()\n this._gcInterval = null\n this._gcCycleBound = this._gcCycle.bind(this)\n }\n\n get size() {\n return this.map.size\n }\n\n watch(store) {\n if (store.watchIndex !== -1) return\n store.watchIndex = this.watching.push(store) - 1\n }\n\n unwatch(store) {\n if (store.watchIndex === -1) return\n const head = this.watching.pop()\n if (head !== store) this.watching[(head.watchIndex = store.watchIndex)] = head\n store.watchIndex = -1\n }\n\n resume(id) {\n const core = this.map.get(id)\n\n if (!core) return null\n\n // signal back that we have a closing one stored\n if (core.closing) return core\n\n if (core.gc) {\n this._gcing.delete(core)\n if (this._gcing.size === 0) this._stopGC()\n core.gc = 0\n }\n\n return core\n }\n\n opened(id) {\n const core = this.map.get(id)\n return !!(core && core.opened && !core.closing)\n }\n\n get(id) {\n // we allow you do call this from the outside, so support normal buffers also\n if (b4a.isBuffer(id)) id = b4a.toString(id, 'hex')\n const core = this.map.get(id)\n if (!core || core.closing) return null\n return core\n }\n\n set(id, core) {\n this.map.set(id, core)\n if (this.watching.length > 0) this._emit(core)\n }\n\n _emit(core) {\n for (let i = this.watching.length - 1; i >= 0; i--) {\n const store = this.watching[i]\n for (const fn of store.watchers) fn(core)\n }\n }\n\n _gc(core) {\n const id = toHex(core.discoveryKey)\n if (this.map.get(id) === core) this.map.delete(id)\n }\n\n _gcCycle() {\n for (const core of this._gcing) {\n if (++core.gc < 4) continue\n const gc = this._gc.bind(this, core)\n core.close().then(gc, gc)\n this._gcing.delete(core)\n }\n\n if (this._gcing.size === 0) this._stopGC()\n }\n\n gc(core) {\n core.gc = 1 // first strike\n this._gcing.add(core)\n if (this._gcing.size === 1) this._startGC()\n }\n\n _stopGC() {\n clearInterval(this._gcInterval)\n this._gcInterval = null\n }\n\n _startGC() {\n if (this._gcInterval) return\n this._gcInterval = setInterval(this._gcCycleBound, 2000)\n if (this._gcInterval.unref) this._gcInterval.unref()\n }\n\n close() {\n this._stopGC()\n this._gcing.clear()\n\n const all = []\n for (const core of this.map.values()) {\n core.onidle = noop // no reentry\n all.push(core.close())\n }\n this.map.clear()\n\n return Promise.all(all)\n }\n\n *[Symbol.iterator]() {\n for (const core of this.map.values()) {\n if (!core.closing) yield core\n }\n }\n}\n\nclass FindingPeers {\n constructor() {\n this.count = 0\n this.pending = []\n }\n\n add(core) {\n if (this.count === 0) return\n this.pending.push(core.findingPeers())\n }\n\n inc(sessions) {\n if (++this.count !== 1) return\n\n for (const core of sessions) {\n this.pending.push(core.findingPeers())\n }\n }\n\n dec(sessions) {\n if (--this.count !== 0) return\n while (this.pending.length > 0) this.pending.pop()()\n }\n}\n\nclass Corestore extends ReadyResource {\n constructor(storage, opts = {}) {\n super()\n\n this.root = opts.root || null\n this.storage = this.root\n ? this.root.storage\n : Hypercore.defaultStorage(storage, {\n id: opts.id,\n allowBackup: opts.allowBackup,\n readOnly: opts.readOnly,\n wait: opts.wait\n })\n this.streamTracker = this.root ? this.root.streamTracker : new StreamTracker()\n this.cores = this.root ? this.root.cores : new CoreTracker()\n this.sessions = new SessionTracker()\n this.corestores = this.root ? this.root.corestores : new Set()\n this.readOnly = opts.writable === false || !!opts.readOnly\n this.globalCache = this.root ? this.root.globalCache : opts.globalCache || null\n this.primaryKey = this.root ? this.root.primaryKey : opts.primaryKey || null\n this.ns = opts.namespace || DEFAULT_NAMESPACE\n this.manifestVersion = opts.manifestVersion || 1\n this.shouldSuspend = isAndroid ? !!opts.suspend : opts.suspend !== false\n this.active = opts.active !== false\n\n this.watchers = null\n this.watchIndex = -1\n\n this._findingPeers = null // here for legacy\n this._ongcBound = this._ongc.bind(this)\n\n if (opts.primaryKey && !this.root && !opts.unsafe) {\n throw ASSERTION(\n 'Passing the primary key is unsafe unless you know what you are doing. Set unsafe: true to acknowledge that'\n )\n }\n\n if (this.root) this.corestores.add(this)\n\n this.ready().catch(noop)\n }\n\n watch(fn) {\n if (this.watchers === null) {\n this.watchers = new Set()\n this.cores.watch(this)\n }\n\n this.watchers.add(fn)\n }\n\n unwatch(fn) {\n if (this.watchers === null) return\n\n this.watchers.delete(fn)\n\n if (this.watchers.size === 0) {\n this.watchers = null\n this.cores.unwatch(this)\n }\n }\n\n findingPeers() {\n if (this._findingPeers === null) this._findingPeers = new FindingPeers()\n this._findingPeers.inc(this.sessions)\n let done = false\n return () => {\n if (done) return\n done = true\n this._findingPeers.dec(this.sessions)\n }\n }\n\n audit(opts = {}) {\n return auditStore(this, opts)\n }\n\n async suspend({ log = noop } = {}) {\n await log('Flushing db...')\n // If readOnly we don't need to flush\n if (!this.storage.readOnly) await this.storage.db.flush()\n if (!this.shouldSuspend) return\n await log('Suspending db...')\n await this.storage.suspend()\n }\n\n resume() {\n return this.storage.resume()\n }\n\n session(opts) {\n this._maybeClosed()\n const root = this.root || this\n return new Corestore(null, {\n manifestVersion: this.manifestVersion,\n ...opts,\n root\n })\n }\n\n namespace(name, opts) {\n return this.session({\n ...opts,\n namespace: generateNamespace(this.ns, name)\n })\n }\n\n list(namespace) {\n return this.storage.createDiscoveryKeyStream(namespace)\n }\n\n getAuth(discoveryKey) {\n return this.storage.getAuth(discoveryKey)\n }\n\n _ongc(session) {\n if (session.sessions.length === 0) this.sessions.gc(session.id)\n }\n\n async _getOrSetSeed() {\n const seed = await this.storage.getSeed()\n if (seed !== null) return seed\n return await this.storage.setSeed(this.primaryKey || crypto.randomBytes(32))\n }\n\n async _open() {\n if (this.root !== null) {\n if (this.root.opened === false) await this.root.ready()\n this.primaryKey = this.root.primaryKey\n return\n }\n\n const primaryKey = await this._getOrSetSeed()\n\n if (this.primaryKey === null) {\n this.primaryKey = primaryKey\n return\n }\n\n if (!b4a.equals(primaryKey, this.primaryKey)) {\n throw new Error('Another corestore is stored here')\n }\n }\n\n async _close() {\n const closing = []\n const hanging = [...this.sessions]\n for (const sess of hanging) closing.push(sess.close())\n\n if (this.watchers !== null) this.cores.unwatch(this)\n\n if (this.root !== null) {\n await Promise.all(closing)\n return\n }\n\n for (const store of this.corestores) {\n closing.push(store.close())\n }\n\n await Promise.all(closing)\n\n await this.cores.close()\n await this.storage.close()\n }\n\n async _attachMaybe(muxer, discoveryKey) {\n if (this.opened === false) await this.ready()\n if (\n !this.cores.opened(toHex(discoveryKey)) &&\n !(await this.storage.hasCore(discoveryKey, { ifMigrated: true }))\n ) {\n return\n }\n if (this.closing) return\n\n const core = this._openCore(discoveryKey, { createIfMissing: false })\n\n if (!core) return\n if (!core.opened) await core.ready()\n\n if (!core.replicator.attached(muxer)) {\n core.replicator.attachTo(muxer)\n }\n\n core.checkIfIdle()\n }\n\n _shouldReplicate(core, muxer) {\n return (\n core.replicator.downloading && !core.replicator.attached(muxer) && core.opened && this.active\n )\n }\n\n replicate(isInitiator, opts) {\n this._maybeClosed()\n\n const isExternal = isStream(isInitiator)\n const stream = Hypercore.createProtocolStream(isInitiator, {\n ...opts,\n ondiscoverykey: (discoveryKey) => {\n if (this.closing) return\n const muxer = stream.noiseStream.userData\n return this._attachMaybe(muxer, discoveryKey)\n }\n })\n\n if (this.cores.size > 0) {\n const muxer = stream.noiseStream.userData\n const uncork = muxer.uncork.bind(muxer)\n muxer.cork()\n\n for (const core of this.cores) {\n if (!this._shouldReplicate(core, muxer)) {\n continue\n }\n core.replicator.attachTo(muxer)\n }\n\n stream.noiseStream.opened.then(uncork)\n }\n\n const record = this.streamTracker.add(stream, isExternal)\n stream.once('close', () => this.streamTracker.remove(record))\n return stream\n }\n\n _maybeClosed() {\n if (this.closing || (this.root !== null && this.root.closing)) {\n throw new Error('Corestore is closed')\n }\n }\n\n async staticify(core, opts) {\n if (!this.opened) await this.ready()\n if (!core.opened) await core.ready()\n\n const rx = core.state.storage.read()\n\n const headPromise = rx.getHead()\n const authPromise = rx.getAuth()\n\n rx.tryFlush()\n\n const [head, auth] = await Promise.all([headPromise, authPromise])\n if (!head || head.length === 0) throw new Error('Core must have data')\n\n const prologue = {\n length: head.length,\n hash: head.rootHash\n }\n\n const manifest = {\n version: 1,\n hash: auth.manifest.hash,\n quorum: 0,\n signers: [],\n prologue\n }\n\n const c = {\n key: null,\n discoveryKey: null,\n manifest,\n core: core.state.storage.core\n }\n\n c.key = Hypercore.key(c.manifest)\n c.discoveryKey = Hypercore.discoveryKey(c.key)\n\n await this.storage.createCore(c)\n\n const staticCore = this.get({ ...opts, key: c.key })\n await staticCore.ready()\n return staticCore\n }\n\n get(opts) {\n this._maybeClosed()\n\n if (b4a.isBuffer(opts) || typeof opts === 'string') opts = { key: opts }\n if (!opts) opts = {}\n\n const conf = {\n preload: null,\n sessions: null,\n ongc: null,\n core: null,\n active: opts.active !== false,\n encryption: opts.encryption || null,\n encryptionKey: opts.encryptionKey || null, // back compat, should remove\n isBlockKey: !!opts.isBlockKey, // back compat, should remove\n valueEncoding: opts.valueEncoding || null,\n exclusive: !!opts.exclusive,\n manifest: opts.manifest || null,\n keyPair: opts.keyPair || null,\n onwait: opts.onwait || null,\n pushOnly: opts.pushOnly === true,\n wait: opts.wait !== false,\n timeout: opts.timeout || 0,\n draft: !!opts.draft,\n writable: opts.writable === undefined && this.readOnly ? false : opts.writable\n }\n\n // name requires us to rt to storage + ready, so needs preload\n // same goes if user has defined async preload obvs\n if (opts.name || opts.preload) {\n conf.preload = this._preload(opts)\n return this._makeSession(conf)\n }\n\n if (opts.discoveryKey && !opts.key && !opts.manifest) {\n conf.preload = this._preloadCheckIfExists(opts)\n return this._makeSession(conf)\n }\n\n // if not not we can sync create it, which just is easier for the\n // upstream user in terms of guarantees (key is there etc etc)\n const core = this._openCore(null, opts)\n\n conf.core = core\n conf.sessions = this.sessions.get(core.id)\n conf.ongc = this._ongcBound\n\n return this._makeSession(conf)\n }\n\n _makeSession(conf) {\n const session = new Hypercore(null, null, conf)\n if (this._findingPeers !== null) this._findingPeers.add(session)\n return session\n }\n\n async createKeyPair(name, ns = this.ns) {\n if (this.opened === false) await this.ready()\n return createKeyPair(this.primaryKey, ns, name)\n }\n\n async _preloadCheckIfExists(opts) {\n const has = await this.storage.hasCore(opts.discoveryKey)\n if (!has) throw STORAGE_EMPTY('No Hypercore is stored here')\n return this._preload(opts)\n }\n\n async _preload(opts) {\n if (opts.preload) opts = { ...opts, ...(await opts.preload) }\n if (this.opened === false) await this.ready()\n\n const discoveryKey = opts.name\n ? await this.storage.getAlias({ name: opts.name, namespace: this.ns })\n : null\n this._maybeClosed()\n\n const core = this._openCore(discoveryKey, opts)\n\n return {\n core,\n sessions: this.sessions.get(core.id),\n ongc: this._ongcBound,\n encryption: opts.encryption || null,\n encryptionKey: opts.encryptionKey || null, // back compat, should remove\n isBlockKey: !!opts.isBlockKey // back compat, should remove\n }\n }\n\n _auth(discoveryKey, opts) {\n const result = {\n keyPair: null,\n key: null,\n discoveryKey,\n manifest: null\n }\n\n if (opts.name) {\n result.keyPair = createKeyPair(this.primaryKey, this.ns, opts.name)\n } else if (opts.keyPair) {\n result.keyPair = opts.keyPair\n }\n\n if (opts.manifest) {\n result.manifest = opts.manifest\n } else if (result.keyPair && !result.discoveryKey) {\n result.manifest = {\n version: this.manifestVersion,\n signers: [{ publicKey: result.keyPair.publicKey }]\n }\n }\n\n if (opts.key) result.key = ID.decode(opts.key)\n else if (result.manifest) result.key = Hypercore.key(result.manifest)\n\n if (result.discoveryKey) return result\n\n if (opts.discoveryKey) result.discoveryKey = ID.decode(opts.discoveryKey)\n else if (result.key) result.discoveryKey = crypto.discoveryKey(result.key)\n else throw new Error('Could not derive discovery from input')\n\n return result\n }\n\n _openCore(discoveryKey, opts) {\n const auth = this._auth(discoveryKey, opts)\n\n const id = toHex(auth.discoveryKey)\n const existing = this.cores.resume(id)\n if (existing && !existing.closing) return existing\n\n const core = Hypercore.createCore(this.storage, {\n preopen: existing && existing.opened ? existing.closing : null, // always wait for the prev one to close first in any case...\n eagerUpgrade: true,\n notDownloadingLinger: opts.notDownloadingLinger,\n allowFork: opts.allowFork !== false,\n inflightRange: opts.inflightRange,\n compat: false, // no compat for now :)\n force: opts.force,\n createIfMissing: opts.createIfMissing,\n discoveryKey: auth.discoveryKey,\n overwrite: opts.overwrite,\n key: auth.key,\n keyPair: auth.keyPair,\n legacy: opts.legacy,\n manifest: auth.manifest,\n globalCache: opts.globalCache || this.globalCache || null,\n alias: opts.name ? { name: opts.name, namespace: this.ns } : null\n })\n\n core.onidle = () => {\n this.cores.gc(core)\n }\n\n core.replicator.ondownloading = () => {\n if (this.active) this.streamTracker.attachAll(core)\n }\n\n this.cores.set(id, core)\n return core\n }\n}\n\nmodule.exports = Corestore\n\nfunction isStream(s) {\n return typeof s === 'object' && s && typeof s.pipe === 'function'\n}\n\nfunction generateNamespace(namespace, name) {\n if (!b4a.isBuffer(name)) name = b4a.from(name)\n const out = b4a.allocUnsafeSlow(32)\n sodium.crypto_generichash_batch(out, [namespace, name])\n return out\n}\n\nfunction deriveSeed(primaryKey, namespace, name) {\n if (!b4a.isBuffer(name)) name = b4a.from(name)\n const out = b4a.alloc(32)\n sodium.crypto_generichash_batch(out, [NS, namespace, name], primaryKey)\n return out\n}\n\nfunction createKeyPair(primaryKey, namespace, name) {\n const seed = deriveSeed(primaryKey, namespace, name)\n const buf = b4a.alloc(sodium.crypto_sign_PUBLICKEYBYTES + sodium.crypto_sign_SECRETKEYBYTES)\n const keyPair = {\n publicKey: buf.subarray(0, sodium.crypto_sign_PUBLICKEYBYTES),\n secretKey: buf.subarray(sodium.crypto_sign_PUBLICKEYBYTES)\n }\n sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed)\n return keyPair\n}\n\nfunction noop() {}\n\nfunction toHex(discoveryKey) {\n return b4a.toString(discoveryKey, 'hex')\n}\nmodule.exports = async function* audit(store, { dryRun = false } = {}) {\n for await (const { discoveryKey, core } of store.storage.createCoreStream()) {\n if (core.version < 1) continue // not migrated, ignore\n\n const c = store.get({ discoveryKey, active: false })\n await c.ready()\n\n yield { discoveryKey, key: c.key, audit: await c.core.audit({ dryRun }) }\n\n try {\n await c.close()\n } catch {\n // ignore if failed, we are auditing...\n }\n }\n}\n{\n \"name\": \"corestore\",\n \"version\": \"7.8.0\",\n \"description\": \"A Hypercore factory that simplifies managing collections of cores.\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/*\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.6.7\",\n \"hypercore\": \"^11.19.0\",\n \"hypercore-crypto\": \"^3.4.2\",\n \"hypercore-errors\": \"^1.4.0\",\n \"hypercore-id-encoding\": \"^1.3.0\",\n \"ready-resource\": \"^1.1.1\",\n \"sodium-universal\": \"^5.0.1\",\n \"which-runtime\": \"^1.2.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.7.0\",\n \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"rache\": \"^1.0.0\",\n \"test-tmp\": \"^1.3.0\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:bare\": \"brittle-bare --coverage test/*.js\",\n \"lint\": \"prettier --check . && lunte\",\n \"test:node\": \"brittle-node --coverage test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/corestore.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/corestore/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/corestore\"\n}\nmodule.exports = function debounce (worker, context = null) {\n debounced.running = null\n return debounced\n\n async function debounced () {\n if (debounced.running !== null) {\n try {\n await debounced.running\n } catch (_) {\n // ignore - do not fail on old errors\n }\n }\n\n // another \"thread\" beat us to it, just piggy pack on that one\n if (debounced.running !== null) return debounced.running\n\n debounced.running = worker.call(context)\n\n try {\n return await debounced.running\n } finally {\n debounced.running = null\n }\n }\n}\n{\n \"name\": \"debounceify\",\n \"version\": \"1.1.0\",\n \"description\": \"Tiny async debouncer\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^16.0.3\",\n \"tape\": \"^5.1.1\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/debounceify.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/debounceify/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/debounceify\"\n}\nconst fs = require('fs')\nconst path = require('path')\nconst fsx = require('fs-native-extensions')\nconst b4a = require('b4a')\nconst ReadyResource = require('ready-resource')\nconst FDLock = require('fd-lock')\n\nconst PLATFORM = global.Bare ? global.Bare.platform : global.process.platform\nconst IS_WIN = PLATFORM === 'win32'\nconst IS_LINUX = PLATFORM === 'linux'\nconst MODIFIED_SLACK = 5000\nconst EMPTY = b4a.alloc(0)\nconst ATTR = IS_LINUX ? 'user.device-file' : 'device-file'\n\nconst nl = IS_WIN ? '\\r\\n' : '\\n'\n\nclass DeviceFile extends ReadyResource {\n constructor(filename, { create = true, wait = false, lock = wait, data = {} } = {}) {\n super()\n\n this.filename = filename\n this.data = data\n this.lock = null\n\n this._create = create\n this._wait = wait\n this._lock = lock\n }\n\n async _open() {\n if (await verifyDeviceFile(this)) return\n if (!this._create) throwDeviceFileError('No device file present', false)\n await writeDeviceFile(this)\n }\n\n static async validate(filename, data) {\n const file = new DeviceFile(filename, { create: false, wait: false, data })\n try {\n await verifyDeviceFile(file)\n } catch (err) {\n if (err.fatal && err.code === 'DEVICE_FILE') return false\n throw err\n }\n return true\n }\n\n transfer() {\n return this.lock ? this.lock.transfer() : -1\n }\n\n async _close() {\n if (this.lock) await this.lock.close()\n }\n\n async suspend() {\n if (!this.opened) await this.ready()\n if (this.lock) await this.lock.suspend()\n }\n\n async resume() {\n if (!this.opened) await this.ready()\n if (this.lock) await this.lock.resume()\n }\n}\n\nmodule.exports = DeviceFile\n\nasync function writeDeviceFile(device) {\n let s = ''\n\n for (const [key, value] of Object.entries(device.data)) {\n if (value === null) continue\n s += key + '=' + value + nl\n }\n\n await fs.promises.mkdir(path.dirname(device.filename), { recursive: true })\n\n const fd = await open(device.filename, 'w')\n\n device.lock = device._lock ? new FDLock(fd, { wait: device._wait }) : null\n\n if (device.lock) {\n try {\n await device.lock.ready()\n } catch (err) {\n await device.lock.close()\n throw err\n }\n }\n\n const st = await fstat(fd)\n\n const created = Date.now()\n\n s += 'device/platform=' + PLATFORM + nl\n s += 'device/inode=' + st.ino + nl\n s += 'device/created=' + created + nl\n\n if (await setAttr(fd, ATTR, b4a.from('original'))) {\n s += 'device/attribute=original' + nl\n }\n\n await write(fd, b4a.from(s))\n\n if (!device.lock) await close(fd)\n}\n\nasync function verifyDeviceFile(device) {\n let fd = 0\n\n try {\n fd = await open(device.filename, 'r+')\n } catch (e) {\n fd = 0\n }\n\n if (fd === 0) return false\n\n device.lock = device._lock ? new FDLock(fd, { wait: device._wait }) : null\n\n if (device.lock) {\n try {\n await device.lock.ready()\n } catch (err) {\n await device.lock.close()\n throw err\n }\n }\n\n const buf = await read(fd)\n const result = {}\n\n const s = b4a.toString(buf).trim().split('\\n')\n\n let inode = 0\n let created = 0\n let attr = ''\n let platform = ''\n\n for (const ln of s) {\n const i = ln.indexOf('=')\n if (i === -1) continue\n\n const k = ln.slice(0, i).trim()\n const v = ln.slice(i + 1).trim()\n\n switch (k) {\n case 'device/platform':\n platform = v\n break\n case 'device/inode':\n inode = Number(v)\n break\n case 'device/created':\n created = Number(v)\n break\n case 'device/attribute':\n attr = v\n break\n default:\n result[k] = v\n break\n }\n }\n\n for (const [k, v] of Object.entries(device.data)) {\n if (v === null) continue\n if (result[k] === undefined) continue // allow upserts\n if (result[k] !== '' + v) {\n await teardown()\n throwDeviceFileError(`Invalid device file, ${k} has changed. Was ${result[k]}, is ${v}`, true)\n }\n }\n\n const st = await fstat(fd)\n const at = await getAttr(fd, ATTR)\n\n const sameAttr = b4a.toString(at || EMPTY) === attr\n const modified = Math.max(st.mtime.getTime(), st.birthtime.getTime())\n\n if (platform && platform !== PLATFORM) {\n await teardown()\n throwDeviceFileError('Invalid device file, was made on different platform', true)\n }\n\n if (!sameAttr) {\n await teardown()\n throwDeviceFileError('Invalid device file, was moved unsafely', true)\n }\n\n if (st.ino !== inode || (created && Math.abs(modified - created) >= MODIFIED_SLACK)) {\n await teardown()\n throwDeviceFileError('Invalid device file, was modified', true)\n }\n\n if (!device.lock) await close(fd)\n\n device.data = result\n return true\n\n async function teardown() {\n if (device.lock) await device.lock.close()\n else await close(fd)\n }\n}\n\nasync function getAttr(fd, name) {\n try {\n return await fsx.getAttr(fd, name)\n } catch {\n return null\n }\n}\n\nasync function setAttr(fd, name, value) {\n try {\n await fsx.setAttr(fd, name, value)\n return true\n } catch {\n return false\n }\n}\n\nfunction fstat(fd) {\n return new Promise((resolve, reject) => {\n fs.fstat(fd, (err, st) => {\n if (err) reject(err)\n resolve(st)\n })\n })\n}\n\nfunction close(fd) {\n return new Promise((resolve, reject) => {\n fs.close(fd, (err, st) => {\n if (err) reject(err)\n resolve(st)\n })\n })\n}\n\nfunction write(fd, buf) {\n return new Promise((resolve, reject) => {\n let offset = 0\n\n onwrite(null, 0)\n\n function onwrite(err, wrote) {\n if (err) return reject(err)\n if (offset === buf.byteLength) return resolve()\n offset += wrote\n fs.write(fd, buf, offset, buf.byteLength - offset, offset, onwrite)\n }\n })\n}\n\nfunction read(fd) {\n const buf = b4a.allocUnsafe(4096)\n\n return new Promise((resolve, reject) => {\n let offset = 0\n\n fs.read(fd, buf, 0, buf.byteLength, 0, onread)\n\n function onread(err, read) {\n if (err) return reject(err)\n if (read === 0) return resolve(buf.subarray(0, offset))\n offset += read\n fs.read(fd, buf, offset, buf.byteLength - offset, offset, onread)\n }\n })\n}\n\nfunction open(filename, flags) {\n return new Promise((resolve, reject) => {\n fs.open(filename, flags, (err, fd) => {\n if (err) reject(err)\n resolve(fd)\n })\n })\n}\n\nfunction throwDeviceFileError(message, fatal) {\n const err = new Error(message)\n err.code = 'DEVICE_FILE'\n err.fatal = fatal\n throw err\n}\n{\n \"name\": \"device-file\",\n \"version\": \"2.3.1\",\n \"description\": \"Device only file\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.6.7\",\n \"bare-fs\": \"^4.0.1\",\n \"bare-path\": \"^3.0.0\",\n \"fd-lock\": \"^2.1.0\",\n \"fs-native-extensions\": \"^1.4.0\",\n \"ready-resource\": \"^1.2.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.13.1\",\n \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:node\": \"brittle-node --coverage test.js\",\n \"test:bare\": \"brittle-bare --coverage test.js\"\n },\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/device-file.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/device-file/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/device-file\"\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 NetworkHealth = require('./lib/health')\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, DELAYED_PING } = 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\nconst DEFAULTS = {\n concurrency: 10,\n maxWindow: IO.DEFAULT_MAX_WINDOW,\n maxHealthWindow: NetworkHealth.DEFAULT_MAX_HEALTH_WINDOW,\n maxPingDelay: 10_000\n}\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 this.health = new NetworkHealth(this, opts)\n\n this.concurrency = opts.concurrency || DEFAULTS.concurrency\n this.maxPingDelay = opts.maxPingDelay || DEFAULTS.maxPingDelay\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.degraded = false\n this.stats = {\n queries: { active: 0, total: 0 },\n requests: this.io.stats.requests,\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 this._sendDownHints = opts.sendDownHints !== false\n this._downHintsRateLimit =\n opts.downHintsRateLimit !== undefined ? opts.downHintsRateLimit : 10 * 5\n this._downHintsSentPerTick = 0\n this._pendingTimers = new Set()\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 DEFAULTS = DEFAULTS\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({\n port,\n ephemeral: false,\n firewalled: false,\n anyPort: false,\n bootstrap: [],\n ...opts\n })\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 get config() {\n return {\n concurrency: this.concurrency,\n maxWindow: this.io.congestion._maxWindow,\n randomPunchInterval: this._randomPunchInterval,\n connectionKeepAlive: this.connectionKeepAlive,\n sendDownHints: this._sendDownHints,\n downHintsRateLimit: this._downHintsRateLimit\n }\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 this.health.reset()\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(\n { id: null, host, port },\n null,\n true,\n PING,\n null,\n value,\n (opts && opts.session) || null,\n opts && opts.ttl\n )\n return this._requestToPromise(req, opts)\n }\n\n delayedPing({ host, port }, delayMs, opts) {\n if (delayMs > this.maxPingDelay) {\n throw new Error(`Delay exceeds max delay: ${this.maxPingDelay}ms`)\n }\n\n const value = b4a.allocUnsafe(4)\n c.uint32.encode({ start: 0, end: 4, buffer: value }, delayMs)\n\n const req = this.io.createRequest(\n { id: null, host, port },\n null,\n true,\n DELAYED_PING,\n null,\n value,\n (opts && opts.session) || null,\n opts && opts.ttl\n )\n // add 1 second to the timeout to account for network overhead\n req.timeout = delayMs + 1_000\n return this._requestToPromise(req, opts)\n }\n\n async rttStats() {\n const stats = {\n successes: 0,\n errors: 0,\n responses: {\n avgRtt: 0,\n errors: 0,\n avgCloserNodes: 0\n },\n closestReplies: {\n avgRtt: 0,\n errors: 0,\n avgCloserNodes: 0\n }\n }\n\n if (this.nodes.latest) {\n const q = this.findNode(this.nodes.latest.id)\n\n let responseCount = 0\n let closestCount = 0\n\n for await (const msg of q) {\n stats.responses.avgRtt += msg.rtt\n stats.responses.errors += msg.error\n stats.responses.avgCloserNodes += msg.closerNodes?.length || 0\n responseCount++\n }\n stats.responses.avgRtt /= responseCount\n stats.responses.avgCloserNodes /= responseCount\n\n for await (const msg of q.closestReplies) {\n stats.closestReplies.avgRtt += msg.rtt\n stats.closestReplies.errors += msg.error\n stats.closestReplies.avgCloserNodes += msg.closerNodes?.length || 0\n closestCount++\n }\n stats.closestReplies.avgRtt /= closestCount\n stats.closestReplies.avgCloserNodes /= closestCount\n\n stats.successes = q.successes\n stats.errors = q.errors\n }\n\n return stats\n }\n\n request({ token = null, command, target = null, value = null }, { host, port }, opts) {\n const req = this.io.createRequest(\n { id: null, host, port },\n token,\n false,\n command,\n target,\n value,\n (opts && opts.session) || null,\n opts && opts.ttl\n )\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(\n data.from,\n false,\n true,\n PING_NAT,\n null,\n value,\n null,\n () => {\n testNat = true\n },\n noop\n )\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 for (const timer of this._pendingTimers) {\n clearTimeout(timer)\n }\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 if (internal && !this._sendDownHints && command === DOWN_HINT) return null\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 (\n oldest === null ||\n oldest.pinged > node.pinged ||\n (oldest.pinged === node.pinged && oldest.added > node.added)\n ) {\n oldest = node\n }\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(\n { id: null, host: oldNode.host, port: oldNode.port },\n false,\n true,\n PING,\n null,\n null,\n null,\n onsuccess,\n onswap\n )\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 case DELAYED_PING: {\n this._ondelayedping(req)\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 _ondelayedping(req) {\n if (req.value === null || req.value.byteLength < 4) return\n const delayMs = c.uint32.decode({ start: 0, end: 4, buffer: req.value })\n if (delayMs > this.maxPingDelay) return\n const timer = setTimeout(() => {\n if (this.destroyed) return\n this._pendingTimers.delete(timer)\n req.sendReply(0, null, false, false)\n }, delayMs)\n this._pendingTimers.add(timer)\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(\n { id: null, host: node.host, port: node.port },\n false,\n true,\n PING,\n null,\n null,\n null,\n onresponse,\n onerror\n )\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) {\n // 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 (\n ((this._tick & 63) === 0 && this.nodes.length < this.table.k) ||\n --this._refreshTicks <= 0\n ) {\n this.refresh()\n }\n\n this._downHintsSentPerTick = 0\n this.health.update()\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) {\n return true\n }\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, {\n concurrency: backgroundCon\n })\n\n q.on('data', () => {\n // yield to other traffic\n q.concurrency = this.io.inflight.length < 3 ? this.concurrency : backgroundCon\n })\n\n return q\n }\n\n // called by health\n _online() {\n if (this.online && !this.degraded) return\n this.online = true\n this.degraded = false\n this.emit('network-update')\n }\n\n // called by health\n _degraded() {\n if (this.degraded) return\n this.online = true\n this.degraded = true\n this.emit('network-update')\n }\n\n // called by health\n _offline() {\n if (!this.online) return\n this.online = false\n this.degraded = 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(\n node,\n false,\n internal,\n command,\n null,\n value,\n null,\n onsuccess,\n onerror\n )\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\nexports.DELAYED_PING = 4\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 MAX_HEALTH_WINDOW = 4\nconst TIMEOUTS_THRESHOLD = 0.5\n\nmodule.exports = class NetworkHealth {\n static DEFAULT_MAX_HEALTH_WINDOW = MAX_HEALTH_WINDOW\n\n constructor(dht, { maxHealthWindow = MAX_HEALTH_WINDOW } = {}) {\n this._dht = dht\n this._maxHealthWindow = maxHealthWindow\n this._window = []\n this._head = -1\n this.online = true\n this.degraded = false\n }\n\n get oldest() {\n return this._window[this._tail]\n }\n\n get previous() {\n return this._window[(this._head - 1 + this._maxHealthWindow) % this._maxHealthWindow]\n }\n\n get newest() {\n return this._window[this._head]\n }\n\n get recentResponses() {\n if (!this.newest || !this.previous) return 0\n return this.newest.responses - this.previous.responses\n }\n\n get recentTimeouts() {\n if (!this.newest || !this.previous) return 0\n return this.newest.timeouts - this.previous.timeouts\n }\n\n get responses() {\n if (!this.newest || !this.oldest) return 0\n return this.newest.responses - this.oldest.responses\n }\n\n get timeouts() {\n if (!this.newest || !this.oldest) return 0\n return this.newest.timeouts - this.oldest.timeouts\n }\n\n get timeoutsRate() {\n if (this.timeouts === 0) return 0\n return this.timeouts / (this.responses + this.timeouts)\n }\n\n get idle() {\n return this.recentResponses === 0 && this.recentTimeouts === 0\n }\n\n get stats() {\n return {\n online: this.online,\n degraded: this.degraded,\n idle: this.idle,\n responses: this.responses,\n timeouts: this.timeouts,\n timeoutsRate: this.timeoutsRate,\n recentResponses: this.recentResponses,\n recentTimeouts: this.recentTimeouts\n }\n }\n\n get _tail() {\n return (this._head + 1) % this._maxHealthWindow\n }\n\n reset() {\n this._window = []\n this._head = -1\n this.online = true\n this.degraded = false\n this._dht._online()\n }\n\n update() {\n this._head = this._tail\n this._window[this._head] = {\n responses: this._dht.stats.requests.responses,\n timeouts: this._dht.stats.requests.timeouts\n }\n\n if (this.idle) return\n\n this.online = this.recentResponses > 0\n this.degraded = this.online && this.timeoutsRate > TIMEOUTS_THRESHOLD\n\n if (this.online && !this.degraded) this._dht._online()\n else if (this.degraded) this._dht._degraded()\n else this._dht._offline()\n }\n}\nconst FIFO = require('fast-fifo')\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst AdaptiveTimeout = require('adaptive-timeout')\nconst peer = require('./peer')\nconst { INVALID_TOKEN, REQUEST_TIMEOUT, REQUEST_DESTROYED, IO_SUSPENDED } = require('./errors')\n\nconst VERSION = 0b11\nconst RESPONSE_ID = (0b0001 << 4) | VERSION\nconst REQUEST_ID = (0b0000 << 4) | VERSION\nconst EMPTY_ARRAY = []\nconst MAX_WINDOW = 80\n\nmodule.exports = class IO {\n constructor(\n table,\n udx,\n {\n maxWindow = MAX_WINDOW,\n port = 0,\n host = '0.0.0.0',\n anyPort = true,\n firewalled = true,\n onrequest,\n onresponse = noop,\n ontimeout = noop,\n adaptiveTimeout\n } = {}\n ) {\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 requests: {\n active: 0,\n total: 0,\n responses: 0,\n timeouts: 0,\n retries: 0\n },\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 ? port : 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 this._adt = adaptiveTimeout ? new AdaptiveTimeout(adaptiveTimeout) : null\n }\n\n static DEFAULT_MAX_WINDOW = MAX_WINDOW\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 (\n req.token !== null &&\n !b4a.equals(req.token, this.token(req.from, 1)) &&\n !b4a.equals(req.token, this.token(req.from, 0))\n ) {\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 (this._adt && req.sent <= 2) {\n this._adt.put(`${req.to.host}:${req.to.port}`, res.rtt)\n }\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.stats.requests.active--\n this.stats.requests.responses++\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 this.stats.requests.active--\n\n req.onerror(suspended ? IO_SUSPENDED() : REQUEST_DESTROYED(), req)\n }\n\n await Promise.allSettled([this.serverSocket.close(), this.clientSocket.close()])\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(\n this,\n socket,\n tid,\n null,\n to,\n token,\n internal,\n command,\n target,\n value,\n session,\n ttl || 0\n )\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 this.stats.requests.active++\n this.stats.requests.total++\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 this.timeout = 0\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(\n io,\n socket,\n tid,\n from,\n to,\n token,\n internal,\n command,\n target,\n value,\n null,\n 0\n )\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) {\n this._buffer = this._encodeRequest(this.token, this.value, this.to, this.socket)\n }\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 const value =\n this.timeout || this._io._adt?.get(`${this.to.host}:${this.to.port}`, this.sent) || 1000\n this._timeout = setTimeout(oncycle, value, 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.stats.requests.active--\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 =\n 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++] =\n (id ? 1 : 0) |\n (token ? 2 : 0) |\n (closerNodes.length > 0 ? 4 : 0) |\n (error > 0 ? 8 : 0) |\n (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++] =\n (id ? 1 : 0) |\n (token ? 2 : 0) |\n (this.internal ? 4 : 0) |\n (this.target ? 8 : 0) |\n (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._io.stats.requests.timeouts++\n req.destroy(REQUEST_TIMEOUT())\n req._io.ontimeout(req)\n } else {\n req._io.stats.requests.retries++\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({ start: 0, end: 6, buffer: addr }, { host, port })\n sodium.crypto_generichash(out, addr)\n return out\n}\nconst { Readable, getStreamError } = 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.retries =\n opts.retries === 0 ? 0 : opts.retries || (this.internal && command === DOWN_HINT ? 3 : 5)\n this.closestReplies = []\n\n this._slow = 0\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(\n {\n id: node.id || peer.id(node.host, node.port),\n host: node.host,\n port: node.port\n },\n null\n )\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 if (this.destroyed) {\n const error = getStreamError(this)\n if (error) reject(error)\n else resolve()\n return\n }\n\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 (\n this.closestReplies.length < this.k ||\n this._compare(id, this.closestReplies[this.closestReplies.length - 1].from.id) < 0\n )\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 and we have a full result.\n if (\n this.inflight === 0 ||\n (this._slow === this.inflight && this.closestReplies.length >= this.k)\n ) {\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 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 // Check rate limit\n if (\n this.dht._downHintsRateLimit !== -1 &&\n this.dht._downHintsSentPerTick >= this.dht._downHintsRateLimit\n ) {\n return null\n }\n\n this.dht._downHintsSentPerTick++\n\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(\n to,\n this.force,\n this.internal,\n this.command,\n this.target,\n this.value,\n this._session,\n this._onvisitbound,\n this._onerrorbound\n )\n if (req === null) {\n this.destroy(new Error('Node was destroyed'))\n return\n }\n req.retries = this.retries\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._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(\n {\n token: reply.token,\n target: query.target,\n command: query.command,\n value: query.value\n },\n reply.from\n )\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(\n { token, command, target, value },\n { host, port },\n { ...opts, session: this }\n )\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.26.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 \"adaptive-timeout\": \"^1.0.1\",\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 \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"test-suspend\": \"^1.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:bare\": \"brittle-bare --coverage test.js\",\n \"lint\": \"prettier --check . && lunte\",\n \"test:node\": \"brittle-node --coverage 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}\nmodule.exports = require('bare-events')\n{\n \"name\": \"events-universal\",\n \"version\": \"1.0.1\",\n \"description\": \"Universal wrapper for the Node.js events module\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"bare\": \"./bare.js\",\n \"react-native\": \"./react-native.js\",\n \"default\": \"./default.js\"\n }\n },\n \"files\": [\n \"index.js\",\n \"default.js\",\n \"bare.js\",\n \"react-native.js\"\n ],\n \"scripts\": {\n \"test\": \"prettier . --check\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/events-universal.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/events-universal/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/events-universal#readme\",\n \"dependencies\": {\n \"bare-events\": \"^2.7.0\"\n },\n \"devDependencies\": {\n \"prettier\": \"^3.4.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\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}\nconst fs = require('fs')\nconst fsx = require('fs-native-extensions')\nconst onexit = require('resource-on-exit')\nconst ReadyResource = require('ready-resource')\n\nmodule.exports = class FDLock extends ReadyResource {\n constructor(fd, opts = {}) {\n const { wait = false } = opts\n\n super()\n\n this._fd = fd\n this._wait = wait\n this._locked = false\n\n onexit.add(this, closeSync)\n }\n\n async _open() {\n try {\n await this._resume()\n } catch (err) {\n onexit.remove(this)\n await close(this)\n throw err\n }\n }\n\n async _close() {\n onexit.remove(this)\n await close(this)\n }\n\n transfer() {\n const fd = this._fd\n if (fd === -1) throw new Error('Lock has already been transferred')\n this._fd = -1\n onexit.remove(this)\n return fd\n }\n\n async suspend() {\n if (!this.opened) await this.ready()\n return this._suspend()\n }\n\n async _suspend() {\n if (this._fd === -1 || this._locked === false) return\n fsx.unlock(this._fd)\n this._locked = false\n }\n\n async resume() {\n if (!this.opened) await this.ready()\n return this._resume()\n }\n\n async _resume() {\n if (this._fd === -1 || this._locked === true) return\n if (this._wait) await fsx.waitForLock(this._fd)\n else if (!fsx.tryLock(this._fd)) {\n throw new Error('File descriptor could not be locked')\n }\n this._locked = true\n }\n}\n\nfunction close(lock) {\n const fd = lock._fd\n if (fd === -1) return\n lock._fd = -1\n return new Promise((resolve) => fs.close(fd, () => resolve()))\n}\n\nfunction closeSync(lock) {\n const fd = lock._fd\n if (fd === -1) return\n lock._fd = -1\n try {\n fs.closeSync(fd)\n } catch {}\n}\n{\n \"name\": \"fd-lock\",\n \"version\": \"2.1.1\",\n \"description\": \"Stateful file descriptor locks for JavaScript\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": \"./index.js\"\n },\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n }\n },\n \"files\": [\n \"index.js\"\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 \"format\": \"prettier --write .\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/fd-lock.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/fd-lock/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/fd-lock#readme\",\n \"dependencies\": {\n \"bare-fs\": \"^4.5.0\",\n \"fs-native-extensions\": \"^1.4.4\",\n \"ready-resource\": \"^1.2.0\",\n \"resource-on-exit\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.3.2\",\n \"prettier\": \"^3.4.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n }\n}\nexports.fullRoots = function (index, result) {\n if (index & 1) throw new Error('You can only look up roots for depth(0) blocks')\n if (!result) result = []\n\n index /= 2\n\n let offset = 0\n let factor = 1\n\n while (true) {\n if (!index) return result\n while (factor * 2 <= index) factor *= 2\n result.push(offset + factor - 1)\n offset = offset + 2 * factor\n index -= factor\n factor = 1\n }\n}\n\nexports.futureRoots = function (index, result) {\n if (index & 1) throw new Error('You can only look up future roots for depth(0) blocks')\n if (!result) result = []\n\n let factor = 1\n\n // make first root\n while (factor * 2 <= index) factor *= 2\n\n // full factor of 2 - done\n if (factor * 2 - 2 === index) return result\n\n let pos = factor / 2 - 1\n\n // while its not a full tree\n while ((pos + factor / 2 - 1) !== index) {\n pos += factor\n\n // read too far, to to left child\n while ((pos + factor / 2 - 1) > index) {\n factor /= 2\n pos -= factor / 2\n }\n\n // the \"gap\" is a future root\n result.push(pos - factor / 2)\n }\n\n return result\n}\n\nexports.patch = function (from, to) {\n if (from === 0 || from >= to) return []\n\n const roots = exports.fullRoots(from)\n const target = exports.fullRoots(to)\n\n // first find the first root that is different\n\n let i = 0\n for (; i < target.length; i++) {\n if (i >= roots.length || roots[i] !== target[i]) break\n }\n\n const patch = []\n\n if (i < roots.length) {\n // now we need to grow the newest root until it hits the diff one\n let prev = roots.length - 1\n\n const ite = exports.iterator(roots[prev--])\n\n while (ite.index !== target[i]) {\n ite.sibling()\n\n if (prev >= 0 && ite.index === roots[prev]) {\n prev--\n } else {\n patch.push(ite.index)\n }\n\n patch.push(ite.parent())\n }\n\n i++ // patched to next root, so inc\n }\n\n // include the rest\n\n for (; i < target.length; i++) patch.push(target[i])\n\n return patch\n}\n\nexports.depth = function (index) {\n let depth = 0\n\n index += 1\n while (!(index & 1)) {\n depth++\n index = rightShift(index)\n }\n\n return depth\n}\n\nexports.sibling = function (index, depth) {\n if (!depth) depth = exports.depth(index)\n const offset = exports.offset(index, depth)\n\n return exports.index(depth, offset & 1 ? offset - 1 : offset + 1)\n}\n\nexports.parent = function (index, depth) {\n if (!depth) depth = exports.depth(index)\n const offset = exports.offset(index, depth)\n\n return exports.index(depth + 1, rightShift(offset))\n}\n\nexports.leftChild = function (index, depth) {\n if (!(index & 1)) return -1\n if (!depth) depth = exports.depth(index)\n return exports.index(depth - 1, exports.offset(index, depth) * 2)\n}\n\nexports.rightChild = function (index, depth) {\n if (!(index & 1)) return -1\n if (!depth) depth = exports.depth(index)\n return exports.index(depth - 1, 1 + (exports.offset(index, depth) * 2))\n}\n\nexports.children = function (index, depth) {\n if (!(index & 1)) return null\n\n if (!depth) depth = exports.depth(index)\n const offset = exports.offset(index, depth) * 2\n\n return [\n exports.index(depth - 1, offset),\n exports.index(depth - 1, offset + 1)\n ]\n}\n\nexports.leftSpan = function (index, depth) {\n if (!(index & 1)) return index\n if (!depth) depth = exports.depth(index)\n return exports.offset(index, depth) * twoPow(depth + 1)\n}\n\nexports.rightSpan = function (index, depth) {\n if (!(index & 1)) return index\n if (!depth) depth = exports.depth(index)\n return (exports.offset(index, depth) + 1) * twoPow(depth + 1) - 2\n}\n\nexports.nextLeaf = function (index) {\n let factor = 1\n let r = index\n\n while ((r & 1) === 1) {\n r = (r - 1) / 2\n factor *= 2\n }\n\n return index + factor + 1\n}\n\nexports.count = function (index, depth) {\n if (!(index & 1)) return 1\n if (!depth) depth = exports.depth(index)\n return twoPow(depth + 1) - 1\n}\n\nexports.countLeaves = function (index) {\n return (exports.count(index) + 1) / 2\n}\n\nexports.spans = function (index, depth) {\n if (!(index & 1)) return [index, index]\n if (!depth) depth = exports.depth(index)\n\n const offset = exports.offset(index, depth)\n const width = twoPow(depth + 1)\n\n return [offset * width, (offset + 1) * width - 2]\n}\n\nexports.index = function (depth, offset) {\n return (1 + 2 * offset) * twoPow(depth) - 1\n}\n\nexports.offset = function (index, depth) {\n if (!(index & 1)) return index / 2\n if (!depth) depth = exports.depth(index)\n\n return ((index + 1) / twoPow(depth) - 1) / 2\n}\n\nexports.iterator = function (index) {\n const ite = new Iterator()\n ite.seek(index || 0)\n return ite\n}\n\nfunction twoPow (n) {\n return n < 31 ? 1 << n : ((1 << 30) * (1 << (n - 30)))\n}\n\nfunction rightShift (n) {\n return (n - (n & 1)) / 2\n}\n\nfunction Iterator () {\n this.index = 0\n this.offset = 0\n this.factor = 0\n}\n\nIterator.prototype.seek = function (index) {\n this.index = index\n if (this.index & 1) {\n this.offset = exports.offset(index)\n this.factor = twoPow(exports.depth(index) + 1)\n } else {\n this.offset = index / 2\n this.factor = 2\n }\n}\n\nIterator.prototype.isLeft = function () {\n return (this.offset & 1) === 0\n}\n\nIterator.prototype.isRight = function () {\n return (this.offset & 1) === 1\n}\n\nIterator.prototype.isRoot = function (length) {\n const currentLength = 1 + (this.index + this.factor / 2 - 1) / 2\n if (length < currentLength) return false\n\n const factor = this.factor * 2\n const index = (this.offset & 1)\n ? this.index - this.factor / 2\n : this.index + this.factor / 2\n\n const parentLength = 1 + (index + factor / 2 - 1) / 2\n return parentLength > length\n}\n\nIterator.prototype.contains = function (index) {\n return index > this.index\n ? index < (this.index + this.factor / 2)\n : index < this.index\n ? index > (this.index - this.factor / 2)\n : true\n}\n\nIterator.prototype.prev = function () {\n if (!this.offset) return this.index\n this.offset--\n this.index -= this.factor\n return this.index\n}\n\nIterator.prototype.next = function () {\n this.offset++\n this.index += this.factor\n return this.index\n}\n\nIterator.prototype.count = function () {\n if (!(this.index & 1)) return 1\n return this.factor - 1\n}\n\nIterator.prototype.countLeaves = function () {\n return (this.count() + 1) / 2\n}\n\nIterator.prototype.sibling = function () {\n return this.isLeft() ? this.next() : this.prev()\n}\n\nIterator.prototype.parent = function () {\n if (this.offset & 1) {\n this.index -= this.factor / 2\n this.offset = (this.offset - 1) / 2\n } else {\n this.index += this.factor / 2\n this.offset /= 2\n }\n this.factor *= 2\n return this.index\n}\n\nIterator.prototype.leftSpan = function () {\n this.index = this.index - this.factor / 2 + 1\n this.offset = this.index / 2\n this.factor = 2\n return this.index\n}\n\nIterator.prototype.peekLeftSpan = function () {\n return this.index - this.factor / 2 + 1\n}\n\nIterator.prototype.rightSpan = function () {\n this.index = this.index + this.factor / 2 - 1\n this.offset = this.index / 2\n this.factor = 2\n return this.index\n}\n\nIterator.prototype.peekRightSpan = function () {\n return this.index + this.factor / 2 - 1\n}\n\nIterator.prototype.leftChild = function () {\n if (this.factor === 2) return this.index\n this.factor /= 2\n this.index -= this.factor / 2\n this.offset *= 2\n return this.index\n}\n\nIterator.prototype.rightChild = function () {\n if (this.factor === 2) return this.index\n this.factor /= 2\n this.index += this.factor / 2\n this.offset = 2 * this.offset + 1\n return this.index\n}\n\nIterator.prototype.nextTree = function () {\n this.index = this.index + this.factor / 2 + 1\n this.offset = this.index / 2\n this.factor = 2\n return this.index\n}\n\nIterator.prototype.prevTree = function () {\n if (!this.offset) {\n this.index = 0\n this.factor = 2\n } else {\n this.index = this.index - this.factor / 2 - 1\n this.offset = this.index / 2\n this.factor = 2\n }\n return this.index\n}\n\nIterator.prototype.fullRoot = function (index) {\n if (index <= this.index || (this.index & 1) > 0) return false\n while (index > this.index + this.factor + this.factor / 2) {\n this.index += this.factor / 2\n this.factor *= 2\n this.offset /= 2\n }\n return true\n}\n{\n \"name\": \"flat-tree\",\n \"version\": \"1.13.0\",\n \"description\": \"A series of functions to map a binary tree to a list\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {},\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/flat-tree.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/flat-tree/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/flat-tree\"\n}\nrequire.addon = require('require-addon')\n\nmodule.exports = require.addon('.', __filename)\nconst { isWindows } = require('which-runtime')\nconst binding = require('./binding')\n\nfunction onwork(err, result) {\n if (err) this.reject(err)\n else this.resolve(result)\n}\n\nexports.tryLock = function tryLock(fd, offset = 0, length = 0, opts = {}) {\n if (typeof offset === 'object') {\n opts = offset\n offset = 0\n }\n\n if (typeof length === 'object') {\n opts = length\n length = 0\n }\n\n if (typeof opts !== 'object' || opts === null) {\n opts = {}\n }\n\n try {\n binding.tryLock(fd, offset, length, opts.shared !== true)\n } catch (err) {\n if (err.code === 'EAGAIN') return false\n throw err\n }\n\n return true\n}\n\nexports.waitForLock = function waitForLock(\n fd,\n offset = 0,\n length = 0,\n opts = {}\n) {\n if (typeof offset === 'object') {\n opts = offset\n offset = 0\n }\n\n if (typeof length === 'object') {\n opts = length\n length = 0\n }\n\n if (typeof opts !== 'object' || opts === null) {\n opts = {}\n }\n\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.waitForLock(\n fd,\n offset,\n length,\n opts.shared !== true,\n req,\n onwork\n )\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.tryDowngradeLock = function tryDowngradeLock(\n fd,\n offset = 0,\n length = 0\n) {\n try {\n binding.tryDowngradeLock(fd, offset, length)\n } catch (err) {\n if (err.code === 'EAGAIN') return false\n throw err\n }\n\n return true\n}\n\nexports.waitForDowngradeLock = function downgradeLock(\n fd,\n offset = 0,\n length = 0\n) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.waitForDowngradeLock(fd, offset, length, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.tryUpgradeLock = function tryUpgradeLock(fd, offset = 0, length = 0) {\n try {\n binding.tryUpgradeLock(fd, offset, length)\n } catch (err) {\n if (err.code === 'EAGAIN') return false\n throw err\n }\n\n return true\n}\n\nexports.waitForUpgradeLock = function upgradeLock(fd, offset = 0, length = 0) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.waitForUpgradeLock(fd, offset, length, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.unlock = function unlock(fd, offset = 0, length = 0) {\n binding.unlock(fd, offset, length)\n}\n\nexports.trim = function trim(fd, offset, length) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.trim(fd, offset, length, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.sparse = function sparse(fd) {\n // Short circuit on everything but Windows\n if (!isWindows) return Promise.resolve()\n\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.sparse(fd, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.swap = function swap(from, to) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.swap(from, to, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.getAttr = function getAttr(fd, name) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.getAttr(fd, name, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise.then((buffer) =>\n buffer === null ? null : Buffer.from(buffer)\n )\n}\n\nexports.setAttr = function setAttr(fd, name, value, encoding) {\n if (typeof value === 'string') value = Buffer.from(value, encoding)\n\n const req = {\n value,\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.setAttr(\n fd,\n name,\n value.buffer,\n value.byteOffset,\n value.byteLength,\n req,\n onwork\n )\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.removeAttr = function removeAttr(fd, name) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.removeAttr(fd, name, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n\nexports.listAttrs = function listAttrs(fd) {\n const req = {\n handle: null,\n resolve: null,\n reject: null\n }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.listAttrs(fd, req, onwork)\n } catch (err) {\n return Promise.reject(err)\n }\n\n return promise\n}\n{\n \"name\": \"fs-native-extensions\",\n \"version\": \"1.4.5\",\n \"description\": \"Native file system extensions for advanced file operations\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"macros.h\",\n \"binding.c\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"include\",\n \"src\",\n \"prebuilds\"\n ],\n \"imports\": {\n \"child_process\": {\n \"bare\": \"bare-subprocess\",\n \"default\": \"child_process\"\n },\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"fs/*\": {\n \"bare\": \"bare-fs/*\",\n \"default\": \"fs/*\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"addon\": true,\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/fs-native-extensions.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð <kasper@funktionel.co>\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/fs-native-extensions/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/fs-native-extensions#readme\",\n \"dependencies\": {\n \"require-addon\": \"^1.1.0\",\n \"which-runtime\": \"^1.2.0\"\n },\n \"devDependencies\": {\n \"bare-compat-napi\": \"^1.3.8\",\n \"bare-fs\": \"^4.4.4\",\n \"bare-path\": \"^3.0.0\",\n \"bare-subprocess\": \"^4.0.5\",\n \"brittle\": \"^3.1.1\",\n \"cmake-bare\": \"^1.1.10\",\n \"cmake-fetch\": \"^1.4.7\",\n \"cmake-napi\": \"^1.0.5\",\n \"cmake-npm\": \"^1.1.0\",\n \"minimist\": \"^1.2.6\",\n \"prettier\": \"^3.5.3\",\n \"prettier-config-standard\": \"^7.0.0\",\n \"test-tmp\": \"^1.2.1\"\n }\n}\nconst codecs = require('codecs')\nconst { Readable } = require('streamx')\nconst mutexify = require('mutexify/promise')\nconst b4a = require('b4a')\nconst safetyCatch = require('safety-catch')\nconst ReadyResource = require('ready-resource')\nconst debounce = require('debounceify')\nconst Rache = require('rache')\nconst rrp = require('resolve-reject-promise')\n\nconst { all: unslabAll } = require('unslab')\n\nconst RangeIterator = require('./iterators/range')\nconst HistoryIterator = require('./iterators/history')\nconst DiffIterator = require('./iterators/diff')\nconst LocalBlockIterator = require('./iterators/local')\nconst Extension = require('./lib/extension')\nconst { YoloIndex, Node, Header } = require('./lib/messages')\nconst { BLOCK_NOT_AVAILABLE, DECODING_ERROR } = require('hypercore-errors')\n\nconst T = 5\nconst MIN_KEYS = T - 1\nconst MAX_CHILDREN = MIN_KEYS * 2 + 1\n\nconst SEP = b4a.alloc(1)\nconst EMPTY = b4a.alloc(0)\n\nclass Key {\n constructor(seq, value) {\n this.seq = seq\n this.value = value\n }\n}\n\nclass Child {\n constructor(seq, offset, value) {\n this.seq = seq\n this.offset = offset\n this.value = value\n }\n}\n\nclass Cache {\n constructor(rache) {\n this.keys = rache\n this.length = 0\n }\n\n get(seq) {\n return this.keys.get(seq) || null\n }\n\n set(seq, key) {\n this.keys.set(seq, key)\n if (seq >= this.length) this.length = seq + 1\n }\n\n gc(length) {\n // if we need to \"work\" more than 128 ticks, just bust the cache...\n if (this.length - length > 128) {\n this.keys.clear()\n } else {\n for (let i = length; i < this.length; i++) {\n this.keys.delete(i)\n }\n }\n\n this.length = length\n }\n\n clear() {\n this.keys.clear()\n }\n}\n\nclass Pointers {\n constructor(decoded) {\n this.levels = decoded.levels.map((l) => {\n const children = []\n const keys = []\n\n for (let i = 0; i < l.keys.length; i++) {\n keys.push(new Key(l.keys[i], null))\n }\n\n for (let i = 0; i < l.children.length; i += 2) {\n children.push(new Child(l.children[i], l.children[i + 1], null))\n }\n\n return { keys, children }\n })\n }\n\n get(i) {\n return this.levels[i]\n }\n\n hasKey(seq) {\n for (const lvl of this.levels) {\n for (const key of lvl.keys) {\n if (key.seq === seq) return true\n }\n }\n return false\n }\n}\n\nfunction inflate(entry) {\n if (entry.inflated === null) {\n entry.inflated = YoloIndex.decode(entry.index)\n entry.index = null\n }\n return new Pointers(entry.inflated)\n}\n\nfunction deflate(index) {\n const levels = index.map((l) => {\n const keys = []\n const children = []\n\n for (let i = 0; i < l.value.keys.length; i++) {\n keys.push(l.value.keys[i].seq)\n }\n\n for (let i = 0; i < l.value.children.length; i++) {\n children.push(l.value.children[i].seq, l.value.children[i].offset)\n }\n\n return { keys, children }\n })\n\n return YoloIndex.encode({ levels })\n}\n\nclass TreeNode {\n constructor(block, keys, children, offset) {\n this.block = block\n this.offset = offset\n this.keys = keys\n this.children = children\n this.changed = false\n\n this.preload()\n }\n\n preload() {\n if (this.block === null) return\n\n const core = getBackingCore(this.block.tree.core)\n if (!core) return\n\n const bitfield = core.core.bitfield\n const blocks = []\n\n for (let i = 0; i < this.keys.length; i++) {\n const k = this.keys[i]\n if (k.value) continue\n if (k.seq >= core.signedLength || (bitfield && bitfield.get(k.seq))) continue\n blocks.push(k.seq)\n }\n for (let i = 0; i < this.children.length; i++) {\n const c = this.children[i]\n if (c.value) continue\n if (c.seq >= core.signedLength || (bitfield && bitfield.get(c.seq))) continue\n blocks.push(c.seq)\n }\n\n if (blocks.length) core.download({ blocks })\n }\n\n async insertKey(key, value, child, node, encoding, cas) {\n let s = 0\n let e = this.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n c = b4a.compare(key.value, await this.getKey(mid))\n\n if (c === 0) {\n if (cas) {\n const prev = await this.getKeyNode(mid)\n if (!(await cas(prev.final(encoding), node))) return true\n }\n if (!this.block.tree.tree.alwaysDuplicate) {\n const prev = await this.getKeyNode(mid)\n if (sameValue(prev.value, value)) return true\n }\n this.changed = true\n this.keys[mid] = key\n return true\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n const i = c < 0 ? e : s\n this.keys.splice(i, 0, key)\n if (child) this.children.splice(i + 1, 0, new Child(0, 0, child))\n this.changed = true\n\n return this.keys.length < MAX_CHILDREN\n }\n\n removeKey(index) {\n this.keys.splice(index, 1)\n if (this.children.length) {\n this.children[index + 1].seq = 0 // mark as freed\n this.children.splice(index + 1, 1)\n }\n this.changed = true\n }\n\n async siblings(parent) {\n for (let i = 0; i < parent.children.length; i++) {\n if (parent.children[i].value === this) {\n const [left, right] = await Promise.all([\n i ? parent.getChildNode(i - 1) : null,\n i < parent.children.length - 1 ? parent.getChildNode(i + 1) : null\n ])\n return { left, index: i, right }\n }\n }\n\n throw new Error('Bad parent')\n }\n\n merge(node, median) {\n this.changed = true\n this.keys.push(median)\n for (let i = 0; i < node.keys.length; i++) this.keys.push(node.keys[i])\n for (let i = 0; i < node.children.length; i++) this.children.push(node.children[i])\n }\n\n async split() {\n const len = this.keys.length >> 1\n const right = TreeNode.create(this.block)\n\n while (right.keys.length < len) right.keys.push(this.keys.pop())\n right.keys.reverse()\n\n await this.getKey(this.keys.length - 1) // make sure the median is loaded\n const median = this.keys.pop()\n\n if (this.children.length) {\n while (right.children.length < len + 1) right.children.push(this.children.pop())\n right.children.reverse()\n }\n\n this.changed = true\n\n return {\n left: this,\n median,\n right\n }\n }\n\n getKeyNode(index) {\n return this.block.tree.getBlock(this.keys[index].seq)\n }\n\n async getChildNode(index) {\n const child = this.children[index]\n if (child.value) return child.value\n const block =\n child.seq === this.block.seq ? this.block : await this.block.tree.getBlock(child.seq)\n return (child.value = block.getTreeNode(child.offset))\n }\n\n setKey(index, key) {\n this.keys[index] = key\n this.changed = true\n }\n\n async getKey(index) {\n const key = this.keys[index]\n if (key.value) return key.value\n const k = key.seq === this.block.seq ? this.block.key : await this.block.tree.getKey(key.seq)\n return (key.value = k)\n }\n\n indexChanges(index, seq) {\n const offset = index.push(null) - 1\n this.changed = false\n\n for (const child of this.children) {\n if (!child.value || !child.value.changed) continue\n child.seq = seq\n child.offset = child.value.indexChanges(index, seq)\n index[child.offset] = child\n }\n\n return offset\n }\n\n updateChildren(seq, block) {\n for (const child of this.children) {\n if (!child.value || child.seq !== seq) continue\n child.value.block = block\n child.value.updateChildren(seq, block)\n }\n }\n\n static create(block) {\n const node = new TreeNode(block, [], [], 0)\n node.changed = true\n return node\n }\n}\n\nclass BlockEntry {\n constructor(seq, tree, entry) {\n this.seq = seq\n this.tree = tree\n this.index = null\n this.entry = entry\n this.key = entry.key\n this.value = entry.value\n }\n\n isTarget(key) {\n return b4a.equals(this.key, key)\n }\n\n inflate() {\n if (this.index === null) {\n this.index = inflate(this.entry)\n }\n }\n\n isDeletion() {\n if (this.value !== null) return false\n\n if (this.index === null) {\n this.index = inflate(this.entry)\n }\n\n return !this.index.hasKey(this.seq)\n }\n\n final(encoding) {\n return {\n seq: this.seq,\n key: encoding.key ? encoding.key.decode(this.key) : this.key,\n value: this.value && (encoding.value ? encoding.value.decode(this.value) : this.value)\n }\n }\n\n getTreeNode(offset) {\n if (this.index === null) {\n this.index = inflate(this.entry)\n }\n const entry = this.index.get(offset)\n return new TreeNode(this, entry.keys, entry.children, offset)\n }\n}\n\nclass CacheLock {\n constructor() {\n this.map = new Map()\n }\n\n enter(seq) {\n const pending = this.map.get(seq)\n\n if (!pending) {\n this.map.set(seq, [])\n return Promise.resolve()\n }\n\n const { resolve, promise } = rrp()\n pending.push(resolve)\n return promise\n }\n\n exit(seq) {\n const pending = this.map.get(seq)\n if (!pending.length) {\n this.map.delete(seq)\n return\n }\n\n pending.pop()()\n }\n}\n\nclass BatchEntry extends BlockEntry {\n constructor(seq, tree, key, value, index) {\n super(seq, tree, { key, value, index: null, inflated: null })\n this.pendingIndex = index\n }\n\n isTarget(key) {\n return false\n }\n\n getTreeNode(offset) {\n return this.pendingIndex[offset].value\n }\n}\n\nclass Hyperbee extends ReadyResource {\n constructor(core, opts = {}) {\n super()\n // this.feed is now deprecated, and will be this.core going forward\n this.feed = core\n this.core = core\n\n this.keyEncoding = opts.keyEncoding ? codecs(opts.keyEncoding) : null\n this.valueEncoding = opts.valueEncoding ? codecs(opts.valueEncoding) : null\n this.extension = opts.extension !== false ? getExtension(this, opts) : null\n this.metadata = opts.metadata || null\n this.lock = opts.lock || mutexify()\n this.sep = opts.sep || SEP\n this.readonly = !!opts.readonly\n this.prefix = opts.prefix || null\n\n // In a future version, this should be false by default\n this.alwaysDuplicate = opts.alwaysDuplicate !== false\n\n this._unprefixedKeyEncoding = this.keyEncoding\n this._sub = !!this.prefix\n this._checkout = opts.checkout || 0\n this._view = !!opts._view\n\n this._onappendBound = this._view ? null : this._onappend.bind(this)\n this._ontruncateBound = this._view ? null : this._ontruncate.bind(this)\n this._watchers = this._onappendBound ? [] : null\n this._entryWatchers = this._onappendBound ? [] : null\n this._sessions = opts.sessions !== false\n\n this._keyCache = null\n this._nodeCache = null\n this._cacheLock = new CacheLock()\n\n this._batches = []\n\n if (this._watchers) {\n this.core.on('append', this._onappendBound)\n this.core.on('truncate', this._ontruncateBound)\n }\n\n if (this.prefix && opts._sub) {\n this.keyEncoding = prefixEncoding(this.prefix, this.keyEncoding)\n }\n\n this.ready().catch(safetyCatch)\n }\n\n async _open() {\n if (this.core.opened === false) await this.core.ready()\n // bump the internal ranges since the nodes are small\n if (this._watchers && this.core.replicator.setInflightRange) {\n this.core.replicator.setInflightRange(256, 512)\n }\n\n // snapshot\n if (this._checkout === -1) this._checkout = Math.max(1, this.core.length)\n\n const baseCache = Rache.from(this.core.globalCache)\n this._keyCache = new Cache(baseCache)\n this._nodeCache = new Cache(Rache.from(baseCache))\n }\n\n get version() {\n return Math.max(1, this._checkout || this.core.length)\n }\n\n get id() {\n return this.core.id\n }\n\n get key() {\n return this.core.key\n }\n\n get discoveryKey() {\n return this.core.discoveryKey\n }\n\n get writable() {\n return this.core.writable\n }\n\n get readable() {\n return this.core.readable\n }\n\n replicate(isInitiator, opts) {\n return this.core.replicate(isInitiator, opts)\n }\n\n update(opts) {\n return this.core.update(opts)\n }\n\n peek(range, opts) {\n return iteratorPeek(this.createRangeIterator(range, { ...opts, limit: 1 }))\n }\n\n createRangeIterator(range, opts = {}) {\n // backwards compat range arg\n opts = opts ? { ...opts, ...range } : range\n\n const extension = opts.extension === false && opts.limit !== 0 ? null : this.extension\n const keyEncoding = opts.keyEncoding ? codecs(opts.keyEncoding) : this.keyEncoding\n\n if (extension) {\n const { onseq, onwait } = opts\n let version = 0\n let next = 0\n\n opts = encRange(keyEncoding, {\n ...opts,\n sub: this._sub,\n onseq(seq) {\n if (!version) version = seq + 1\n if (next) next--\n if (onseq) onseq(seq)\n },\n onwait(seq) {\n if (!next) {\n next = Extension.BATCH_SIZE\n extension.iterator(ite.snapshot(version))\n }\n if (onwait) onwait(seq)\n }\n })\n } else {\n opts = encRange(keyEncoding, { ...opts, sub: this._sub })\n }\n\n const ite = new RangeIterator(\n new Batch(this, this._makeSnapshot(), null, false, opts),\n null,\n opts\n )\n return ite\n }\n\n createReadStream(range, opts) {\n const signal = (opts && opts.signal) || null\n return iteratorToStream(this.createRangeIterator(range, opts), signal)\n }\n\n createHistoryStream(opts) {\n const session = opts && opts.live ? this.core.session() : this._makeSnapshot()\n const signal = (opts && opts.signal) || null\n return iteratorToStream(\n new HistoryIterator(new Batch(this, session, null, false, opts), opts),\n signal\n )\n }\n\n createDiffStream(right, range, opts) {\n if (typeof right === 'number') right = this.checkout(Math.max(1, right), { reuseSession: true })\n\n // backwards compat range arg\n opts = opts ? { ...opts, ...range } : range\n\n const signal = (opts && opts.signal) || null\n\n const keyEncoding = opts && opts.keyEncoding ? codecs(opts.keyEncoding) : this.keyEncoding\n if (keyEncoding) opts = encRange(keyEncoding, { ...opts, sub: this._sub })\n\n let done\n let closing\n let ite\n\n const left = this\n\n const rs = new Readable({\n signal,\n eagerOpen: true,\n async open(cb) {\n try {\n if (right.opened === false) await right.ready()\n if (left.opened === false) await left.ready()\n } catch (err) {\n cb(err)\n return\n }\n\n if (closing) {\n cb(null)\n return\n }\n\n if (left.core.closing || right.core.closing) {\n cb(new Error('Bee closed'))\n return\n }\n\n const snapshot = right.version > left.version ? right._makeSnapshot() : left._makeSnapshot()\n\n done = cb\n ite = new DiffIterator(\n new Batch(left, snapshot, null, false, opts),\n new Batch(right, snapshot, null, false, opts),\n opts\n )\n ite.open().then(fin, fin)\n },\n read(cb) {\n done = cb\n ite.next().then(push, fin)\n },\n predestroy() {\n if (!ite) {\n closing = Promise.resolve()\n } else {\n closing = ite.close()\n closing.catch(noop)\n }\n },\n destroy(cb) {\n done = cb\n if (!closing) closing = ite.close()\n closing.then(fin, fin)\n }\n })\n\n return rs\n\n function fin(err) {\n done(err)\n }\n\n function push(val) {\n rs.push(val)\n done(null)\n }\n }\n\n get(key, opts) {\n const b = new Batch(this, this._makeSnapshot(), null, true, opts)\n return b.get(key)\n }\n\n getBySeq(seq, opts) {\n const b = new Batch(this, this._makeSnapshot(), null, true, opts)\n return b.getBySeq(seq)\n }\n\n put(key, value, opts) {\n const b = new Batch(this, this.core, null, true, opts)\n return b.put(key, value, opts)\n }\n\n batch(opts) {\n return new Batch(this, this.core, mutexify(), true, opts)\n }\n\n del(key, opts) {\n const b = new Batch(this, this.core, null, true, opts)\n return b.del(key, opts)\n }\n\n watch(range, opts) {\n if (!this._watchers) throw new Error('Can only watch the main bee instance')\n return new Watcher(this, range, opts)\n }\n\n async getAndWatch(key, opts) {\n if (!this._watchers) throw new Error('Can only watch the main bee instance')\n\n const watcher = new EntryWatcher(this, key, opts)\n await watcher._debouncedUpdate()\n\n if (this.closing) {\n await watcher.close()\n throw new Error('Bee closed')\n }\n\n return watcher\n }\n\n _onappend() {\n for (const watcher of this._watchers) {\n watcher._onappend()\n }\n\n for (const watcher of this._entryWatchers) {\n watcher._onappend()\n }\n }\n\n _ontruncate(length) {\n for (const watcher of this._watchers) {\n watcher._ontruncate()\n }\n\n for (const watcher of this._entryWatchers) {\n watcher._ontruncate()\n }\n\n this._nodeCache.gc(length)\n this._keyCache.gc(length)\n }\n\n _makeSnapshot() {\n if (this._sessions === false) return this.core\n // TODO: better if we could encapsulate this in hypercore in the future\n return this._checkout <= this.core.length || this._checkout <= 1\n ? this.core.snapshot()\n : this.core.session({ snapshot: false })\n }\n\n async clearUnlinked(options = {}) {\n await this.ready()\n\n const { gte = 0, lt = this.version - 1, batchSize = 4096, wait = true } = options\n const checkout = this.version\n\n let prev = this.batch({ wait: false, checkout: gte })\n let b = this.batch({ wait, checkout })\n\n const iteBatch = this.batch({ wait: false, checkout })\n const ite = new LocalBlockIterator(iteBatch, { gte, lt })\n await ite.open()\n\n let ticks = 0\n\n while (true) {\n const data = await ite.next()\n if (!data) break\n\n if (!(await isLinked(b, data))) {\n if (b.core.closing || this.core.closing || this.closing) break\n await this.core.clear(data.seq)\n }\n\n const prevNode = await prev.get(data.key, { finalize: false }).catch(toNull)\n\n if (prevNode && !(await isLinked(b, prevNode))) {\n if (b.core.closing || this.core.closing || this.closing) break\n await this.core.clear(prevNode.seq)\n }\n\n if (ticks++ >= batchSize) {\n ticks = 0\n await prev.close()\n await b.close()\n\n prev = this.batch({ wait: false, checkout: gte })\n b = this.batch({ wait, checkout })\n }\n }\n\n if (b.core.closing || this.core.closing || this.closing) throw new Error('Core is closed')\n\n await b.close()\n await prev.close()\n await ite.close()\n await iteBatch.close()\n\n return lt\n }\n\n checkout(version, opts = {}) {\n if (version === 0) version = 1\n\n // same as above, just checkout isn't set yet...\n const snap =\n opts.reuseSession || this._sessions === false\n ? this.core\n : version <= this.core.length || version <= 1\n ? this.core.snapshot()\n : this.core.session({ snapshot: false })\n\n return new Hyperbee(snap, {\n _view: true,\n _sub: false,\n prefix: this.prefix,\n sep: this.sep,\n lock: this.lock,\n checkout: version,\n keyEncoding: opts.keyEncoding || this.keyEncoding,\n valueEncoding: opts.valueEncoding || this.valueEncoding,\n extension: this.extension !== null ? this.extension : false\n })\n }\n\n snapshot(opts) {\n return this.checkout(\n this.core.opened === false || this._checkout <= 0 ? -1 : Math.max(1, this.version),\n opts\n )\n }\n\n sub(prefix, opts = {}) {\n let sep = opts.sep || this.sep\n if (!b4a.isBuffer(sep)) sep = b4a.from(sep)\n\n prefix = b4a.concat([this.prefix || EMPTY, b4a.from(prefix), sep])\n\n const valueEncoding = codecs(opts.valueEncoding || this.valueEncoding)\n const keyEncoding = codecs(opts.keyEncoding || this._unprefixedKeyEncoding)\n\n return new Hyperbee(this.core, {\n _view: true,\n _sub: true,\n prefix,\n sep: this.sep,\n lock: this.lock,\n checkout: this._checkout,\n valueEncoding,\n keyEncoding,\n extension: this.extension !== null ? this.extension : false,\n metadata: this.metadata\n })\n }\n\n async getHeader(opts) {\n const blk = await this.core.get(0, opts)\n try {\n return blk && Header.decode(blk)\n } catch {\n throw DECODING_ERROR()\n }\n }\n\n async _close() {\n if (!this._view) {\n if (this._keyCache) this._keyCache.clear()\n if (this._nodeCache) this._nodeCache.clear()\n }\n\n if (this._watchers) {\n this.core.off('append', this._onappendBound)\n this.core.off('truncate', this._ontruncateBound)\n\n while (this._watchers.length) {\n await this._watchers[this._watchers.length - 1].close()\n }\n }\n\n if (this._entryWatchers) {\n while (this._entryWatchers.length) {\n await this._entryWatchers[this._entryWatchers.length - 1].close()\n }\n }\n\n while (this._batches.length) {\n await this._batches[this._batches.length - 1].close()\n }\n\n return this.core.close()\n }\n\n static async isHyperbee(core, opts) {\n await core.ready()\n\n const blk0 = await core.get(0, opts)\n if (blk0 === null) throw BLOCK_NOT_AVAILABLE()\n\n try {\n return Header.decode(blk0).protocol === 'hyperbee'\n } catch (err) {\n // undecodable\n return false\n }\n }\n}\n\nclass Batch {\n constructor(tree, core, batchLock, cache, options = {}) {\n this.tree = tree\n // this.feed is now deprecated, and will be this.core going forward\n this.feed = core\n this.core = core\n this.index = tree._batches.push(this) - 1\n this.blocks = cache ? new Map() : null\n this.autoFlush = !batchLock\n this.maxBlocksCached = options.maxBlocksCached || 128\n this.rootSeq = 0\n this.root = null\n this.length = 0\n this.checkout = options.checkout === undefined ? -1 : options.checkout\n this.options = options\n this.locked = null\n this.batchLock = batchLock\n this.onseq = this.options.onseq || noop\n this.appending = null\n this.isSnapshot = this.core !== this.tree.core\n this.shouldUpdate = this.options.update !== false\n this.updating = null\n this.encoding = {\n key: options.keyEncoding ? codecs(options.keyEncoding) : tree.keyEncoding,\n value: options.valueEncoding ? codecs(options.valueEncoding) : tree.valueEncoding\n }\n }\n\n async ready() {\n if (this.core.opened === false) await this.core.ready()\n if (this.tree.opened === false) await this.tree.ready()\n }\n\n async lock() {\n if (this.tree.readonly) throw new Error('Hyperbee is marked as read-only')\n if (this.locked === null) this.locked = await this.tree.lock()\n }\n\n get version() {\n if (this.checkout !== -1) return Math.max(1, this.checkout)\n return Math.max(1, this.tree._checkout || this.core.length + this.length)\n }\n\n async getRoot(ensureHeader) {\n await this.ready()\n if (ensureHeader) {\n if (this.core.length === 0 && this.core.writable && !this.tree.readonly) {\n await this.core.append(\n Header.encode({\n protocol: 'hyperbee',\n metadata: this.tree.metadata\n })\n )\n }\n }\n if (this.tree._checkout === 0 && this.checkout === -1 && this.shouldUpdate) {\n if (this.updating === null) this.updating = this.core.update()\n await this.updating\n }\n if (this.version < 2) return null\n return (await this.getBlock(this.version - 1)).getTreeNode(0)\n }\n\n async getKey(seq) {\n await this.tree._cacheLock.enter(seq)\n\n try {\n const k = this.core.fork === this.tree.core.fork ? this.tree._keyCache.get(seq) : null\n if (k !== null) return k\n const key = (await this._getBlock(seq)).key\n if (this.core.fork === this.tree.core.fork) this.tree._keyCache.set(seq, key)\n return key\n } finally {\n this.tree._cacheLock.exit(seq)\n }\n }\n\n async _getNode(seq) {\n const cached =\n this.tree._nodeCache !== null && this.core.fork === this.tree.core.fork\n ? this.tree._nodeCache.get(seq)\n : null\n if (cached !== null) return cached\n const entry = await this.core.get(seq, {\n ...this.options,\n valueEncoding: Node\n })\n if (entry === null) throw BLOCK_NOT_AVAILABLE()\n const wrap = copyEntry(entry)\n if (this.core.fork === this.tree.core.fork && this.tree._nodeCache !== null) {\n this.tree._nodeCache.set(seq, wrap)\n }\n return wrap\n }\n\n async getBlock(seq) {\n if (this.rootSeq === 0) this.rootSeq = seq\n await this.tree._cacheLock.enter(seq)\n\n try {\n return await this._getBlock(seq)\n } finally {\n this.tree._cacheLock.exit(seq)\n }\n }\n\n async _getBlock(seq) {\n let b = this.blocks && this.blocks.get(seq)\n if (b) return b\n this.onseq(seq)\n const entry = await this._getNode(seq)\n b = this.blocks && this.blocks.get(seq)\n if (b) return b\n b = new BlockEntry(seq, this, entry)\n if (this.blocks && this.blocks.size - this.length < this.maxBlocksCached) {\n this.blocks.set(seq, b)\n }\n return b\n }\n\n _onwait(key) {\n this.options.onwait = null\n this.tree.extension.get(this.rootSeq + 1, key)\n }\n\n _getEncoding(opts) {\n if (!opts) return this.encoding\n return {\n key: opts.keyEncoding ? codecs(opts.keyEncoding) : this.encoding.key,\n value: opts.valueEncoding ? codecs(opts.valueEncoding) : this.encoding.value\n }\n }\n\n peek(range, opts) {\n return iteratorPeek(this.createRangeIterator(range, { ...opts, limit: 1 }))\n }\n\n createRangeIterator(range, opts = {}) {\n // backwards compat range arg\n opts = opts ? { ...opts, ...range } : range\n\n const encoding = this._getEncoding(opts)\n return new RangeIterator(\n this,\n encoding,\n encRange(encoding.key, { ...opts, sub: this.tree._sub })\n )\n }\n\n createReadStream(range, opts) {\n const signal = (opts && opts.signal) || null\n return iteratorToStream(this.createRangeIterator(range, opts), signal)\n }\n\n async getBySeq(seq, opts) {\n const encoding = this._getEncoding(opts)\n\n try {\n const block = (await this.getBlock(seq)).final(encoding)\n return { key: block.key, value: block.value }\n } finally {\n await this._closeSnapshot()\n }\n }\n\n async get(key, opts) {\n const encoding = this._getEncoding(opts)\n const finalize = opts ? opts.finalize !== false : true\n\n try {\n return await this._get(key, encoding, finalize)\n } finally {\n await this._closeSnapshot()\n }\n }\n\n async _get(key, encoding, finalize) {\n key = enc(encoding.key, key)\n\n if (this.tree.extension !== null && this.options.extension !== false) {\n this.options.onwait = this._onwait.bind(this, key)\n }\n\n let node = await this.getRoot(false)\n if (!node) return null\n\n while (true) {\n if (node.block.isTarget(key)) {\n return node.block.isDeletion() ? null : finalize ? node.block.final(encoding) : node.block\n }\n\n let s = 0\n let e = node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n\n c = b4a.compare(key, await node.getKey(mid))\n\n if (c === 0) {\n const block = await this.getBlock(node.keys[mid].seq)\n return finalize ? block.final(encoding) : block\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n if (!node.children.length) return null\n\n const i = c < 0 ? e : s\n node = await node.getChildNode(i)\n }\n }\n\n async links(key, seq) {\n let node = await this.getRoot(false)\n if (!node) return false\n\n if (node.block.seq === seq) return true\n\n while (true) {\n if (node.block.isTarget(key)) return false\n\n let s = 0\n let e = node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n\n if (node.keys[mid].seq === seq) return true\n c = b4a.compare(key, await node.getKey(mid))\n\n if (c === 0) return false\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n if (!node.children.length) return false\n\n const i = c < 0 ? e : s\n node = await node.getChildNode(i)\n if (node.block.seq === seq) return true\n }\n }\n\n async put(key, value, opts) {\n const release = this.batchLock ? await this.batchLock() : null\n\n const cas = (opts && opts.cas) || null\n const encoding = this._getEncoding(opts)\n\n if (!this.locked) await this.lock()\n if (!release) return this._put(key, value, encoding, cas)\n\n try {\n return await this._put(key, value, encoding, cas)\n } finally {\n release()\n }\n }\n\n async _put(key, value, encoding, cas) {\n const newNode = {\n seq: 0,\n key,\n value\n }\n key = enc(encoding.key, key)\n value = enc(encoding.value, value)\n\n const stack = []\n\n let root\n let node = (root = await this.getRoot(true))\n if (!node) node = root = TreeNode.create(null)\n\n const seq = (newNode.seq = this.core.length + this.length)\n const target = new Key(seq, key)\n\n while (node.children.length) {\n stack.push(node)\n node.changed = true // changed, but compressible\n\n let s = 0\n let e = node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n c = b4a.compare(target.value, await node.getKey(mid))\n\n if (c === 0) {\n if (cas) {\n const prev = await node.getKeyNode(mid)\n if (!(await cas(prev.final(encoding), newNode))) return this._unlockMaybe()\n }\n if (!this.tree.alwaysDuplicate) {\n const prev = await node.getKeyNode(mid)\n if (sameValue(prev.value, value)) return this._unlockMaybe()\n }\n node.setKey(mid, target)\n return this._append(root, seq, key, value)\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n const i = c < 0 ? e : s\n node = await node.getChildNode(i)\n }\n\n let needsSplit = !(await node.insertKey(target, value, null, newNode, encoding, cas))\n if (!node.changed) return this._unlockMaybe()\n\n while (needsSplit) {\n const parent = stack.pop()\n const { median, right } = await node.split()\n\n if (parent) {\n needsSplit = !(await parent.insertKey(median, value, right, null, encoding, null))\n node = parent\n } else {\n root = TreeNode.create(node.block)\n root.changed = true\n root.keys.push(median)\n root.children.push(new Child(0, 0, node), new Child(0, 0, right))\n needsSplit = false\n }\n }\n\n return this._append(root, seq, key, value)\n }\n\n async del(key, opts) {\n const release = this.batchLock ? await this.batchLock() : null\n const cas = (opts && opts.cas) || null\n const encoding = this._getEncoding(opts)\n\n if (!this.locked) await this.lock()\n if (!release) return this._del(key, encoding, cas)\n\n try {\n return await this._del(key, encoding, cas)\n } finally {\n release()\n }\n }\n\n async _del(key, encoding, cas) {\n const delNode = {\n seq: 0,\n key,\n value: null\n }\n\n key = enc(encoding.key, key)\n\n const stack = []\n\n let node = await this.getRoot(true)\n if (!node) return this._unlockMaybe()\n\n const seq = (delNode.seq = this.core.length + this.length)\n\n while (true) {\n stack.push(node)\n\n let s = 0\n let e = node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n c = b4a.compare(key, await node.getKey(mid))\n\n if (c === 0) {\n if (cas) {\n const prev = await node.getKeyNode(mid)\n if (!(await cas(prev.final(encoding), delNode))) return this._unlockMaybe()\n }\n if (node.children.length) await setKeyToNearestLeaf(node, mid, stack)\n else node.removeKey(mid)\n // we mark these as changed late, so we don't rewrite them if it is a 404\n for (const node of stack) node.changed = true\n return this._append(await rebalance(stack), seq, key, null)\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n if (!node.children.length) return this._unlockMaybe()\n\n const i = c < 0 ? e : s\n node = await node.getChildNode(i)\n }\n }\n\n async _closeSnapshot() {\n if (this.isSnapshot) {\n await this.core.close()\n this._finalize()\n }\n }\n\n async close() {\n if (this.isSnapshot) return this._closeSnapshot()\n\n this.root = null\n if (this.blocks) this.blocks.clear()\n this.length = 0\n this._unlock()\n }\n\n destroy() {\n // compat, remove later\n this.close().catch(noop)\n }\n\n toBlocks() {\n if (this.appending) return this.appending\n\n const batch = new Array(this.length)\n\n for (let i = 0; i < this.length; i++) {\n const seq = this.core.length + i\n const { pendingIndex, key, value } = this.blocks.get(seq)\n\n if (i < this.length - 1) {\n pendingIndex[0] = null\n let j = 0\n\n while (j < pendingIndex.length) {\n const idx = pendingIndex[j]\n if (idx !== null && idx.seq === seq) {\n idx.offset = j++\n continue\n }\n if (j === pendingIndex.length - 1) pendingIndex.pop()\n else pendingIndex[j] = pendingIndex.pop()\n }\n }\n\n batch[i] = Node.encode({\n key,\n value,\n index: deflate(pendingIndex)\n })\n }\n\n this.appending = batch\n return batch\n }\n\n flush() {\n if (!this.length) return this.close()\n\n const batch = this.toBlocks()\n\n this.root = null\n this.blocks.clear()\n this.length = 0\n\n return this._appendBatch(batch)\n }\n\n _unlockMaybe() {\n if (this.autoFlush) this._unlock()\n }\n\n _unlock() {\n const locked = this.locked\n this.locked = null\n if (locked !== null) locked()\n this._finalize()\n }\n\n _finalize() {\n // technically finalize can be called more than once, so here we just check if we already have been removed\n if (this.index >= this.tree._batches.length || this.tree._batches[this.index] !== this) return\n const top = this.tree._batches.pop()\n if (top === this) return\n top.index = this.index\n this.tree._batches[top.index] = top\n }\n\n _append(root, seq, key, value) {\n const index = []\n root.indexChanges(index, seq)\n index[0] = new Child(seq, 0, root)\n\n if (!this.autoFlush) {\n const block = new BatchEntry(seq, this, key, value, index)\n root.block = block\n this.root = root\n this.length++\n this.blocks.set(seq, block)\n\n root.updateChildren(seq, block)\n return\n }\n\n return this._appendBatch(\n Node.encode({\n key,\n value,\n index: deflate(index)\n })\n )\n }\n\n async _appendBatch(raw) {\n try {\n await this.core.append(raw)\n } finally {\n this._unlock()\n }\n }\n}\n\nclass EntryWatcher extends ReadyResource {\n constructor(bee, key, opts = {}) {\n super()\n\n this.keyEncoding = opts.keyEncoding || bee.keyEncoding\n this.valueEncoding = opts.valueEncoding || bee.valueEncoding\n\n this.index = bee._entryWatchers.push(this) - 1\n this.bee = bee\n\n this.key = key\n this.node = null\n\n this._forceUpdate = false\n this._debouncedUpdate = debounce(this._processUpdate.bind(this))\n }\n\n _close() {\n const top = this.bee._entryWatchers.pop()\n if (top !== this) {\n top.index = this.index\n this.bee._entryWatchers[top.index] = top\n }\n }\n\n _onappend() {\n this._debouncedUpdate()\n }\n\n _ontruncate() {\n this._forceUpdate = true\n this._debouncedUpdate()\n }\n\n async _processUpdate() {\n const force = this._forceUpdate\n this._forceUpdate = false\n\n let newNode\n try {\n newNode = await this.bee.get(this.key, {\n keyEncoding: this.keyEncoding,\n valueEncoding: this.valueEncoding\n })\n } catch (e) {\n if (e.code === 'SNAPSHOT_NOT_AVAILABLE') {\n // There was a truncate event before the get resolved\n // So this handler will run again anyway\n return\n } else if (this.bee.closing) {\n this.close().catch(safetyCatch)\n return\n }\n this.emit('error', e)\n return\n }\n\n if (force || newNode?.seq !== this.node?.seq) {\n this.node = newNode\n this.emit('update')\n }\n }\n}\n\nclass Watcher extends ReadyResource {\n constructor(bee, range, opts = {}) {\n super()\n\n this.keyEncoding = opts.keyEncoding || bee.keyEncoding\n this.valueEncoding = opts.valueEncoding || bee.valueEncoding\n this.index = bee._watchers.push(this) - 1\n this.bee = bee\n this.core = bee.core\n\n this.latestDiff = 0\n this.range = range\n this.map = opts.map || defaultWatchMap\n\n this.current = null\n this.previous = null\n this.currentMapped = null\n this.previousMapped = null\n this.stream = null\n\n this._lock = mutexify()\n this._flowing = false\n this._resolveOnChange = null\n this._differ = opts.differ || defaultDiffer\n this._eager = !!opts.eager\n this._onchange = opts.onchange || null\n\n this.on('newListener', autoFlowOnUpdate)\n\n this.ready().catch(safetyCatch)\n }\n\n async _consume() {\n if (this._flowing) return\n try {\n for await (const _ of this) {\n } // eslint-disable-line\n } catch {}\n }\n\n async _open() {\n await this.bee.ready()\n\n const opts = {\n keyEncoding: this.keyEncoding,\n valueEncoding: this.valueEncoding\n }\n\n // Point from which to start watching\n this.current = this._eager ? this.bee.checkout(1, opts) : this.bee.snapshot(opts)\n await this.current.ready()\n\n if (this._onchange) {\n if (this._eager) await this._onchange()\n this._consume()\n }\n }\n\n [Symbol.asyncIterator]() {\n this._flowing = true\n return this\n }\n\n _ontruncate() {\n this._onappend()\n }\n\n _onappend() {\n const resolve = this._resolveOnChange\n this._resolveOnChange = null\n if (resolve) resolve()\n }\n\n async _waitForChanges() {\n if (this.current.version < this.bee.version || this.closing) return\n\n await new Promise((resolve) => {\n this._resolveOnChange = resolve\n })\n }\n\n async next() {\n try {\n return await this._next()\n } catch (err) {\n if (this.closing) return { value: undefined, done: true }\n await this.close()\n throw err\n }\n }\n\n async _next() {\n const release = await this._lock()\n\n try {\n if (this.closing) return { value: undefined, done: true }\n\n if (!this.opened) await this.ready()\n\n while (true) {\n await this._waitForChanges()\n\n if (this.closing) return { value: undefined, done: true }\n\n await this._closePrevious()\n this.previous = this.current.snapshot()\n\n await this._closeCurrent()\n this.current = this.bee.snapshot({\n keyEncoding: this.keyEncoding,\n valueEncoding: this.valueEncoding\n })\n\n await this.current.ready()\n await this.previous.ready()\n\n if (this.current.core.fork !== this.previous.core.fork) {\n return await this._yield()\n }\n\n this.stream = this._differ(this.current, this.previous, this.range)\n\n try {\n for await (const data of this.stream) {\n // eslint-disable-line\n return await this._yield()\n }\n } finally {\n this.stream = null\n }\n }\n } finally {\n release()\n }\n }\n\n async _yield() {\n this.currentMapped = this.map(this.current)\n this.previousMapped = this.map(this.previous)\n\n if (this._onchange) {\n try {\n await this._onchange()\n } catch (err) {\n safetyCatch(err)\n }\n }\n\n this.emit('update')\n return { done: false, value: [this.currentMapped, this.previousMapped] }\n }\n\n async return() {\n await this.close()\n return { done: true }\n }\n\n async _close() {\n const top = this.bee._watchers.pop()\n if (top !== this) {\n top.index = this.index\n this.bee._watchers[top.index] = top\n }\n\n if (this.stream && !this.stream.destroying) {\n this.stream.destroy()\n }\n\n this._onappend() // Continue execution being closed\n\n await this._closeCurrent().catch(safetyCatch)\n await this._closePrevious().catch(safetyCatch)\n\n const release = await this._lock()\n release()\n }\n\n destroy() {\n return this.close()\n }\n\n async _closeCurrent() {\n if (this.currentMapped) await this.currentMapped.close()\n if (this.current) await this.current.close()\n this.current = this.currentMapped = null\n }\n\n async _closePrevious() {\n if (this.previousMapped) await this.previousMapped.close()\n if (this.previous) await this.previous.close()\n this.previous = this.previousMapped = null\n }\n}\n\nfunction autoFlowOnUpdate(name) {\n if (name === 'update') this._consume()\n}\n\nfunction defaultWatchMap(snapshot) {\n return snapshot\n}\n\nasync function leafSize(node, goLeft) {\n while (node.children.length) node = await node.getChildNode(goLeft ? 0 : node.children.length - 1)\n return node.keys.length\n}\n\nasync function setKeyToNearestLeaf(node, index, stack) {\n let [left, right] = await Promise.all([node.getChildNode(index), node.getChildNode(index + 1)])\n const [ls, rs] = await Promise.all([leafSize(left, false), leafSize(right, true)])\n\n if (ls < rs) {\n // if fewer leaves on the left\n stack.push(right)\n while (right.children.length) stack.push((right = right.children[0].value))\n node.keys[index] = right.keys.shift()\n } else {\n // if fewer leaves on the right\n stack.push(left)\n while (left.children.length) stack.push((left = left.children[left.children.length - 1].value))\n node.keys[index] = left.keys.pop()\n }\n}\n\nasync function rebalance(stack) {\n const root = stack[0]\n\n while (stack.length > 1) {\n const node = stack.pop()\n const parent = stack[stack.length - 1]\n\n if (node.keys.length >= MIN_KEYS) return root\n\n let { left, index, right } = await node.siblings(parent)\n\n // maybe borrow from left sibling?\n if (left && left.keys.length > MIN_KEYS) {\n left.changed = true\n node.keys.unshift(parent.keys[index - 1])\n if (left.children.length) node.children.unshift(left.children.pop())\n parent.keys[index - 1] = left.keys.pop()\n return root\n }\n\n // maybe borrow from right sibling?\n if (right && right.keys.length > MIN_KEYS) {\n right.changed = true\n node.keys.push(parent.keys[index])\n if (right.children.length) node.children.push(right.children.shift())\n parent.keys[index] = right.keys.shift()\n return root\n }\n\n // merge node with another sibling\n if (left) {\n index--\n right = node\n } else {\n left = node\n }\n\n left.merge(right, parent.keys[index])\n parent.removeKey(index)\n }\n\n // check if the tree shrunk\n if (!root.keys.length && root.children.length) return root.getChildNode(0)\n return root\n}\n\nfunction iteratorToStream(ite, signal) {\n let done\n let closing\n\n const rs = new Readable({\n signal,\n open(cb) {\n done = cb\n ite.open().then(fin, fin)\n },\n read(cb) {\n done = cb\n ite.next().then(push, fin)\n },\n predestroy() {\n closing = ite.close()\n closing.catch(noop)\n },\n destroy(cb) {\n done = cb\n if (!closing) closing = ite.close()\n closing.then(fin, fin)\n }\n })\n\n return rs\n\n function fin(err) {\n done(err)\n }\n\n function push(val) {\n rs.push(val)\n done(null)\n }\n}\n\nasync function iteratorPeek(ite) {\n try {\n await ite.open()\n return await ite.next()\n } finally {\n await ite.close()\n }\n}\n\nfunction encRange(e, opts) {\n if (!e) return opts\n\n if (e.encodeRange) {\n const r = e.encodeRange({\n gt: opts.gt,\n gte: opts.gte,\n lt: opts.lt,\n lte: opts.lte\n })\n opts.gt = r.gt\n opts.gte = r.gte\n opts.lt = r.lt\n opts.lte = r.lte\n return opts\n }\n\n if (opts.gt !== undefined) opts.gt = enc(e, opts.gt)\n if (opts.gte !== undefined) opts.gte = enc(e, opts.gte)\n if (opts.lt !== undefined) opts.lt = enc(e, opts.lt)\n if (opts.lte !== undefined) opts.lte = enc(e, opts.lte)\n if (opts.sub && !opts.gt && !opts.gte) opts.gt = enc(e, SEP)\n if (opts.sub && !opts.lt && !opts.lte) opts.lt = bump(enc(e, EMPTY))\n\n return opts\n}\n\nfunction bump(key) {\n // key should have been copied by enc above before hitting this\n key[key.length - 1]++\n return key\n}\n\nfunction enc(e, v) {\n if (v === undefined || v === null) return null\n if (e !== null) return e.encode(v)\n if (typeof v === 'string') return b4a.from(v)\n return v\n}\n\nfunction prefixEncoding(prefix, keyEncoding) {\n return {\n encode(key) {\n return b4a.concat([prefix, b4a.isBuffer(key) ? key : enc(keyEncoding, key)])\n },\n decode(key) {\n const sliced = key.slice(prefix.length, key.length)\n return keyEncoding ? keyEncoding.decode(sliced) : sliced\n }\n }\n}\n\nfunction copyEntry(entry) {\n let key = entry.key\n let value = entry.value\n let index = entry.index\n\n // key, value and index all refer to the same buffer (one hypercore block)\n // If together they are larger than half the buffer's byteLength,\n // this means that they got their own private slab (see Buffer.allocUnsafe docs)\n // so no need to unslab\n const size =\n key.byteLength +\n (value === null ? 0 : value.byteLength) +\n (index === null ? 0 : index.byteLength)\n if (2 * size < key.buffer.byteLength) {\n const [newKey, newValue, newIndex] = unslabAll([entry.key, entry.value, entry.index])\n key = newKey\n value = newValue\n index = newIndex\n }\n\n return {\n key,\n value,\n index,\n inflated: null\n }\n}\n\nfunction defaultDiffer(currentSnap, previousSnap, opts) {\n return currentSnap.createDiffStream(previousSnap, opts)\n}\n\nfunction toNull() {\n return null\n}\n\nfunction getBackingCore(core) {\n if (core.core) return core\n if (core.getBackingCore) return core.getBackingCore().session\n return null\n}\n\nfunction sameValue(a, b) {\n return a === b || (a !== null && b !== null && b4a.equals(a, b))\n}\n\nfunction noop() {}\n\nfunction getExtension(db, opts) {\n if (opts.extension === false) return null\n if (opts.extension && opts.extension !== true) return opts.extension\n return Extension.register(db)\n}\n\nasync function isLinked(batch, block) {\n const seq = block.seq\n block.inflate()\n\n const keys = [block.key]\n const wait = batch.options.wait\n\n for (const l of block.index.levels) {\n if (!l.keys.length) continue\n batch.options.wait = false\n try {\n keys.push(await batch.getKey(l.keys[0].seq))\n } catch {}\n batch.options.wait = wait\n }\n\n for (const k of keys) {\n if (await batch.links(k, seq)) return true\n }\n\n if (batch.core.closing) throw new Error('Core is closed')\n\n return false\n}\n\nmodule.exports = Hyperbee\nconst b4a = require('b4a')\n\nclass SubTree {\n constructor(node, parent) {\n this.node = node\n this.parent = parent\n\n this.isKey = node.children.length === 0\n this.i = this.isKey ? 1 : 0\n this.n = 0\n\n const child = this.isKey ? null : this.node.children[0]\n this.seq = child !== null ? child.seq : this.node.keys[0].seq\n this.offset = child !== null ? child.offset : 0\n }\n\n next() {\n this.i++\n this.isKey = (this.i & 1) === 1\n if (!this.isKey && !this.node.children.length) this.i++\n return this.update()\n }\n\n async bisect(key, incl) {\n let s = 0\n let e = this.node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n c = cmp(key, await this.node.getKey(mid))\n\n if (c === 0) {\n if (incl) this.i = mid * 2 + 1\n else this.i = mid * 2 + (this.node.children.length ? 2 : 3)\n return true\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n const i = c < 0 ? e : s\n this.i = 2 * i + (this.node.children.length ? 0 : 1)\n return this.node.children.length === 0\n }\n\n update() {\n this.isKey = (this.i & 1) === 1\n this.n = this.i >> 1\n if (this.n >= (this.isKey ? this.node.keys.length : this.node.children.length)) return false\n const child = this.isKey ? null : this.node.children[this.n]\n this.seq = child !== null ? child.seq : this.node.keys[this.n].seq\n this.offset = child !== null ? child.offset : 0\n return true\n }\n\n async key() {\n return this.n < this.node.keys.length\n ? this.node.getKey(this.n)\n : this.parent && this.parent.key()\n }\n\n async compare(tree) {\n const [a, b] = await Promise.all([this.key(), tree.key()])\n return cmp(a, b)\n }\n}\n\nclass TreeIterator {\n constructor(batch, opts) {\n this.batch = batch\n this.stack = []\n this.lt = opts.lt || opts.lte || null\n this.lte = !!opts.lte\n this.gt = opts.gt || opts.gte || null\n this.gte = !!opts.gte\n this.seeking = !!this.gt\n this.encoding = opts.encoding || batch.encoding\n }\n\n async open() {\n const node = await this.batch.getRoot(false)\n if (!node || !node.keys.length) return\n const tree = new SubTree(node, null)\n if (this.seeking && !(await this._seek(tree))) return\n this.stack.push(tree)\n }\n\n async _seek(tree) {\n const done = await tree.bisect(this.gt, this.gte)\n const oob = !tree.update()\n if (done || oob) {\n this.seeking = false\n if (oob) return false\n }\n return true\n }\n\n peek() {\n if (!this.stack.length) return null\n return this.stack[this.stack.length - 1]\n }\n\n skip() {\n if (!this.stack.length) return\n if (!this.stack[this.stack.length - 1].next()) this.stack.pop()\n }\n\n async nextKey() {\n let n = null\n while (this.stack.length && n === null) n = await this.next()\n if (n === null) return null\n if (!this.lt) return n.final(this.encoding)\n\n const c = cmp(n.key, this.lt)\n if (this.lte ? c <= 0 : c < 0) return n.final(this.encoding)\n this.stack = []\n return null\n }\n\n async next() {\n if (!this.stack.length) return null\n\n const top = this.stack[this.stack.length - 1]\n const { isKey, n, seq } = top\n\n if (!top.next()) {\n this.stack.pop()\n }\n\n if (isKey) {\n this.seeking = false\n return this.batch.getBlock(seq)\n }\n\n const child = await top.node.getChildNode(n)\n top.node.children[n] = null // unlink to save memory\n const tree = new SubTree(child, top)\n if (this.seeking && !(await this._seek(tree))) return null\n this.stack.push(tree)\n\n return null\n }\n\n close() {\n return this.batch._closeSnapshot()\n }\n}\n\nmodule.exports = class DiffIterator {\n constructor(left, right, opts = {}) {\n this.left = new TreeIterator(left, opts)\n this.right = new TreeIterator(right, opts)\n this.limit = typeof opts.limit === 'number' ? opts.limit : -1\n }\n\n async open() {\n await Promise.all([this.left.open(), this.right.open()])\n }\n\n async next() {\n if (this.limit === 0) return null\n const res = await this._next()\n if (!res || (res.left === null && res.right === null)) return null\n this.limit--\n return res\n }\n\n async _next() {\n const a = this.left\n const b = this.right\n\n while (true) {\n const [l, r] = await Promise.all([a.peek(), b.peek()])\n\n if (!l && !r) return null\n if (!l) return { left: null, right: await b.nextKey() }\n if (!r) return { left: await a.nextKey(), right: null }\n\n if (l.seq === r.seq && l.isKey === r.isKey && l.offset === r.offset) {\n a.skip()\n b.skip()\n continue\n }\n\n const c = await l.compare(r)\n\n if (l.isKey && !r.isKey) {\n await b.next()\n continue\n }\n\n if (!l.isKey && r.isKey) {\n await a.next()\n continue\n }\n\n if (l.isKey && r.isKey) {\n if (c === 0) return { left: await a.nextKey(), right: await b.nextKey() }\n if (c < 0) return { left: await a.nextKey(), right: null }\n return { left: null, right: await b.nextKey() }\n }\n\n if (c === 0) await Promise.all([a.next(), b.next()])\n else if (c < 0) await b.next()\n else await a.next()\n }\n }\n\n async close() {\n await Promise.all([this.left.close(), this.right.close()])\n }\n}\n\nfunction cmp(a, b) {\n if (!a) return b ? 1 : 0\n if (!b) return a ? -1 : 0\n return b4a.compare(a, b)\n}\nmodule.exports = class HistoryIterator {\n constructor(batch, opts = {}) {\n this.batch = batch\n this.options = opts\n this.live = !!opts.live\n this.gte = 0\n this.lt = 0\n this.reverse = !!opts.reverse\n this.limit = typeof opts.limit === 'number' ? opts.limit : -1\n this.encoding = opts.encoding || batch.encoding\n if (this.live && this.reverse) {\n throw new Error('Cannot have both live and reverse enabled')\n }\n }\n\n async open() {\n await this.batch.getRoot(false) // does the update dance\n this.gte = gte(this.options, this.batch.version)\n this.lt = this.live ? Infinity : lt(this.options, this.batch.version)\n }\n\n async next() {\n if (this.limit === 0) return null\n if (this.limit > 0) this.limit--\n\n if (this.gte >= this.lt) return null\n\n if (this.reverse) {\n if (this.lt <= 1) return null\n return final(await this.batch.getBlock(--this.lt), this.encoding)\n }\n\n return final(await this.batch.getBlock(this.gte++), this.encoding)\n }\n\n close() {\n return this.batch._closeSnapshot()\n }\n}\n\nfunction final(node, encoding) {\n const type = node.isDeletion() ? 'del' : 'put'\n return { type, ...node.final(encoding) }\n}\n\nfunction gte(opts, version) {\n if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1\n const gte = opts.gte || opts.since || 1\n return gte < 0 ? gte + version : gte\n}\n\nfunction lt(opts, version) {\n if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0\n if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1\n const lt = opts.lt || opts.end || version\n return lt < 0 ? lt + version : lt\n}\nmodule.exports = class LocalBlocksIterator {\n constructor(batch, opts = {}) {\n this.batch = batch\n this.options = opts\n this.gte = 0\n this.lt = 0\n this.limit = typeof opts.limit === 'number' ? opts.limit : -1\n }\n\n async open() {\n await this.batch.getRoot(false) // does the update dance\n this.gte = gte(this.options, this.batch.version)\n this.lt = lt(this.options, this.batch.version)\n }\n\n async next() {\n if (this.limit === 0) return null\n if (this.limit > 0) this.limit--\n\n while (this.gte < this.lt) {\n try {\n return await this.batch.getBlock(this.gte++)\n } catch {\n continue\n }\n }\n\n return null\n }\n\n close() {\n return this.batch._closeSnapshot()\n }\n}\n\nfunction gte(opts, version) {\n if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1\n const gte = opts.gte || opts.since || 1\n return gte < 0 ? gte + version : gte\n}\n\nfunction lt(opts, version) {\n if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0\n if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1\n const lt = opts.lt || opts.end || version\n return lt < 0 ? lt + version : lt\n}\nconst b4a = require('b4a')\n\nmodule.exports = class RangeIterator {\n constructor(batch, encoding, opts = {}) {\n this.batch = batch\n this.stack = []\n this.opened = false\n this.encoding = encoding || batch.encoding\n\n this._limit = typeof opts.limit === 'number' ? opts.limit : -1\n this._gIncl = !opts.gt\n this._gKey = opts.gt || opts.gte || null\n this._lIncl = !opts.lt\n this._lKey = opts.lt || opts.lte || null\n this._reverse = !!opts.reverse\n this._version = 0\n this._checkpoint = opts.checkpoint && opts.checkpoint.length ? opts.checkpoint : null\n this._nexting = false\n this._closed = false\n }\n\n snapshot(version = this.batch.version) {\n const checkpoint = []\n for (const s of this.stack) {\n let { node, i } = s\n if (this._nexting && s === this.stack[this.stack.length - 1]) {\n i = this._reverse ? i + 1 : i - 1\n }\n if (!node.block) continue\n if (i < 0) continue\n checkpoint.push(node.block.seq, node.offset, i)\n }\n\n return {\n version,\n gte: this._gIncl ? this._gKey : null,\n gt: this._gIncl ? null : this._gKey,\n lte: this._lIncl ? this._lKey : null,\n lt: this._lIncl ? null : this._lKey,\n limit: this._limit,\n reverse: this._reverse,\n ended: this.opened && !checkpoint.length,\n checkpoint: this.opened ? checkpoint : []\n }\n }\n\n async open() {\n await this._open()\n this.opened = true\n }\n\n async _open() {\n if (this._checkpoint) {\n for (let j = 0; j < this._checkpoint.length; j += 3) {\n const seq = this._checkpoint[j]\n const offset = this._checkpoint[j + 1]\n const i = this._checkpoint[j + 2]\n this.stack.push({\n node: (await this.batch.getBlock(seq)).getTreeNode(offset),\n i\n })\n }\n return\n }\n\n this._nexting = true\n\n let node = await this.batch.getRoot(false)\n if (!node) {\n this._nexting = false\n return\n }\n\n const incl = this._reverse ? this._lIncl : this._gIncl\n const start = this._reverse ? this._lKey : this._gKey\n\n if (!start) {\n this.stack.push({ node, i: this._reverse ? node.keys.length << 1 : 0 })\n this._nexting = false\n this._preloadQuery()\n return\n }\n\n while (true) {\n const entry = { node, i: this._reverse ? node.keys.length << 1 : 0 }\n\n let s = 0\n let e = node.keys.length\n let c\n\n while (s < e) {\n const mid = (s + e) >> 1\n c = b4a.compare(start, await node.getKey(mid))\n\n if (c === 0) {\n if (incl) entry.i = mid * 2 + 1\n else entry.i = mid * 2 + (this._reverse ? 0 : 2)\n this.stack.push(entry)\n this._nexting = false\n this._preloadQuery()\n return\n }\n\n if (c < 0) e = mid\n else s = mid + 1\n }\n\n const i = c < 0 ? e : s\n entry.i = 2 * i + (this._reverse ? -1 : 1)\n\n if (entry.i >= 0 && entry.i <= node.keys.length << 1) this.stack.push(entry)\n if (!node.children.length) {\n this._nexting = false\n this._preloadQuery()\n return\n }\n\n node = await node.getChildNode(i)\n }\n }\n\n _preloadQuery() {\n const max = { nodes: 0, max: 2048 }\n\n if (this._limit === -1 && !this._reverse && this.stack.length) {\n for (const s of this.stack) {\n const k = (s.i - (s.i & 1)) / 2\n this._preload(s.node, k + 1, this._lKey, max).catch(noop)\n }\n }\n }\n\n async _preload(node, i, end, max) {\n for (; i < node.keys.length; i++) {\n const key = node.keys[i]\n const block = await this.batch.getBlock(key.seq)\n if (this._closed) return\n const c = end ? b4a.compare(block.key, end) : -1\n if (c >= 0 || max.nodes >= max.max || i >= node.children.length) return\n max.nodes++\n const next = await node.getChildNode(i)\n if (this._closed) return\n this._preload(next, 0, end, max).catch(noop)\n }\n\n if (node.children.length && max.nodes < max.max) {\n const next = await node.getChildNode(node.children.length - 1)\n if (this._closed) return\n this._preload(next, 0, end, max).catch(noop)\n }\n }\n\n async next() {\n // TODO: this nexting flag is only needed if someone asks for a snapshot during\n // a lookup (ie the extension, pretty important...).\n // A better solution would be to refactor this so top.i is incremented eagerly\n // to get the current block instead of the way it is done now (++i vs i++)\n this._nexting = true\n\n const end = this._reverse ? this._gKey : this._lKey\n const incl = this._reverse ? this._gIncl : this._lIncl\n\n while (this.stack.length && (this._limit === -1 || this._limit > 0)) {\n const top = this.stack[this.stack.length - 1]\n const isKey = (top.i & 1) === 1\n const n = this._reverse ? (top.i < 0 ? top.node.keys.length : top.i-- >> 1) : top.i++ >> 1\n\n if (!isKey) {\n if (!top.node.children.length) continue\n const node = await top.node.getChildNode(n)\n if (top.node.block.seq < this.batch.core.length) {\n top.node.children[n].value = null // unlink it to save memory\n }\n this.stack.push({ i: this._reverse ? node.keys.length << 1 : 0, node })\n continue\n }\n\n if (n >= top.node.keys.length) {\n this.stack.pop()\n continue\n }\n\n const key = top.node.keys[n]\n const block = await this.batch.getBlock(key.seq)\n if (end) {\n const c = b4a.compare(block.key, end)\n if (c === 0 ? !incl : this._reverse ? c < 0 : c > 0) {\n this._limit = 0\n break\n }\n }\n if (this._limit > 0) this._limit--\n this._nexting = false\n return block.final(this.encoding)\n }\n\n this._nexting = false\n return null\n }\n\n close() {\n this._closed = true\n return this.batch._closeSnapshot()\n }\n}\n\nfunction noop() {}\nconst { Extension } = require('./messages')\n\n// const MAX_ACTIVE = 32\nconst FLUSH_BATCH = 128\nconst MAX_PASSIVE_BATCH = 2048\nconst MAX_ACTIVE_BATCH = MAX_PASSIVE_BATCH + FLUSH_BATCH\n\nclass Batch {\n constructor(outgoing, from) {\n this.blocks = []\n this.start = 0\n this.end = 0\n this.outgoing = outgoing\n this.from = from\n }\n\n push(seq) {\n const len = this.blocks.push(seq)\n if (len === 1 || seq < this.start) this.start = seq\n if (len === 1 || seq >= this.end) this.end = seq + 1\n if (len >= FLUSH_BATCH) {\n this.send()\n this.clear()\n }\n }\n\n send() {\n if (!this.blocks.length) return\n this.outgoing.send(\n Extension.encode({\n cache: { blocks: this.blocks, start: this.start, end: this.end }\n }),\n this.from\n )\n }\n\n clear() {\n this.start = this.end = 0\n this.blocks = []\n }\n}\n\nclass HyperbeeExtension {\n constructor(db) {\n this.encoding = null\n this.outgoing = null\n this.db = db\n this.active = 0\n }\n\n get(version, key) {\n this.outgoing.broadcast(Extension.encode({ get: { version, key } }))\n }\n\n iterator(snapshot) {\n if (snapshot.ended) return\n if (snapshot.limit === 0) return\n if (snapshot.limit === -1) snapshot.limit = 0\n this.outgoing.broadcast(Extension.encode({ iterator: snapshot }))\n }\n\n onmessage(buf, from) {\n // TODO: handle max active extension messages\n // this.active++\n\n const message = decode(buf)\n if (!message) return\n\n if (message.cache) this.oncache(message.cache, from)\n if (message.get) this.onget(message.get, from)\n if (message.iterator) this.oniterator(message.iterator, from)\n }\n\n oncache(message, from) {\n if (!message.blocks.length) return\n this.db.core.download(message)\n }\n\n onget(message, from) {\n if (!message.version || message.version > this.db.version) return\n\n const b = new Batch(this.outgoing, from)\n const db = this.db.checkout(message.version)\n\n db.get(message.key, {\n extension: false,\n wait: false,\n update: false,\n onseq\n }).then(done, done)\n\n function done() {\n db.close().catch(noop)\n b.send()\n }\n\n function onseq(seq) {\n b.push(seq)\n }\n }\n\n async oniterator(message, from) {\n if (!message.version || message.version > this.db.version) return\n\n const b = new Batch(this.outgoing, from)\n const seqs = new Set()\n\n let skip = message.checkpoint.length\n let work = 0\n\n const db = this.db.checkout(message.version)\n const ite = db.createRangeIterator({\n ...message,\n wait: false,\n extension: false,\n update: false,\n limit: message.limit === 0 ? -1 : message.limit,\n onseq(seq) {\n if (skip && skip--) return\n if (seqs.has(seq)) return\n work++\n seqs.add(seq)\n b.push(seq)\n }\n })\n\n try {\n await ite.open()\n // eslint-disable-next-line no-unmodified-loop-condition\n while (work < MAX_ACTIVE_BATCH) {\n if (!(await ite.next())) break\n }\n } catch (_) {\n // do nothing\n } finally {\n ite.close().catch(noop)\n db.close().catch(noop)\n b.send()\n }\n }\n\n static register(db) {\n const e = new this(db)\n e.outgoing = db.core.registerExtension('hyperbee', e)\n return e\n }\n}\n\nHyperbeeExtension.BATCH_SIZE = MAX_PASSIVE_BATCH\n\nmodule.exports = HyperbeeExtension\n\nfunction decode(buf) {\n try {\n return Extension.decode(buf)\n } catch (err) {\n return null\n }\n}\n\nfunction noop() {}\n// This file is auto generated by the protocol-buffers compiler\n\n/* eslint-disable quotes */\n/* eslint-disable indent */\n/* eslint-disable no-redeclare */\n/* eslint-disable camelcase */\n/* eslint-disable no-var */\n\n// Remember to `npm install --save protocol-buffers-encodings`\nvar encodings = require('protocol-buffers-encodings')\nvar b4a = require('b4a')\nvar varint = encodings.varint\nvar skip = encodings.skip\n\nvar YoloIndex = exports.YoloIndex = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n}\n\nvar Header = exports.Header = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n}\n\nvar Node = exports.Node = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n}\n\nvar Extension = exports.Extension = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n}\n\ndefineYoloIndex()\ndefineHeader()\ndefineNode()\ndefineExtension()\n\nfunction defineYoloIndex () {\n var Level = YoloIndex.Level = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n }\n\n defineLevel()\n\n function defineLevel () {\n Level.encodingLength = encodingLength\n Level.encode = encode\n Level.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.keys)) {\n var packedLen = 0\n for (var i = 0; i < obj.keys.length; i++) {\n if (!defined(obj.keys[i])) continue\n var len = encodings.varint.encodingLength(obj.keys[i])\n packedLen += len\n }\n if (packedLen) {\n length += 1 + packedLen + varint.encodingLength(packedLen)\n }\n }\n if (defined(obj.children)) {\n var packedLen = 0\n for (var i = 0; i < obj.children.length; i++) {\n if (!defined(obj.children[i])) continue\n var len = encodings.varint.encodingLength(obj.children[i])\n packedLen += len\n }\n if (packedLen) {\n length += 1 + packedLen + varint.encodingLength(packedLen)\n }\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.keys)) {\n var packedLen = 0\n for (var i = 0; i < obj.keys.length; i++) {\n if (!defined(obj.keys[i])) continue\n packedLen += encodings.varint.encodingLength(obj.keys[i])\n }\n if (packedLen) {\n buf[offset++] = 10\n varint.encode(packedLen, buf, offset)\n offset += varint.encode.bytes\n }\n for (var i = 0; i < obj.keys.length; i++) {\n if (!defined(obj.keys[i])) continue\n encodings.varint.encode(obj.keys[i], buf, offset)\n offset += encodings.varint.encode.bytes\n }\n }\n if (defined(obj.children)) {\n var packedLen = 0\n for (var i = 0; i < obj.children.length; i++) {\n if (!defined(obj.children[i])) continue\n packedLen += encodings.varint.encodingLength(obj.children[i])\n }\n if (packedLen) {\n buf[offset++] = 18\n varint.encode(packedLen, buf, offset)\n offset += varint.encode.bytes\n }\n for (var i = 0; i < obj.children.length; i++) {\n if (!defined(obj.children[i])) continue\n encodings.varint.encode(obj.children[i], buf, offset)\n offset += encodings.varint.encode.bytes\n }\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n keys: [],\n children: []\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n var packedEnd = varint.decode(buf, offset)\n offset += varint.decode.bytes\n packedEnd += offset\n while (offset < packedEnd) {\n obj.keys.push(encodings.varint.decode(buf, offset))\n offset += encodings.varint.decode.bytes\n }\n break\n case 2:\n var packedEnd = varint.decode(buf, offset)\n offset += varint.decode.bytes\n packedEnd += offset\n while (offset < packedEnd) {\n obj.children.push(encodings.varint.decode(buf, offset))\n offset += encodings.varint.decode.bytes\n }\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n }\n\n YoloIndex.encodingLength = encodingLength\n YoloIndex.encode = encode\n YoloIndex.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.levels)) {\n for (var i = 0; i < obj.levels.length; i++) {\n if (!defined(obj.levels[i])) continue\n var len = Level.encodingLength(obj.levels[i])\n length += varint.encodingLength(len)\n length += 1 + len\n }\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.levels)) {\n for (var i = 0; i < obj.levels.length; i++) {\n if (!defined(obj.levels[i])) continue\n buf[offset++] = 10\n varint.encode(Level.encodingLength(obj.levels[i]), buf, offset)\n offset += varint.encode.bytes\n Level.encode(obj.levels[i], buf, offset)\n offset += Level.encode.bytes\n }\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n levels: []\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n var len = varint.decode(buf, offset)\n offset += varint.decode.bytes\n obj.levels.push(Level.decode(buf, offset, offset + len))\n offset += Level.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n}\n\nfunction defineHeader () {\n var Metadata = Header.Metadata = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n }\n\n defineMetadata()\n\n function defineMetadata () {\n Metadata.encodingLength = encodingLength\n Metadata.encode = encode\n Metadata.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.contentFeed)) {\n var len = encodings.bytes.encodingLength(obj.contentFeed)\n length += 1 + len\n }\n if (defined(obj.userData)) {\n var len = encodings.bytes.encodingLength(obj.userData)\n length += 1 + len\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.contentFeed)) {\n buf[offset++] = 10\n encodings.bytes.encode(obj.contentFeed, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n if (defined(obj.userData)) {\n buf[offset++] = 18\n encodings.bytes.encode(obj.userData, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n contentFeed: null,\n userData: null\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.contentFeed = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n case 2:\n obj.userData = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n }\n\n Header.encodingLength = encodingLength\n Header.encode = encode\n Header.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (!defined(obj.protocol)) throw new Error(\"protocol is required\")\n var len = encodings.string.encodingLength(obj.protocol)\n length += 1 + len\n if (defined(obj.metadata)) {\n var len = Metadata.encodingLength(obj.metadata)\n length += varint.encodingLength(len)\n length += 1 + len\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (!defined(obj.protocol)) throw new Error(\"protocol is required\")\n buf[offset++] = 10\n encodings.string.encode(obj.protocol, buf, offset)\n offset += encodings.string.encode.bytes\n if (defined(obj.metadata)) {\n buf[offset++] = 18\n varint.encode(Metadata.encodingLength(obj.metadata), buf, offset)\n offset += varint.encode.bytes\n Metadata.encode(obj.metadata, buf, offset)\n offset += Metadata.encode.bytes\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n protocol: \"\",\n metadata: null\n }\n var found0 = false\n while (true) {\n if (end <= offset) {\n if (!found0) throw new Error(\"Decoded message is not valid\")\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.protocol = encodings.string.decode(buf, offset)\n offset += encodings.string.decode.bytes\n found0 = true\n break\n case 2:\n var len = varint.decode(buf, offset)\n offset += varint.decode.bytes\n obj.metadata = Metadata.decode(buf, offset, offset + len)\n offset += Metadata.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n}\n\nfunction defineNode () {\n Node.encodingLength = encodingLength\n Node.encode = encode\n Node.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (!defined(obj.index)) throw new Error(\"index is required\")\n var len = encodings.bytes.encodingLength(obj.index)\n length += 1 + len\n if (!defined(obj.key)) throw new Error(\"key is required\")\n var len = encodings.bytes.encodingLength(obj.key)\n length += 1 + len\n if (defined(obj.value)) {\n var len = encodings.bytes.encodingLength(obj.value)\n length += 1 + len\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (!defined(obj.index)) throw new Error(\"index is required\")\n buf[offset++] = 10\n encodings.bytes.encode(obj.index, buf, offset)\n offset += encodings.bytes.encode.bytes\n if (!defined(obj.key)) throw new Error(\"key is required\")\n buf[offset++] = 18\n encodings.bytes.encode(obj.key, buf, offset)\n offset += encodings.bytes.encode.bytes\n if (defined(obj.value)) {\n buf[offset++] = 26\n encodings.bytes.encode(obj.value, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n index: null,\n key: null,\n value: null\n }\n var found0 = false\n var found1 = false\n while (true) {\n if (end <= offset) {\n if (!found0 || !found1) throw new Error(\"Decoded message is not valid\")\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.index = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n found0 = true\n break\n case 2:\n obj.key = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n found1 = true\n break\n case 3:\n obj.value = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n}\n\nfunction defineExtension () {\n var Get = Extension.Get = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n }\n\n var Iterator = Extension.Iterator = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n }\n\n var Cache = Extension.Cache = {\n buffer: true,\n encodingLength: null,\n encode: null,\n decode: null\n }\n\n defineGet()\n defineIterator()\n defineCache()\n\n function defineGet () {\n Get.encodingLength = encodingLength\n Get.encode = encode\n Get.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.version)) {\n var len = encodings.varint.encodingLength(obj.version)\n length += 1 + len\n }\n if (defined(obj.key)) {\n var len = encodings.bytes.encodingLength(obj.key)\n length += 1 + len\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.version)) {\n buf[offset++] = 8\n encodings.varint.encode(obj.version, buf, offset)\n offset += encodings.varint.encode.bytes\n }\n if (defined(obj.key)) {\n buf[offset++] = 18\n encodings.bytes.encode(obj.key, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n version: 0,\n key: null\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.version = encodings.varint.decode(buf, offset)\n offset += encodings.varint.decode.bytes\n break\n case 2:\n obj.key = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n }\n\n function defineIterator () {\n Iterator.encodingLength = encodingLength\n Iterator.encode = encode\n Iterator.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.version)) {\n var len = encodings.varint.encodingLength(obj.version)\n length += 1 + len\n }\n if (defined(obj.gte)) {\n var len = encodings.bytes.encodingLength(obj.gte)\n length += 1 + len\n }\n if (defined(obj.gt)) {\n var len = encodings.bytes.encodingLength(obj.gt)\n length += 1 + len\n }\n if (defined(obj.lte)) {\n var len = encodings.bytes.encodingLength(obj.lte)\n length += 1 + len\n }\n if (defined(obj.lt)) {\n var len = encodings.bytes.encodingLength(obj.lt)\n length += 1 + len\n }\n if (defined(obj.limit)) {\n var len = encodings.varint.encodingLength(obj.limit)\n length += 1 + len\n }\n if (defined(obj.reverse)) {\n var len = encodings.bool.encodingLength(obj.reverse)\n length += 1 + len\n }\n if (defined(obj.checkpoint)) {\n var packedLen = 0\n for (var i = 0; i < obj.checkpoint.length; i++) {\n if (!defined(obj.checkpoint[i])) continue\n var len = encodings.varint.encodingLength(obj.checkpoint[i])\n packedLen += len\n }\n if (packedLen) {\n length += 1 + packedLen + varint.encodingLength(packedLen)\n }\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.version)) {\n buf[offset++] = 8\n encodings.varint.encode(obj.version, buf, offset)\n offset += encodings.varint.encode.bytes\n }\n if (defined(obj.gte)) {\n buf[offset++] = 18\n encodings.bytes.encode(obj.gte, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n if (defined(obj.gt)) {\n buf[offset++] = 26\n encodings.bytes.encode(obj.gt, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n if (defined(obj.lte)) {\n buf[offset++] = 34\n encodings.bytes.encode(obj.lte, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n if (defined(obj.lt)) {\n buf[offset++] = 42\n encodings.bytes.encode(obj.lt, buf, offset)\n offset += encodings.bytes.encode.bytes\n }\n if (defined(obj.limit)) {\n buf[offset++] = 48\n encodings.varint.encode(obj.limit, buf, offset)\n offset += encodings.varint.encode.bytes\n }\n if (defined(obj.reverse)) {\n buf[offset++] = 56\n encodings.bool.encode(obj.reverse, buf, offset)\n offset += encodings.bool.encode.bytes\n }\n if (defined(obj.checkpoint)) {\n var packedLen = 0\n for (var i = 0; i < obj.checkpoint.length; i++) {\n if (!defined(obj.checkpoint[i])) continue\n packedLen += encodings.varint.encodingLength(obj.checkpoint[i])\n }\n if (packedLen) {\n buf[offset++] = 66\n varint.encode(packedLen, buf, offset)\n offset += varint.encode.bytes\n }\n for (var i = 0; i < obj.checkpoint.length; i++) {\n if (!defined(obj.checkpoint[i])) continue\n encodings.varint.encode(obj.checkpoint[i], buf, offset)\n offset += encodings.varint.encode.bytes\n }\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n version: 0,\n gte: null,\n gt: null,\n lte: null,\n lt: null,\n limit: 0,\n reverse: false,\n checkpoint: []\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.version = encodings.varint.decode(buf, offset)\n offset += encodings.varint.decode.bytes\n break\n case 2:\n obj.gte = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n case 3:\n obj.gt = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n case 4:\n obj.lte = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n case 5:\n obj.lt = encodings.bytes.decode(buf, offset)\n offset += encodings.bytes.decode.bytes\n break\n case 6:\n obj.limit = encodings.varint.decode(buf, offset)\n offset += encodings.varint.decode.bytes\n break\n case 7:\n obj.reverse = encodings.bool.decode(buf, offset)\n offset += encodings.bool.decode.bytes\n break\n case 8:\n var packedEnd = varint.decode(buf, offset)\n offset += varint.decode.bytes\n packedEnd += offset\n while (offset < packedEnd) {\n obj.checkpoint.push(encodings.varint.decode(buf, offset))\n offset += encodings.varint.decode.bytes\n }\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n }\n\n function defineCache () {\n Cache.encodingLength = encodingLength\n Cache.encode = encode\n Cache.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (!defined(obj.start)) throw new Error(\"start is required\")\n var len = encodings.varint.encodingLength(obj.start)\n length += 1 + len\n if (!defined(obj.end)) throw new Error(\"end is required\")\n var len = encodings.varint.encodingLength(obj.end)\n length += 1 + len\n if (defined(obj.blocks)) {\n var packedLen = 0\n for (var i = 0; i < obj.blocks.length; i++) {\n if (!defined(obj.blocks[i])) continue\n var len = encodings.varint.encodingLength(obj.blocks[i])\n packedLen += len\n }\n if (packedLen) {\n length += 1 + packedLen + varint.encodingLength(packedLen)\n }\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (!defined(obj.start)) throw new Error(\"start is required\")\n buf[offset++] = 8\n encodings.varint.encode(obj.start, buf, offset)\n offset += encodings.varint.encode.bytes\n if (!defined(obj.end)) throw new Error(\"end is required\")\n buf[offset++] = 16\n encodings.varint.encode(obj.end, buf, offset)\n offset += encodings.varint.encode.bytes\n if (defined(obj.blocks)) {\n var packedLen = 0\n for (var i = 0; i < obj.blocks.length; i++) {\n if (!defined(obj.blocks[i])) continue\n packedLen += encodings.varint.encodingLength(obj.blocks[i])\n }\n if (packedLen) {\n buf[offset++] = 26\n varint.encode(packedLen, buf, offset)\n offset += varint.encode.bytes\n }\n for (var i = 0; i < obj.blocks.length; i++) {\n if (!defined(obj.blocks[i])) continue\n encodings.varint.encode(obj.blocks[i], buf, offset)\n offset += encodings.varint.encode.bytes\n }\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n start: 0,\n end: 0,\n blocks: []\n }\n var found0 = false\n var found1 = false\n while (true) {\n if (end <= offset) {\n if (!found0 || !found1) throw new Error(\"Decoded message is not valid\")\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n obj.start = encodings.varint.decode(buf, offset)\n offset += encodings.varint.decode.bytes\n found0 = true\n break\n case 2:\n obj.end = encodings.varint.decode(buf, offset)\n offset += encodings.varint.decode.bytes\n found1 = true\n break\n case 3:\n var packedEnd = varint.decode(buf, offset)\n offset += varint.decode.bytes\n packedEnd += offset\n while (offset < packedEnd) {\n obj.blocks.push(encodings.varint.decode(buf, offset))\n offset += encodings.varint.decode.bytes\n }\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n }\n\n Extension.encodingLength = encodingLength\n Extension.encode = encode\n Extension.decode = decode\n\n function encodingLength (obj) {\n var length = 0\n if (defined(obj.cache)) {\n var len = Cache.encodingLength(obj.cache)\n length += varint.encodingLength(len)\n length += 1 + len\n }\n if (defined(obj.get)) {\n var len = Get.encodingLength(obj.get)\n length += varint.encodingLength(len)\n length += 1 + len\n }\n if (defined(obj.iterator)) {\n var len = Iterator.encodingLength(obj.iterator)\n length += varint.encodingLength(len)\n length += 1 + len\n }\n return length\n }\n\n function encode (obj, buf, offset) {\n if (!offset) offset = 0\n if (!buf) buf = b4a.allocUnsafe(encodingLength(obj))\n var oldOffset = offset\n if (defined(obj.cache)) {\n buf[offset++] = 10\n varint.encode(Cache.encodingLength(obj.cache), buf, offset)\n offset += varint.encode.bytes\n Cache.encode(obj.cache, buf, offset)\n offset += Cache.encode.bytes\n }\n if (defined(obj.get)) {\n buf[offset++] = 18\n varint.encode(Get.encodingLength(obj.get), buf, offset)\n offset += varint.encode.bytes\n Get.encode(obj.get, buf, offset)\n offset += Get.encode.bytes\n }\n if (defined(obj.iterator)) {\n buf[offset++] = 26\n varint.encode(Iterator.encodingLength(obj.iterator), buf, offset)\n offset += varint.encode.bytes\n Iterator.encode(obj.iterator, buf, offset)\n offset += Iterator.encode.bytes\n }\n encode.bytes = offset - oldOffset\n return buf\n }\n\n function decode (buf, offset, end) {\n if (!offset) offset = 0\n if (!end) end = buf.length\n if (!(end <= buf.length && offset <= buf.length)) throw new Error(\"Decoded message is not valid\")\n var oldOffset = offset\n var obj = {\n cache: null,\n get: null,\n iterator: null\n }\n while (true) {\n if (end <= offset) {\n decode.bytes = offset - oldOffset\n return obj\n }\n var prefix = varint.decode(buf, offset)\n offset += varint.decode.bytes\n var tag = prefix >> 3\n switch (tag) {\n case 1:\n var len = varint.decode(buf, offset)\n offset += varint.decode.bytes\n obj.cache = Cache.decode(buf, offset, offset + len)\n offset += Cache.decode.bytes\n break\n case 2:\n var len = varint.decode(buf, offset)\n offset += varint.decode.bytes\n obj.get = Get.decode(buf, offset, offset + len)\n offset += Get.decode.bytes\n break\n case 3:\n var len = varint.decode(buf, offset)\n offset += varint.decode.bytes\n obj.iterator = Iterator.decode(buf, offset, offset + len)\n offset += Iterator.decode.bytes\n break\n default:\n offset = skip(prefix & 7, buf, offset)\n }\n }\n }\n}\n\nfunction defined (val) {\n return val !== null && val !== undefined && (typeof val !== 'number' || !isNaN(val))\n}\n{\n \"name\": \"hyperbee\",\n \"version\": \"2.27.3\",\n \"description\": \"An append-only B-tree running on a Hypercore.\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/**.js\",\n \"iterators/**.js\"\n ],\n \"dependencies\": {\n \"b4a\": \"^1.6.0\",\n \"codecs\": \"^3.0.0\",\n \"debounceify\": \"^1.0.0\",\n \"hypercore-errors\": \"^1.0.0\",\n \"mutexify\": \"^1.4.0\",\n \"protocol-buffers-encodings\": \"^1.2.0\",\n \"rache\": \"^1.0.0\",\n \"ready-resource\": \"^1.0.0\",\n \"resolve-reject-promise\": \"^1.1.0\",\n \"safety-catch\": \"^1.0.2\",\n \"streamx\": \"^2.12.4\",\n \"unslab\": \"^1.2.0\"\n },\n \"devDependencies\": {\n \"bare-process\": \"^4.2.2\",\n \"brittle\": \"^3.1.0\",\n \"hypercore\": \"^11.0.0\",\n \"lunte\": \"^1.0.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"protocol-buffers\": \"^4.2.0\",\n \"sub-encoder\": \"^1.0.6\",\n \"tree-to-string\": \"^1.1.1\"\n },\n \"imports\": {\n \"process\": {\n \"bare\": \"bare-process\",\n \"default\": \"process\"\n }\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:node\": \"brittle-node test/*.js\",\n \"test:bare\": \"brittle-bare test/*.js\",\n \"protobuf\": \"protocol-buffers schema.proto -o ./lib/messages.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/hyperbee.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperbee/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperbee\"\n}\nconst mutexify = require('mutexify')\nconst b4a = require('b4a')\n\nconst { BlobReadStream, BlobWriteStream } = require('./lib/streams')\nconst Monitor = require('./lib/monitor')\n\nconst DEFAULT_BLOCK_SIZE = 2 ** 16\n\nclass HyperBlobsBatch {\n constructor (blobs) {\n this.blobs = blobs\n this.blocks = []\n this.bytes = 0\n }\n\n ready () {\n return this.blobs.ready()\n }\n\n async put (buffer) {\n if (!this.blobs.core.opened) await this.blobs.core.ready()\n\n const blockSize = this.blobs.blockSize\n const result = {\n blockOffset: this.blobs.core.length + this.blocks.length,\n blockLength: 0,\n byteOffset: this.blobs.core.byteLength + this.bytes,\n byteLength: 0\n }\n\n let offset = 0\n while (offset < buffer.byteLength) {\n const blk = buffer.subarray(offset, offset + blockSize)\n offset += blockSize\n\n result.blockLength++\n result.byteLength += blk.byteLength\n this.bytes += blk.byteLength\n this.blocks.push(blk)\n }\n\n return result\n }\n\n async get (id) {\n if (id.blockOffset < this.blobs.core.length) {\n return this.blobs.get(id)\n }\n\n const bufs = []\n\n for (let i = id.blockOffset - this.blobs.core.length; i < id.blockOffset + id.blockLength; i++) {\n if (i >= this.blocks.length) return null\n bufs.push(this.blocks[i])\n }\n\n return bufs.length === 1 ? bufs[0] : b4a.concat(bufs)\n }\n\n async flush () {\n await this.blobs.core.append(this.blocks)\n this.blocks = []\n this.bytes = 0\n }\n\n close () {\n // noop, atm nothing to unlink\n }\n}\n\nclass Hyperblobs {\n constructor (core, opts = {}) {\n this.core = core\n this.blockSize = opts.blockSize || DEFAULT_BLOCK_SIZE\n\n this._lock = mutexify()\n this._monitors = new Set()\n\n this._boundUpdatePeers = this._updatePeers.bind(this)\n this._boundOnUpload = this._onUpload.bind(this)\n this._boundOnDownload = this._onDownload.bind(this)\n }\n\n get key () {\n return this.core.key\n }\n\n get discoveryKey () {\n return this.core.discoveryKey\n }\n\n get feed () {\n return this.core\n }\n\n get locked () {\n return this._lock.locked\n }\n\n replicate (isInitiator, opts) {\n return this.core.replicate(isInitiator, opts)\n }\n\n ready () {\n return this.core.ready()\n }\n\n close () {\n return this.core.close()\n }\n\n batch () {\n return new HyperBlobsBatch(this)\n }\n\n snapshot () {\n return new Hyperblobs(this.core.snapshot())\n }\n\n async put (blob, opts) {\n if (!b4a.isBuffer(blob)) blob = b4a.from(blob)\n const blockSize = (opts && opts.blockSize) || this.blockSize\n\n const stream = this.createWriteStream(opts)\n for (let i = 0; i < blob.length; i += blockSize) {\n stream.write(blob.subarray(i, i + blockSize))\n }\n stream.end()\n\n return new Promise((resolve, reject) => {\n stream.once('error', reject)\n stream.once('close', () => resolve(stream.id))\n })\n }\n\n async _getAll (id, opts) {\n if (id.blockLength === 1) return this.core.get(id.blockOffset, opts)\n\n const promises = new Array(id.blockLength)\n for (let i = 0; i < id.blockLength; i++) {\n promises[i] = this.core.get(id.blockOffset + i, opts)\n }\n\n const blocks = await Promise.all(promises)\n for (let i = 0; i < id.blockLength; i++) {\n if (blocks[i] === null) return null\n }\n return b4a.concat(blocks)\n }\n\n async get (id, opts) {\n const all = !opts || (!opts.start && opts.length === undefined && opts.end === undefined && !opts.core)\n if (all) return this._getAll(id, opts)\n\n const res = []\n try {\n for await (const block of this.createReadStream(id, opts)) {\n res.push(block)\n }\n } catch (error) {\n if (error.code === 'BLOCK_NOT_AVAILABLE') return null\n throw error\n }\n\n if (res.length === 1) return res[0]\n return b4a.concat(res)\n }\n\n async clear (id, opts) {\n return this.core.clear(id.blockOffset, id.blockOffset + id.blockLength, opts)\n }\n\n createReadStream (id, opts) {\n const core = (opts && opts.core) ? opts.core : this.core\n return new BlobReadStream(core, id, opts)\n }\n\n createWriteStream (opts) {\n const core = (opts && opts.core) ? opts.core : this.core\n return new BlobWriteStream(core, this._lock, opts)\n }\n\n monitor (id) {\n const monitor = new Monitor(this, id)\n if (this._monitors.size === 0) this._startListening()\n this._monitors.add(monitor)\n return monitor\n }\n\n _removeMonitor (mon) {\n this._monitors.delete(mon)\n if (this._monitors.size === 0) this._stopListening()\n }\n\n _updatePeers () {\n for (const m of this._monitors) m._updatePeers()\n }\n\n _onUpload (index, bytes, from) {\n for (const m of this._monitors) m._onUpload(index, bytes, from)\n }\n\n _onDownload (index, bytes, from) {\n for (const m of this._monitors) m._onDownload(index, bytes, from)\n }\n\n _startListening () {\n this.core.on('peer-add', this._boundUpdatePeers)\n this.core.on('peer-remove', this._boundUpdatePeers)\n this.core.on('upload', this._boundOnUpload)\n this.core.on('download', this._boundOnDownload)\n }\n\n _stopListening () {\n this.core.off('peer-add', this._boundUpdatePeers)\n this.core.off('peer-remove', this._boundUpdatePeers)\n this.core.off('upload', this._boundOnUpload)\n this.core.off('download', this._boundOnDownload)\n }\n}\n\nmodule.exports = Hyperblobs\nconst EventEmitter = require('events')\nconst speedometer = require('speedometer')\n\nmodule.exports = class Monitor extends EventEmitter {\n constructor (blobs, id) {\n super()\n\n if (!id) throw new Error('id is required')\n\n this.blobs = blobs\n this.id = id\n this.peers = 0\n this.uploadSpeedometer = null\n this.downloadSpeedometer = null\n\n const stats = {\n startTime: 0,\n percentage: 0,\n peers: 0,\n speed: 0,\n blocks: 0,\n totalBytes: 0, // local + bytes loaded during monitoring\n monitoringBytes: 0, // bytes loaded during monitoring\n targetBytes: 0,\n targetBlocks: 0\n }\n\n this.uploadStats = { ...stats }\n this.downloadStats = { ...stats }\n this.uploadStats.targetBytes = this.downloadStats.targetBytes = this.id.byteLength\n this.uploadStats.targetBlocks = this.downloadStats.targetBlocks = this.id.blockLength\n this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length\n\n this.uploadSpeedometer = speedometer()\n this.downloadSpeedometer = speedometer()\n\n // Handlers\n }\n\n // just an alias\n destroy () {\n return this.close()\n }\n\n close () {\n this.blobs._removeMonitor(this)\n }\n\n _onUpload (index, bytes, from) {\n this._updateStats(this.uploadSpeedometer, this.uploadStats, index, bytes, from)\n }\n\n _onDownload (index, bytes, from) {\n this._updateStats(this.downloadSpeedometer, this.downloadStats, index, bytes, from)\n }\n\n _updatePeers () {\n this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length\n this.emit('update')\n }\n\n _updateStats (speed, stats, index, bytes) {\n if (this.closing) return\n if (!isWithinRange(index, this.id)) return\n\n if (!stats.startTime) stats.startTime = Date.now()\n\n stats.speed = speed(bytes)\n stats.blocks++\n stats.totalBytes += bytes\n stats.monitoringBytes += bytes\n stats.percentage = toFixed(stats.blocks / stats.targetBlocks * 100)\n\n this.emit('update')\n }\n\n downloadSpeed () {\n return this.downloadSpeedometer ? this.downloadSpeedometer() : 0\n }\n\n uploadSpeed () {\n return this.uploadSpeedometer ? this.uploadSpeedometer() : 0\n }\n}\n\nfunction isWithinRange (index, { blockOffset, blockLength }) {\n return index >= blockOffset && index < blockOffset + blockLength\n}\n\nfunction toFixed (n) {\n return Math.round(n * 100) / 100\n}\n// should move to hypercore itself\n\nconst MAX_READAHEAD_TARGET = 0.05 // aim to buffer 5% always\n\nmodule.exports = class Prefetcher {\n constructor (core, { max = 64, start = 0, end = core.length, linear = true } = {}) {\n this.core = core\n this.max = max\n this.range = null\n this.startBound = start\n this.endBound = end\n this.maxReadAhead = Math.max(max * 2, Math.floor((end - start) * MAX_READAHEAD_TARGET))\n\n this.start = start\n this.end = start\n this.linear = linear\n this.missing = 0\n\n this._ondownloadBound = this._ondownload.bind(this)\n this.core.on('download', this._ondownloadBound)\n }\n\n _ondownload (index) {\n if (this.range && index < this.end && this.start <= index) {\n this.missing--\n this._update()\n }\n }\n\n destroy () {\n this.core.off('download', this._ondownloadBound)\n if (this.range) this.range.destroy()\n this.range = null\n this.max = 0\n }\n\n update (position) {\n this.start = position\n if (!this.range) this._update()\n }\n\n _update () {\n if (this.missing >= this.max) return\n if (this.range) this.range.destroy()\n\n let end = this.end\n\n while (end < this.endBound && this.missing < this.max) {\n end = this.core.core.bitfield.firstUnset(end) + 1\n if (end >= this.endBound) break\n this.missing++\n }\n\n if (end > this.start + this.maxReadAhead) end = this.start + this.maxReadAhead\n if (end >= this.endBound) end = this.endBound\n\n this.end = end\n\n if (this.start >= this.end) return\n\n this.range = this.core.download({\n start: this.start,\n end: this.end,\n linear: this.linear\n })\n }\n}\nconst { Readable, Writable } = require('streamx')\nconst { BLOCK_NOT_AVAILABLE } = require('hypercore-errors')\nconst Prefetcher = require('./prefetcher')\n\nclass BlobWriteStream extends Writable {\n constructor (core, lock, opts) {\n super(opts)\n this.id = {}\n this.core = core\n this._lock = lock\n this._release = null\n this._batch = []\n }\n\n _open (cb) {\n this.core.ready().then(() => {\n this._lock(release => {\n this._release = release\n this.id.byteOffset = this.core.byteLength\n this.id.blockOffset = this.core.length\n return cb(null)\n })\n }, err => cb(err))\n }\n\n _final (cb) {\n this._append(err => {\n if (err) return cb(err)\n this.id.blockLength = this.core.length - this.id.blockOffset\n this.id.byteLength = this.core.byteLength - this.id.byteOffset\n return cb(null)\n })\n }\n\n _destroy (cb) {\n if (this._release) this._release()\n cb(null)\n }\n\n _append (cb) {\n if (!this._batch.length) return cb(null)\n return this.core.append(this._batch).then(() => {\n this._batch = []\n return cb(null)\n }, err => {\n this._batch = []\n return cb(err)\n })\n }\n\n _write (data, cb) {\n this._batch.push(data)\n if (this._batch.length >= 16) return this._append(cb)\n return cb(null)\n }\n}\n\nclass BlobReadStream extends Readable {\n constructor (core, id, opts = {}) {\n super(opts)\n this.id = id\n this.core = core.session({ wait: opts.wait, timeout: opts.timeout })\n\n const start = id.blockOffset\n const end = id.blockOffset + id.blockLength\n const noPrefetch = opts.wait === false || opts.prefetch === false || !core.core\n\n this._prefetch = noPrefetch ? null : new Prefetcher(this.core, { max: opts.prefetch, start, end })\n this._lastPrefetch = null\n\n this._pos = opts.start !== undefined ? id.byteOffset + opts.start : id.byteOffset\n\n if (opts.length !== undefined) this._end = this._pos + opts.length\n else if (opts.end !== undefined) this._end = id.byteOffset + opts.end + 1\n else this._end = id.byteOffset + id.byteLength\n\n this._index = 0\n this._relativeOffset = 0\n this._bytesRead = 0\n }\n\n _open (cb) {\n if (this._pos === this.id.byteOffset) {\n this._index = this.id.blockOffset\n this._relativeOffset = 0\n return cb(null)\n }\n\n this.core.seek(this._pos, {\n start: this.id.blockOffset,\n end: this.id.blockOffset + this.id.blockLength\n }).then(result => {\n if (!result) return cb(BLOCK_NOT_AVAILABLE())\n\n this._index = result[0]\n this._relativeOffset = result[1]\n return cb(null)\n }, err => cb(err))\n }\n\n _predestroy () {\n if (this._prefetch) this._prefetch.destroy()\n this.core.close().then(noop, noop)\n }\n\n _destroy (cb) {\n if (this._prefetch) this._prefetch.destroy()\n this.core.close().then(cb, cb)\n }\n\n _read (cb) {\n if (this._pos >= this._end) {\n this.push(null)\n return cb(null)\n }\n\n if (this._prefetch) this._prefetch.update(this._index)\n\n this.core.get(this._index).then(block => {\n if (!block) return cb(BLOCK_NOT_AVAILABLE())\n\n const remainder = this._end - this._pos\n if (this._relativeOffset || (remainder < block.length)) {\n block = block.subarray(this._relativeOffset, this._relativeOffset + remainder)\n }\n\n this._index++\n this._relativeOffset = 0\n this._pos += block.length\n this._bytesRead += block.length\n\n this.push(block)\n return cb(null)\n }, err => cb(err))\n }\n}\n\nmodule.exports = {\n BlobReadStream,\n BlobWriteStream\n}\n\nfunction noop () {}\n{\n \"name\": \"hyperblobs\",\n \"version\": \"2.8.0\",\n \"description\": \"A blob store for Hypercore\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hyperblobs.git\"\n },\n \"keywords\": [\n \"hypercore\",\n \"blob\",\n \"store\"\n ],\n \"author\": \"Andrew Osheroff <andrewosh@gmail.com>\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperblobs/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperblobs#readme\",\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.5.0\",\n \"hypercore-errors\": \"^1.1.1\",\n \"mutexify\": \"^1.4.0\",\n \"speedometer\": \"^1.1.0\",\n \"streamx\": \"^2.13.2\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.0\",\n \"hypercore\": \"^10.18.0\",\n \"random-access-memory\": \"^6.0.0\",\n \"standard\": \"^17.0.0\"\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}\nconst IdEnc = require('hypercore-id-encoding')\n\nmodule.exports = class HypercoreError extends Error {\n constructor (msg, code, fn = HypercoreError, discoveryKey = null) {\n if (discoveryKey) msg = `${msg} (discovery key: ${IdEnc.normalize(discoveryKey)})`\n super(`${code}: ${msg}`)\n\n this.code = code\n this.discoveryKey = discoveryKey\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, fn)\n }\n }\n\n get name () {\n return 'HypercoreError'\n }\n\n static ASSERTION (msg, discoveryKey = null) { // ERR_ASSERTION is picked up by safety-catch also\n return new HypercoreError(msg, 'ERR_ASSERTION', HypercoreError.ASSERT, discoveryKey)\n }\n\n static BAD_ARGUMENT (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'BAD_ARGUMENT', HypercoreError.BAD_ARGUMENT, discoveryKey)\n }\n\n static STORAGE_EMPTY (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'STORAGE_EMPTY', HypercoreError.STORAGE_EMPTY, discoveryKey)\n }\n\n static STORAGE_CONFLICT (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'STORAGE_CONFLICT', HypercoreError.STORAGE_CONFLICT, discoveryKey)\n }\n\n static INVALID_SIGNATURE (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_SIGNATURE', HypercoreError.INVALID_SIGNATURE, discoveryKey)\n }\n\n static INVALID_CAPABILITY (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_CAPABILITY', HypercoreError.INVALID_CAPABILITY, discoveryKey)\n }\n\n static INVALID_CHECKSUM (msg = 'Invalid checksum', discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_CHECKSUM', HypercoreError.INVALID_CHECKSUM, discoveryKey)\n }\n\n static INVALID_OPERATION (msg, discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_OPERATION', HypercoreError.INVALID_OPERATION, discoveryKey)\n }\n\n static INVALID_PROOF (msg = 'Proof not verifiable', discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_PROOF', HypercoreError.INVALID_PROOF, discoveryKey)\n }\n\n static BLOCK_NOT_AVAILABLE (msg = 'Block is not available', discoveryKey = null) {\n return new HypercoreError(msg, 'BLOCK_NOT_AVAILABLE', HypercoreError.BLOCK_NOT_AVAILABLE, discoveryKey)\n }\n\n static SNAPSHOT_NOT_AVAILABLE (msg = 'Snapshot is not available', discoveryKey = null) {\n return new HypercoreError(msg, 'SNAPSHOT_NOT_AVAILABLE', HypercoreError.SNAPSHOT_NOT_AVAILABLE, discoveryKey)\n }\n\n static REQUEST_CANCELLED (msg = 'Request was cancelled', discoveryKey = null) {\n return new HypercoreError(msg, 'REQUEST_CANCELLED', HypercoreError.REQUEST_CANCELLED, discoveryKey)\n }\n\n static REQUEST_TIMEOUT (msg = 'Request timed out', discoveryKey = null) {\n return new HypercoreError(msg, 'REQUEST_TIMEOUT', HypercoreError.REQUEST_TIMEOUT, discoveryKey)\n }\n\n static SESSION_NOT_WRITABLE (msg = 'Session is not writable', discoveryKey = null) {\n return new HypercoreError(msg, 'SESSION_NOT_WRITABLE', HypercoreError.SESSION_NOT_WRITABLE, discoveryKey)\n }\n\n static SESSION_CLOSED (msg = 'Session is closed', discoveryKey = null) {\n return new HypercoreError(msg, 'SESSION_CLOSED', HypercoreError.SESSION_CLOSED, discoveryKey)\n }\n\n static BATCH_UNFLUSHED (msg = 'Batch not yet flushed', discoveryKey = null) {\n return new HypercoreError(msg, 'BATCH_UNFLUSHED', HypercoreError.BATCH_UNFLUSHED, discoveryKey)\n }\n\n static BATCH_ALREADY_EXISTS (msg = 'Batch already exists', discoveryKey = null) {\n return new HypercoreError(msg, 'BATCH_ALREADY_EXISTS', HypercoreError.BATCH_ALREADY_EXISTS, discoveryKey)\n }\n\n static BATCH_ALREADY_FLUSHED (msg = 'Batch has already been flushed', discoveryKey = null) {\n return new HypercoreError(msg, 'BATCH_ALREADY_FLUSHED', HypercoreError.BATCH_ALREADY_FLUSHED, discoveryKey)\n }\n\n static OPLOG_CORRUPT (msg = 'Oplog file appears corrupt or out of date', discoveryKey = null) {\n return new HypercoreError(msg, 'OPLOG_CORRUPT', HypercoreError.OPLOG_CORRUPT, discoveryKey)\n }\n\n static OPLOG_HEADER_OVERFLOW (msg = 'Oplog header exceeds page size', discoveryKey = null) {\n return new HypercoreError(msg, 'OPLOG_HEADER_OVERFLOW', HypercoreError.OPLOG_HEADER_OVERFLOW, discoveryKey)\n }\n\n static INVALID_OPLOG_VERSION (msg = 'Invalid header version', discoveryKey = null) {\n return new HypercoreError(msg, 'INVALID_OPLOG_VERSION', HypercoreError.INVALID_OPLOG_VERSION, discoveryKey)\n }\n\n static WRITE_FAILED (msg = 'Write to storage failed', discoveryKey = null) {\n return new HypercoreError(msg, 'WRITE_FAILED', HypercoreError.WRITE_FAILED, discoveryKey)\n }\n\n static DECODING_ERROR (msg = 'Decoding error', discoveryKey = null) {\n return new HypercoreError(msg, 'DECODING_ERROR', HypercoreError.DECODING_ERROR, discoveryKey)\n }\n\n static SESSION_MOVED (msg = 'Session moved', discoveryKey = null) {\n return new HypercoreError(msg, 'SESSION_MOVED', HypercoreError.SESSION_MOVED, discoveryKey)\n }\n}\n{\n \"name\": \"hypercore-errors\",\n \"version\": \"1.5.0\",\n \"description\": \"Hypercore errors\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/hypercore-errors.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hypercore-errors/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hypercore-errors#readme\",\n \"devDependencies\": {\n \"b4a\": \"^1.6.7\",\n \"brittle\": \"^3.1.3\",\n \"standard\": \"^17.0.0\"\n },\n \"dependencies\": {\n \"hypercore-id-encoding\": \"^1.3.0\"\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}\nconst RocksDB = require('rocksdb-native')\nconst rrp = require('resolve-reject-promise')\nconst ScopeLock = require('scope-lock')\nconst DeviceFile = require('device-file')\nconst path = require('path')\nconst fs = require('fs')\nconst View = require('./lib/view.js')\n\nconst VERSION = 1\nconst COLUMN_FAMILY = 'corestore'\n\nconst { store, core } = require('./lib/keys.js')\n\nconst { CorestoreRX, CorestoreTX, CoreTX, CoreRX } = require('./lib/tx.js')\n\nconst {\n createCoreStream,\n createAliasStream,\n createDiscoveryKeyStream,\n createBlockStream,\n createBitfieldStream,\n createUserDataStream,\n createTreeNodeStream,\n createLocalStream\n} = require('./lib/streams.js')\n\nconst EMPTY = new View()\n\nclass Atom {\n constructor(db) {\n this.db = db\n this.view = new View()\n this.flushedPromise = null\n this.flushing = false\n this.flushes = []\n }\n\n onflush(fn) {\n this.flushes.push(fn)\n }\n\n flushed() {\n if (!this.flushing) return Promise.resolve()\n if (this.flushedPromise !== null) return this.flushedPromise.promise\n this.flushedPromise = rrp()\n return this.flushedPromise.promise\n }\n\n _resolve() {\n const f = this.flushedPromise\n this.flushedPromise = null\n f.resolve()\n }\n\n async flush() {\n if (this.flushing) throw new Error('Atom already flushing')\n this.flushing = true\n\n try {\n await View.flush(this.view.changes, this.db)\n this.view.reset()\n\n const promises = []\n const len = this.flushes.length // in case of reentry\n for (let i = 0; i < len; i++) promises.push(this.flushes[i]())\n\n await Promise.all(promises)\n } finally {\n this.flushing = false\n if (this.flushedPromise !== null) this._resolve()\n }\n }\n}\n\nclass HypercoreStorage {\n constructor(store, db, core, view, atom) {\n this.store = store\n this.db = db\n this.core = core\n this.view = view\n this.atom = atom\n\n this.view.readStart()\n }\n\n get readOnly() {\n return this.store.readOnly\n }\n\n get dependencies() {\n return this.core.dependencies\n }\n\n getDependencyLength() {\n return this.core.dependencies.length\n ? this.core.dependencies[this.core.dependencies.length - 1].length\n : -1\n }\n\n getDependency(length) {\n for (let i = this.core.dependencies.length - 1; i >= 0; i--) {\n const dep = this.core.dependencies[i]\n if (dep.length < length) return dep\n }\n\n return null\n }\n\n setDependencyHead(dep) {\n const deps = this.core.dependencies\n\n for (let i = deps.length - 1; i >= 0; i--) {\n const d = deps[i]\n\n if (d.dataPointer !== dep.dataPointer) continue\n\n // check if nothing changed\n if (d.length === dep.length && i === deps.length - 1) return\n\n this.core = {\n corePointer: this.core.corePointer,\n dataPointer: this.core.dataPointer,\n dependencies: deps.slice(0, i + 1)\n }\n\n this.core.dependencies[i] = {\n dataPointer: dep.dataPointer,\n length: dep.length\n }\n }\n\n this.core.dependencies = [\n {\n dataPointer: dep.dataPointer,\n length: dep.length\n }\n ]\n }\n\n // TODO: this might have to be async if the dependents have changed, but prop ok for now\n updateDependencyLength(length, truncated) {\n const deps = this.core.dependencies\n\n const i = this.findDependencyIndex(length, truncated)\n if (i === -1) throw new Error('Dependency not found')\n\n this.core = {\n corePointer: this.core.corePointer,\n dataPointer: this.core.dataPointer,\n dependencies: deps.slice(0, i + 1)\n }\n\n if (this.core.dependencies[i].length !== length) {\n this.core.dependencies[i] = {\n dataPointer: deps[i].dataPointer,\n length\n }\n }\n }\n\n findDependencyIndex(length, truncated) {\n const deps = this.core.dependencies\n\n if (truncated) {\n for (let i = 0; i < deps.length; i++) {\n if (deps[i].length >= length) return i\n }\n\n return -1\n }\n\n for (let i = deps.length - 1; i >= 0; i--) {\n if (deps[i].length <= length) return i\n }\n\n return -1\n }\n\n get snapshotted() {\n return this.db._snapshot !== null\n }\n\n snapshot() {\n return new HypercoreStorage(\n this.store,\n this.db.snapshot(),\n this.core,\n this.view.snapshot(),\n this.atom\n )\n }\n\n compact() {\n return Promise.all([\n this.db.compactRange(core.core(this.core.corePointer), core.core(this.core.corePointer)),\n this.db.compactRange(core.data(this.core.dataPointer), core.data(this.core.dataPointer))\n ])\n }\n\n atomize(atom) {\n if (this.atom && this.atom !== atom) {\n throw new Error('Cannot atomize and atomized session with a new atom')\n }\n return new HypercoreStorage(this.store, this.db.session(), this.core, atom.view, atom)\n }\n\n createAtom() {\n return this.store.createAtom()\n }\n\n createBlockStream(opts) {\n return createBlockStream(this.core, this.db, this.view, opts)\n }\n\n createTreeNodeStream(opts) {\n return createTreeNodeStream(this.core, this.db, this.view, opts)\n }\n\n createBitfieldStream(opts) {\n return createBitfieldStream(this.core, this.db, this.view, opts)\n }\n\n createUserDataStream(opts) {\n return createUserDataStream(this.core, this.db, this.view, opts)\n }\n\n createLocalStream(opts) {\n return createLocalStream(this.core, this.db, this.view, opts)\n }\n\n async resumeSession(name) {\n const rx = this.read()\n const existingSessionsPromise = rx.getSessions()\n\n rx.tryFlush()\n const existingSessions = await existingSessionsPromise\n\n const sessions = existingSessions || []\n const session = getBatch(sessions, name, false)\n\n if (session === null) return null\n\n const core = {\n corePointer: this.core.corePointer,\n dataPointer: session.dataPointer,\n dependencies: []\n }\n\n const coreRx = new CoreRX(core, this.db, this.view)\n\n const dependencyPromise = coreRx.getDependency()\n coreRx.tryFlush()\n\n const dependency = await dependencyPromise\n if (dependency) core.dependencies = this._addDependency(dependency)\n\n return new HypercoreStorage(\n this.store,\n this.db.session(),\n core,\n this.atom ? this.view : new View(),\n this.atom\n )\n }\n\n async createSession(name, head) {\n const rx = this.read()\n\n const existingSessionsPromise = rx.getSessions()\n const existingHeadPromise = rx.getHead()\n\n rx.tryFlush()\n\n const [existingSessions, existingHead] = await Promise.all([\n existingSessionsPromise,\n existingHeadPromise\n ])\n if (head === null) head = existingHead\n\n if (existingHead !== null && head.length > existingHead.length) {\n throw new Error('Invalid head passed, ahead of core')\n }\n\n const sessions = existingSessions || []\n const session = getBatch(sessions, name, true)\n const fresh = session.dataPointer === -1\n\n if (fresh) {\n session.dataPointer = await this.store._allocData()\n }\n\n const tx = this.write()\n\n tx.setSessions(sessions)\n\n const length = head === null ? 0 : head.length\n const core = {\n corePointer: this.core.corePointer,\n dataPointer: session.dataPointer,\n dependencies: this._addDependency({\n dataPointer: this.core.dataPointer,\n length\n })\n }\n\n const coreTx = new CoreTX(core, this.db, tx.view, tx.changes)\n\n if (length > 0) coreTx.setHead(head)\n coreTx.setDependency(core.dependencies[core.dependencies.length - 1])\n\n if (!fresh) {\n // nuke all existing state...\n coreTx.deleteBlockRange(0, -1)\n coreTx.deleteTreeNodeRange(0, -1)\n coreTx.deleteBitfieldPageRange(0, -1)\n }\n\n await tx.flush()\n\n return new HypercoreStorage(\n this.store,\n this.db.session(),\n core,\n this.atom ? this.view : new View(),\n this.atom\n )\n }\n\n async createAtomicSession(atom, head) {\n const length = head === null ? 0 : head.length\n const core = {\n corePointer: this.core.corePointer,\n dataPointer: this.core.dataPointer,\n dependencies: this._addDependency(null)\n }\n\n const coreTx = new CoreTX(core, this.db, atom.view, [])\n\n if (length > 0) coreTx.setHead(head)\n\n await coreTx.flush()\n\n return this.atomize(atom)\n }\n\n _addDependency(dep) {\n const deps = []\n\n for (let i = 0; i < this.core.dependencies.length; i++) {\n const d = this.core.dependencies[i]\n\n if (dep !== null && d.length > dep.length) {\n if (d.dataPointer !== dep.dataPointer) {\n deps.push({ dataPointer: d.dataPointer, length: dep.length })\n }\n return deps\n }\n\n deps.push(d)\n }\n\n if (\n dep !== null &&\n (deps.length === 0 || deps[deps.length - 1].dataPointer !== dep.dataPointer)\n ) {\n deps.push(dep)\n }\n return deps\n }\n\n read() {\n return new CoreRX(this.core, this.db, this.view)\n }\n\n write() {\n return new CoreTX(this.core, this.db, this.atom ? this.view : null, [])\n }\n\n close() {\n if (this.view !== null) {\n this.view.readStop()\n this.view = null\n }\n\n return this.db.close()\n }\n\n static async export(ptr, db, { batches = false } = {}) {\n const rx = new CoreRX(ptr, db, EMPTY)\n\n const core = {\n head: null,\n auth: null,\n sessions: [],\n data: null\n }\n\n const sessionsPromise = rx.getSessions()\n const headPromise = rx.getHead()\n const authPromise = rx.getAuth()\n\n rx.tryFlush()\n\n const [sessions, head, auth] = await Promise.all([sessionsPromise, headPromise, authPromise])\n\n core.head = head\n core.auth = { ...auth, keyPair: null }\n if (sessions) core.sessions = sessions.map((s) => s.name)\n\n const data = []\n\n data.push(exportData(ptr, db))\n\n if (batches) {\n for (const { dataPointer } of sessions) {\n data.push(exportData({ dataPointer, dependencies: [] }, db))\n }\n }\n\n core.data = await Promise.all(data)\n\n return core\n }\n}\n\nclass CorestoreStorage {\n constructor(db, opts = {}) {\n const storage = typeof db === 'string' ? db : null\n\n this.bootstrap = storage !== null\n this.path = storage !== null ? storage : path.join(db.path, '..')\n this.readOnly = !!opts.readOnly\n this.allowBackup = !!opts.allowBackup\n this.deviceFile = null\n this.wait = !!opts.wait\n\n // tmp sync fix for simplicty since not super deployed yet\n if (this.bootstrap && !this.readOnly) tmpFixStorage(this.path)\n\n this.id = opts.id || null\n this.view = null\n this.enters = 0\n this.lock = new ScopeLock()\n this.flushing = null\n this.version = 0\n this.migrating = null\n\n if ((this.bootstrap && !this.readOnly && !this.allowBackup) || this.wait) {\n const corestoreFile = path.join(this.path, 'CORESTORE')\n\n this.deviceFile = new DeviceFile(corestoreFile, {\n lock: true,\n wait: this.wait,\n data: { id: this.id }\n })\n }\n\n const dbPath = path.join(this.path, 'db')\n\n this.rocks = storage === null ? db : new RocksDB(dbPath, { ...opts, lock: this.deviceFile })\n this.db = createColumnFamily(this.rocks, opts)\n }\n\n get opened() {\n return this.db.opened\n }\n\n get closed() {\n return this.db.closed\n }\n\n async ready() {\n if (this.version === 0) await this._migrateStore()\n return this.db.ready()\n }\n\n compact() {\n return this.db.compactRange()\n }\n\n async audit() {\n for await (const { core } of this.createCoreStream()) {\n const coreRx = new CoreRX(core, this.db, EMPTY)\n const authPromise = coreRx.getAuth()\n\n coreRx.tryFlush()\n\n const auth = await authPromise\n\n if (!auth.manifest || auth.manifest.version > 0) continue\n if (auth.manifest.linked === null) continue\n\n auth.manifest.linked = null\n const coreTx = new CoreTX(core, this.db, null, [])\n coreTx.setAuth(auth)\n await coreTx.flush()\n }\n }\n\n async deleteCore(ptr) {\n const rx = new CoreRX(ptr, this.db, EMPTY)\n\n const authPromise = rx.getAuth()\n const sessionsPromise = rx.getSessions()\n\n rx.tryFlush()\n\n const auth = await authPromise\n const sessions = await sessionsPromise\n\n // no core stored here\n if (!auth) return\n\n const tx = this.db.write({ autoDestroy: true })\n\n tx.tryDelete(store.core(auth.discoveryKey))\n\n // clear core\n const start = core.core(ptr.corePointer)\n const end = core.core(ptr.corePointer + 1)\n tx.tryDeleteRange(start, end)\n\n if (sessions) {\n for (const { dataPointer } of sessions) {\n const start = core.data(dataPointer)\n const end = core.data(dataPointer + 1)\n tx.tryDeleteRange(start, end)\n }\n }\n\n return tx.flush()\n }\n\n static isCoreStorage(db) {\n return isCorestoreStorage(db)\n }\n\n static from(db) {\n if (isCorestoreStorage(db)) return db\n return new this(db)\n }\n\n async _flush() {\n while (this.enters > 0) {\n await this.lock.lock()\n await this.lock.unlock()\n }\n }\n\n // runs pre any other mutation and read\n async _migrateStore() {\n const view = await this._enter()\n\n try {\n if (this.version === VERSION) return\n\n await this._preopen\n await this.db.ready()\n\n const rx = new CorestoreRX(this.db, view)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n const head = await headPromise\n\n const version = head === null ? 0 : head.version\n if (version === VERSION) {\n this.version = VERSION\n return\n }\n\n const target = { version: VERSION, dryRun: false }\n\n switch (version) {\n case 0: {\n await require('./migrations/0').store(this, target)\n break\n }\n default: {\n throw new Error(\n 'Unsupported version: ' + version + ' - you should probably upgrade your dependencies'\n )\n }\n }\n\n this.version = VERSION\n } finally {\n await this._exit()\n }\n }\n\n // runs pre the core is returned to the user\n async _migrateCore(core, discoveryKey, version, locked) {\n const view = locked ? this.view : await this._enter()\n try {\n if (version === VERSION) return\n\n const target = { version: VERSION, dryRun: false }\n\n switch (version) {\n case 0: {\n await require('./migrations/0').core(core, target)\n break\n }\n default: {\n throw new Error(\n 'Unsupported version: ' + version + ' - you should probably upgrade your dependencies'\n )\n }\n }\n\n if (locked === false) return\n\n // if its locked, then move the core state into the memview\n // in case the core is reopened from the memview, pre flush\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const tx = new CorestoreTX(view)\n\n const corePromise = rx.getCore(discoveryKey)\n rx.tryFlush()\n\n tx.putCore(discoveryKey, await corePromise)\n tx.apply()\n } finally {\n if (!locked) await this._exit()\n }\n }\n\n async _enter() {\n this.enters++\n await this.lock.lock()\n if (this.view === null) this.view = new View()\n return this.view\n }\n\n async _exit() {\n this.enters--\n\n if (this.flushing === null) this.flushing = rrp()\n const flushed = this.flushing.promise\n\n if (this.enters === 0 || this.view.size() > 128) {\n try {\n await View.flush(this.view.changes, this.db)\n this.flushing.resolve()\n } catch (err) {\n this.flushing.reject(err)\n } finally {\n this.flushing = null\n this.view = null\n }\n }\n\n this.lock.unlock()\n return flushed\n }\n\n // when used with core catches this isnt transactional for simplicity, HOWEVER, its just a number\n // so worth the tradeoff\n async _allocData() {\n let dataPointer = 0\n\n const view = await this._enter()\n const tx = new CorestoreTX(view)\n\n try {\n const head = await this._getHead(view)\n\n dataPointer = head.allocated.datas++\n\n tx.setHead(head)\n tx.apply()\n } finally {\n await this._exit()\n }\n\n return dataPointer\n }\n\n // exposes here so migrations can easily access the head in an init state\n async _getHead(view) {\n const rx = new CorestoreRX(this.db, view)\n const headPromise = rx.getHead()\n rx.tryFlush()\n\n const head = await headPromise\n return head === null ? initStoreHead() : head\n }\n\n createAtom() {\n return new Atom(this.db)\n }\n\n async flush() {\n await this.rocks.flush()\n }\n\n async close() {\n if (this.db.closed) return\n await this._flush()\n await this.db.close()\n await this.rocks.close()\n if (this.deviceFile) await this.deviceFile.close()\n }\n\n async clear() {\n if (this.version === 0) await this._migrateStore()\n\n const view = await this._enter()\n const tx = new CorestoreTX(view)\n\n tx.clear()\n tx.apply()\n\n await this._exit()\n }\n\n createCoreStream() {\n // TODO: be nice to run the mgiration here also, but too much plumbing atm\n return createCoreStream(this.db, EMPTY)\n }\n\n createAliasStream(namespace) {\n // TODO: be nice to run the mgiration here also, but too much plumbing atm\n return createAliasStream(this.db, EMPTY, namespace)\n }\n\n createDiscoveryKeyStream(namespace) {\n return createDiscoveryKeyStream(this.db, EMPTY, namespace)\n }\n\n async getAlias(alias) {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const discoveryKeyPromise = rx.getCoreByAlias(alias)\n rx.tryFlush()\n return discoveryKeyPromise\n }\n\n async getSeed() {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n\n const head = await headPromise\n return head === null ? null : head.seed\n }\n\n async setSeed(seed, { overwrite = true } = {}) {\n if (this.version === 0) await this._migrateStore()\n\n const view = await this._enter()\n const tx = new CorestoreTX(view)\n\n try {\n const rx = new CorestoreRX(this.db, view)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n\n const head = (await headPromise) || initStoreHead()\n\n if (head.seed === null || overwrite) head.seed = seed\n tx.setHead(head)\n tx.apply()\n\n return head.seed\n } finally {\n await this._exit()\n }\n }\n\n async getDefaultDiscoveryKey() {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n\n const head = await headPromise\n return head === null ? null : head.defaultDiscoveryKey\n }\n\n async setDefaultDiscoveryKey(discoveryKey, { overwrite = true } = {}) {\n if (this.version === 0) await this._migrateStore()\n\n const view = await this._enter()\n const tx = new CorestoreTX(view)\n\n try {\n const rx = new CorestoreRX(this.db, view)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n\n const head = (await headPromise) || initStoreHead()\n\n if (head.defaultDiscoveryKey === null || overwrite) head.defaultDiscoveryKey = discoveryKey\n tx.setHead(head)\n tx.apply()\n\n return head.defaultDiscoveryKey\n } finally {\n await this._exit()\n }\n }\n\n async hasCore(discoveryKey, { ifMigrated = false } = {}) {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const promise = rx.getCore(discoveryKey)\n\n rx.tryFlush()\n\n const core = await promise\n\n if (core === null) return false\n if (core.version !== VERSION && ifMigrated) return false\n\n return true\n }\n\n async getAuth(discoveryKey) {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const corePromise = rx.getCore(discoveryKey)\n\n rx.tryFlush()\n\n const core = await corePromise\n if (core === null) return null\n\n const read = this.db.read({ autoDestroy: true })\n const authPromise = CoreRX.getAuth(read, core)\n\n read.tryFlush()\n\n return authPromise\n }\n\n async getInfo(discoveryKey, opts) {\n return (await this.getInfos([discoveryKey], opts))[0]\n }\n\n async getInfos(discoveryKeys, { auth = true, head = true, hints = true } = {}) {\n if (this.version === 0) await this._migrateStore()\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const corePromises = new Array(discoveryKeys.length)\n\n for (let i = 0; i < discoveryKeys.length; i++) {\n corePromises[i] = rx.getCore(discoveryKeys[i])\n }\n\n rx.tryFlush()\n\n const cores = await Promise.all(corePromises)\n const read = this.db.read({ autoDestroy: true })\n\n const resultPromises = new Array(cores.length)\n\n for (let i = 0; i < cores.length; i++) {\n resultPromises[i] = cores[i]\n ? getInfoFromBatch(read, cores[i], discoveryKeys[i], auth, head, hints)\n : null\n }\n\n read.tryFlush()\n\n return Promise.all(resultPromises)\n }\n\n async suspend() {\n await this.db.suspend()\n if (this.deviceFile) await this.deviceFile.suspend()\n }\n\n async resume() {\n if (this.deviceFile) await this.deviceFile.resume()\n await this.db.resume()\n }\n\n async resumeCore(discoveryKey) {\n if (this.version === 0) await this._migrateStore()\n\n if (!discoveryKey) {\n discoveryKey = await this.getDefaultDiscoveryKey()\n if (!discoveryKey) return null\n }\n\n const rx = new CorestoreRX(this.db, EMPTY)\n const corePromise = rx.getCore(discoveryKey)\n\n rx.tryFlush()\n const core = await corePromise\n\n if (core === null) return null\n return this._resumeFromPointers(EMPTY, discoveryKey, false, core)\n }\n\n async export(discoveryKey, opts) {\n const rx = new CorestoreRX(this.db, EMPTY)\n const corePromise = rx.getCore(discoveryKey)\n\n rx.tryFlush()\n const core = await corePromise\n if (core === null) return null\n\n let { dataPointer, corePointer } = core\n\n const ptr = { corePointer, dataPointer, dependencies: [] }\n\n while (true) {\n const rx = new CoreRX({ dataPointer, corePointer: 0, dependencies: [] }, this.db, EMPTY)\n const dependencyPromise = rx.getDependency()\n rx.tryFlush()\n const dependency = await dependencyPromise\n if (!dependency) break\n ptr.dependencies.push(dependency)\n dataPointer = dependency.dataPointer\n }\n\n const session = this.db.session()\n try {\n return await HypercoreStorage.export(ptr, session, opts)\n } finally {\n await session.close()\n }\n }\n\n async _resumeFromPointers(view, discoveryKey, create, { version, corePointer, dataPointer }) {\n const core = { corePointer, dataPointer, dependencies: [] }\n\n while (true) {\n const rx = new CoreRX({ dataPointer, corePointer: 0, dependencies: [] }, this.db, view)\n const dependencyPromise = rx.getDependency()\n rx.tryFlush()\n const dependency = await dependencyPromise\n if (!dependency) break\n core.dependencies.push(dependency)\n dataPointer = dependency.dataPointer\n }\n\n const result = new HypercoreStorage(this, this.db.session(), core, EMPTY, null)\n\n if (version < VERSION) await this._migrateCore(result, discoveryKey, version, create)\n return result\n }\n\n // not allowed to throw validation errors as its a shared tx!\n async _create(\n view,\n { key, manifest, keyPair, encryptionKey, discoveryKey, alias, userData, core: ptrs }\n ) {\n const rx = new CorestoreRX(this.db, view)\n const tx = new CorestoreTX(view)\n\n const corePromise = rx.getCore(discoveryKey)\n const headPromise = rx.getHead()\n\n rx.tryFlush()\n\n let [core, head] = await Promise.all([corePromise, headPromise])\n if (core) return this._resumeFromPointers(view, discoveryKey, true, core)\n\n if (head === null) head = initStoreHead()\n if (head.defaultDiscoveryKey === null) head.defaultDiscoveryKey = discoveryKey\n\n const corePointer = ptrs ? ptrs.corePointer : head.allocated.cores++\n const dataPointer = ptrs ? ptrs.dataPointer : head.allocated.datas++\n\n core = { version: VERSION, corePointer, dataPointer, alias }\n\n tx.setHead(head)\n tx.putCore(discoveryKey, core)\n if (alias) tx.putCoreByAlias(alias, discoveryKey)\n\n const ptr = { corePointer, dataPointer, dependencies: [] }\n const ctx = new CoreTX(ptr, this.db, view, tx.changes)\n\n ctx.setAuth({\n key,\n discoveryKey,\n manifest,\n keyPair,\n encryptionKey\n })\n\n if (userData) {\n for (const { key, value } of userData) {\n ctx.putUserData(key, value)\n }\n }\n\n tx.apply()\n\n return new HypercoreStorage(this, this.db.session(), ptr, EMPTY, null)\n }\n\n async createCore(data) {\n if (this.version === 0) await this._migrateStore()\n\n const view = await this._enter()\n\n try {\n return await this._create(view, data)\n } finally {\n await this._exit()\n }\n }\n}\n\nmodule.exports = CorestoreStorage\n\nfunction initStoreHead() {\n return {\n version: 0, // cause we wanna run the migration\n allocated: {\n datas: 0,\n cores: 0\n },\n seed: null,\n defaultDiscoveryKey: null\n }\n}\n\nfunction getBatch(sessions, name, alloc) {\n for (let i = 0; i < sessions.length; i++) {\n if (sessions[i].name === name) return sessions[i]\n }\n\n if (!alloc) return null\n\n const result = { name, dataPointer: -1 }\n sessions.push(result)\n return result\n}\n\nfunction isCorestoreStorage(s) {\n return typeof s === 'object' && !!s && typeof s.setDefaultDiscoveryKey === 'function'\n}\n\nfunction createColumnFamily(db, opts = {}) {\n const {\n tableCacheIndexAndFilterBlocks = true,\n blockCache = true,\n optimizeFiltersForMemory = false\n } = opts\n\n const col = new RocksDB.ColumnFamily(COLUMN_FAMILY, {\n enableBlobFiles: true,\n minBlobSize: 4096,\n blobFileSize: 256 * 1024 * 1024,\n enableBlobGarbageCollection: true,\n tableBlockSize: 8192,\n tableCacheIndexAndFilterBlocks,\n tableFormatVersion: 6,\n optimizeFiltersForMemory,\n blockCache\n })\n\n return db.columnFamily(col)\n}\n\n// TODO: remove in like 3-6 mo\nfunction tmpFixStorage(p) {\n // if CORESTORE file is written, new format\n if (fs.existsSync(path.join(p, 'CORESTORE'))) return\n\n let files = []\n\n try {\n files = fs.readdirSync(p)\n } catch {}\n\n const notRocks = new Set([\n 'CORESTORE',\n 'primary-key',\n 'cores',\n 'app-preferences',\n 'cache',\n 'preferences.json',\n 'db',\n 'clone',\n 'core',\n 'notifications'\n ])\n\n for (const f of files) {\n if (notRocks.has(f)) continue\n\n try {\n fs.mkdirSync(path.join(p, 'db'))\n } catch {}\n\n fs.renameSync(path.join(p, f), path.join(p, 'db', f))\n }\n}\n\nasync function exportData(ptr, db, opts) {\n // just need dataPointer\n const reads = [\n toArray(createBlockStream(ptr, db, EMPTY, opts)),\n toArray(createTreeNodeStream(ptr, db, EMPTY, opts)),\n toArray(createBitfieldStream(ptr, db, EMPTY, opts))\n ]\n\n const [blocks, tree, bitfield] = await Promise.all(reads)\n\n return {\n blocks,\n tree,\n bitfield\n }\n}\n\nasync function toArray(stream) {\n const all = []\n for await (const e of stream) all.push(e)\n return all\n}\n\nfunction noop() {}\n\nasync function getInfoFromBatch(db, c, discoveryKey, getAuth, getHead, getHints) {\n const authPromise = getAuth ? CoreRX.getAuth(db, c) : null\n const headPromise = getHead ? CoreRX.getHead(db, c) : null\n const hintsPromise = getHints ? CoreRX.getHints(db, c) : null\n\n // ensure no uncaughts\n if (authPromise) authPromise.catch(noop)\n if (headPromise) headPromise.catch(noop)\n if (hintsPromise) hintsPromise.catch(noop)\n\n return {\n discoveryKey,\n core: c,\n auth: await authPromise,\n head: await headPromise,\n hints: await hintsPromise\n }\n}\nconst { Readable, getStreamError } = require('streamx')\nconst { core } = require('./keys')\n\nmodule.exports = class BlockStream extends Readable {\n constructor(core, db, updates, start, end, reverse) {\n super()\n\n this.core = core\n this.db = db\n this.updates = updates\n this.start = start\n this.end = end\n this.reverse = reverse === true\n\n this._drained = true\n this._consumed = 0\n this._stream = null\n this._oncloseBound = this._onclose.bind(this)\n this._maybeDrainBound = this._maybeDrain.bind(this)\n\n this._update()\n }\n\n _update() {\n if (this._consumed > this.core.dependencies.length) return\n\n const deps = this.core.dependencies\n const index = this._findDependencyIndex(deps)\n\n const curr = index < deps.length ? deps[index] : null\n const prev = index > 0 && index - 1 < deps.length ? deps[index - 1] : null\n\n const start = prev && prev.length > this.start ? prev.length : this.start\n const end = curr && (this.end === -1 || curr.length < this.end) ? curr.length : this.end\n\n const ptr = curr ? curr.dataPointer : this.core.dataPointer\n\n this._makeStream(core.block(ptr, start), core.block(ptr, end))\n }\n\n _findDependencyIndex(deps) {\n if (!this.reverse) return this._consumed++\n\n let i = deps.length - this._consumed++\n while (i > 0) {\n if (deps[i - 1].length <= this.end) return i\n i--\n this._consumed++\n }\n\n return 0\n }\n\n _predestroy() {\n if (this._stream !== null) this._stream.destroy()\n }\n\n _read(cb) {\n this._drained = this._onreadable()\n cb(null)\n }\n\n _maybeDrain() {\n if (this._drained === true) return\n this._drained = this._onreadable()\n }\n\n _onreadable() {\n if (this._stream === null) {\n this.push(null)\n return true\n }\n\n let data = this._stream.read()\n\n if (data === null) return false\n\n do {\n this.push(data)\n data = this._stream.read()\n } while (data !== null)\n\n return true\n }\n\n _onclose() {\n if (this.destroying) return\n\n const err = getStreamError(this._stream)\n\n if (err !== null) {\n this.destroy(err)\n return\n }\n\n // empty the current stream\n if (this._onreadable() === true) this._drained = true\n\n this._stream = null\n\n this._update()\n this._maybeDrain()\n }\n\n _makeStream(start, end) {\n this._stream = this.updates.iterator(this.db, start, end, this.reverse)\n this._stream.on('readable', this._maybeDrainBound)\n this._stream.on('error', noop)\n this._stream.on('close', this._oncloseBound)\n }\n}\n\nfunction noop() {}\nconst { Readable } = require('streamx')\n\n// used for returned a stream that just errors (during read during teardown)\n\nmodule.exports = class CloseErrorStream extends Readable {\n constructor(err) {\n super()\n this.error = err\n }\n\n _open(cb) {\n cb(this.error)\n }\n}\nconst { UINT, STRING } = require('index-encoder')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\n\nconst TL_HEAD = 0\nconst TL_CORE_BY_DKEY = 1\nconst TL_CORE_BY_ALIAS = 2\nconst TL_CORE = 3\nconst TL_DATA = 4\n\nconst TL_END = TL_DATA + 1\n\nconst CORE_AUTH = 0\nconst CORE_SESSIONS = 1\n\nconst DATA_HEAD = 0\nconst DATA_DEPENDENCY = 1\nconst DATA_HINTS = 2\nconst DATA_BLOCK = 3\nconst DATA_TREE = 4\nconst DATA_BITFIELD = 5\nconst DATA_USER_DATA = 6\nconst DATA_LOCAL = 7\n\nconst slab = { buffer: b4a.allocUnsafe(65536), start: 0, end: 0 }\n\nconst store = {}\nconst core = {}\n\nstore.clear = function () {\n const state = alloc()\n let start = state.start\n UINT.encode(state, 0)\n const a = state.buffer.subarray(start, state.start)\n start = state.start\n UINT.encode(state, TL_END)\n const b = state.buffer.subarray(start, state.start)\n return [a, b]\n}\n\nstore.head = function () {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_HEAD)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.core = function (discoveryKey) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE_BY_DKEY)\n c.fixed32.encode(state, discoveryKey)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.coreStart = function () {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE_BY_DKEY)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.coreEnd = function () {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE_BY_DKEY + 1)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.coreByAlias = function ({ namespace, name }) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE_BY_ALIAS)\n c.fixed32.encode(state, namespace)\n STRING.encode(state, name)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.coreByAliasStart = function (namespace) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE_BY_ALIAS)\n if (namespace) c.fixed32.encode(state, namespace)\n return state.buffer.subarray(start, state.start)\n}\n\nstore.coreByAliasEnd = function (namespace) {\n const state = alloc()\n const start = state.start\n\n if (namespace) {\n UINT.encode(state, TL_CORE_BY_ALIAS)\n c.fixed32.encode(state, namespace)\n state.buffer[state.start++] = 0xff\n } else {\n UINT.encode(state, TL_CORE_BY_ALIAS + 1)\n }\n\n return state.buffer.subarray(start, state.start)\n}\n\nstore.alias = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n const namespace = c.fixed32.decode(state)\n const name = STRING.decode(state)\n return { namespace, name }\n}\n\nstore.discoveryKey = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n return c.fixed32.decode(state)\n}\n\ncore.core = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE)\n UINT.encode(state, ptr)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.data = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.auth = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE)\n UINT.encode(state, ptr)\n UINT.encode(state, CORE_AUTH)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.sessions = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_CORE)\n UINT.encode(state, ptr)\n UINT.encode(state, CORE_SESSIONS)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.head = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_HEAD)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.dependency = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_DEPENDENCY)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.hints = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_HINTS)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.block = function (ptr, index) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_BLOCK)\n UINT.encode(state, index)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.tree = function (ptr, index) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_TREE)\n UINT.encode(state, index)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.bitfield = function (ptr, index, type) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_BITFIELD)\n UINT.encode(state, index)\n UINT.encode(state, type)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.userData = function (ptr, key) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_USER_DATA)\n STRING.encode(state, key)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.userDataEnd = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_USER_DATA + 1)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.local = function (ptr, key) {\n if (key.byteLength > 2048) {\n throw new Error('local keys has an upper limit of 2048 bytes atm')\n }\n\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_LOCAL)\n\n state.buffer.set(key, state.start)\n state.start += key.byteLength\n return state.buffer.subarray(start, state.start)\n}\n\ncore.localEnd = function (ptr) {\n const state = alloc()\n const start = state.start\n UINT.encode(state, TL_DATA)\n UINT.encode(state, ptr)\n UINT.encode(state, DATA_LOCAL + 1)\n return state.buffer.subarray(start, state.start)\n}\n\ncore.blockIndex = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n UINT.decode(state) // ptr\n UINT.decode(state) // type\n return UINT.decode(state)\n}\n\ncore.bitfieldIndexAndType = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n UINT.decode(state) // ptr\n UINT.decode(state) // type\n return [UINT.decode(state), UINT.decode(state)]\n}\n\ncore.userDataKey = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n UINT.decode(state) // ptr\n UINT.decode(state) // type\n return STRING.decode(state)\n}\n\ncore.localKey = function (buffer) {\n const state = { buffer, start: 0, end: buffer.byteLength }\n UINT.decode(state) // ns\n UINT.decode(state) // ptr\n UINT.decode(state) // type\n return state.buffer.subarray(state.start, state.end)\n}\n\nmodule.exports = { store, core }\n\nfunction alloc() {\n if (slab.buffer.byteLength - slab.start < 4096) {\n slab.buffer = b4a.allocUnsafe(slab.buffer.byteLength)\n slab.start = 0\n }\n return slab\n}\nconst b4a = require('b4a')\nconst BlockDependencyStream = require('./block-dependency-stream.js')\nconst { core, store } = require('./keys.js')\nconst schema = require('../spec/hyperschema')\n\nconst CORESTORE_CORE = schema.getEncoding('@corestore/core')\nconst CORE_TREE_NODE = schema.getEncoding('@core/tree-node')\nconst EMPTY = b4a.alloc(0)\n\nmodule.exports = {\n createBlockStream,\n createBitfieldStream,\n createUserDataStream,\n createCoreStream,\n createAliasStream,\n createDiscoveryKeyStream,\n createTreeNodeStream,\n createLocalStream\n}\n\nfunction createCoreStream(db, view) {\n const start = store.coreStart()\n const end = store.coreEnd()\n\n const ite = view.iterator(db, start, end, false)\n\n ite._readableState.map = mapCore\n return ite\n}\n\nfunction createDiscoveryKeyStream(db, view, namespace) {\n const start = namespace ? store.coreByAliasStart(namespace) : store.coreStart()\n const end = namespace ? store.coreByAliasEnd(namespace) : store.coreEnd()\n\n const ite = view.iterator(db, start, end, false)\n\n ite._readableState.map = namespace ? mapNamespaceDiscoveryKeys : mapAllDiscoveryKeys\n return ite\n}\n\nfunction createAliasStream(db, view, namespace) {\n const start = store.coreByAliasStart(namespace)\n const end = store.coreByAliasEnd(namespace)\n\n const ite = view.iterator(db, start, end, false)\n\n ite._readableState.map = mapAlias\n return ite\n}\n\nfunction createBlockIterator(ptr, db, view, start, end, reverse) {\n if (ptr.dependencies.length > 0) {\n return new BlockDependencyStream(ptr, db, view, start, end, reverse)\n }\n\n const s = core.block(ptr.dataPointer, start)\n const e = core.block(ptr.dataPointer, end === -1 ? Infinity : end)\n return view.iterator(db, s, e, reverse)\n}\n\nfunction createBlockStream(\n ptr,\n db,\n view,\n { gt = -1, gte = gt + 1, lte = -1, lt = lte === -1 ? -1 : lte + 1, reverse = false } = {}\n) {\n const ite = createBlockIterator(ptr, db, view, gte, lt, reverse)\n\n ite._readableState.map = mapBlock\n return ite\n}\n\nfunction createBitfieldStream(\n ptr,\n db,\n view,\n { gt = -1, gte = gt + 1, lte = -1, lt = lte === -1 ? -1 : lte + 1, reverse = false } = {}\n) {\n const s = core.bitfield(ptr.dataPointer, gte, 0)\n const e = core.bitfield(ptr.dataPointer, lt === -1 ? Infinity : lt, 0)\n const ite = view.iterator(db, s, e, false)\n\n ite._readableState.map = mapBitfield\n return ite\n}\n\n// NOTE: this does not do dependency lookups atm\nfunction createTreeNodeStream(\n ptr,\n db,\n view,\n { gt = -1, gte = gt + 1, lte = -1, lt = lte === -1 ? -1 : lte + 1, reverse = false } = {}\n) {\n const s = core.tree(ptr.dataPointer, gte, 0)\n const e = core.tree(ptr.dataPointer, lt === -1 ? Infinity : lt, 0)\n const ite = view.iterator(db, s, e, false)\n\n ite._readableState.map = mapTreeNode\n return ite\n}\n\nfunction createUserDataStream(\n ptr,\n db,\n view,\n { gt = null, gte = '', lte = null, lt = null, reverse = false } = {}\n) {\n if (gt !== null || lte !== null) {\n throw new Error('gt and lte not yet supported for user data streams')\n }\n\n const s = core.userData(ptr.dataPointer, gte)\n const e = lt === null ? core.userDataEnd(ptr.dataPointer) : core.userData(ptr.dataPointer, lt)\n const ite = view.iterator(db, s, e, false)\n\n ite._readableState.map = mapUserData\n return ite\n}\n\nfunction createLocalStream(\n ptr,\n db,\n view,\n { gt = null, gte = EMPTY, lte = null, lt = null, reverse = false } = {}\n) {\n if (gt !== null || lte !== null) throw new Error('gt and lte not yet supported for local streams')\n\n const s = core.local(ptr.dataPointer, gte)\n const e = lt === null ? core.localEnd(ptr.dataPointer) : core.local(ptr.dataPointer, lt)\n const ite = view.iterator(db, s, e, false)\n\n ite._readableState.map = mapLocal\n return ite\n}\n\nfunction mapBitfield(data) {\n const [index, type] = core.bitfieldIndexAndType(data.key)\n if (type !== 0) return null // ignore for now\n return { index, page: data.value }\n}\n\nfunction mapLocal(data) {\n const key = core.localKey(data.key)\n return { key, value: data.value }\n}\n\nfunction mapUserData(data) {\n const key = core.userDataKey(data.key)\n return { key, value: data.value }\n}\n\nfunction mapCore(data) {\n const discoveryKey = store.discoveryKey(data.key)\n const core = CORESTORE_CORE.decode({\n start: 0,\n end: data.value.byteLength,\n buffer: data.value\n })\n return { discoveryKey, core }\n}\n\nfunction mapAllDiscoveryKeys(data) {\n return store.discoveryKey(data.key)\n}\n\nfunction mapNamespaceDiscoveryKeys(data) {\n return data.value\n}\n\nfunction mapAlias(data) {\n const alias = store.alias(data.key)\n return { alias, discoveryKey: data.value }\n}\n\nfunction mapBlock(data) {\n return { index: core.blockIndex(data.key), value: data.value }\n}\n\nfunction mapTreeNode(data) {\n return CORE_TREE_NODE.decode({\n start: 0,\n end: data.value.byteLength,\n buffer: data.value\n })\n}\nconst schema = require('../spec/hyperschema')\nconst { store, core } = require('./keys.js')\nconst View = require('./view.js')\nconst b4a = require('b4a')\nconst flat = require('flat-tree')\n\nconst CORESTORE_HEAD = schema.getEncoding('@corestore/head')\nconst CORESTORE_CORE = schema.getEncoding('@corestore/core')\n\nconst CORE_AUTH = schema.getEncoding('@core/auth')\nconst CORE_SESSIONS = schema.getEncoding('@core/sessions')\nconst CORE_HEAD = schema.getEncoding('@core/head')\nconst CORE_TREE_NODE = schema.getEncoding('@core/tree-node')\nconst CORE_DEPENDENCY = schema.getEncoding('@core/dependency')\nconst CORE_HINTS = schema.getEncoding('@core/hints')\n\nclass CoreTX {\n constructor(core, db, view, changes) {\n if (db.snapshotted) throw new Error('Cannot open core tx on snapshot')\n this.core = core\n this.db = db\n this.view = view\n this.changes = changes\n }\n\n setAuth(auth) {\n this.changes.push([core.auth(this.core.corePointer), encode(CORE_AUTH, auth), null])\n }\n\n setSessions(sessions) {\n this.changes.push([core.sessions(this.core.corePointer), encode(CORE_SESSIONS, sessions), null])\n }\n\n setHead(head) {\n this.changes.push([core.head(this.core.dataPointer), encode(CORE_HEAD, head), null])\n }\n\n deleteHead() {\n this.changes.push([core.head(this.core.dataPointer), null, null])\n }\n\n setDependency(dep) {\n this.changes.push([core.dependency(this.core.dataPointer), encode(CORE_DEPENDENCY, dep), null])\n }\n\n setHints(hints) {\n this.changes.push([core.hints(this.core.dataPointer), encode(CORE_HINTS, hints), null])\n }\n\n putBlock(index, data) {\n this.changes.push([core.block(this.core.dataPointer, index), data, null])\n }\n\n deleteBlock(index) {\n this.changes.push([core.block(this.core.dataPointer, index), null, null])\n }\n\n deleteBlockRange(start, end) {\n this.changes.push([\n core.block(this.core.dataPointer, start),\n null,\n core.block(this.core.dataPointer, end === -1 ? Infinity : end)\n ])\n }\n\n putBitfieldPage(index, data) {\n this.changes.push([core.bitfield(this.core.dataPointer, index, 0), data, null])\n }\n\n deleteBitfieldPage(index) {\n this.changes.push([core.bitfield(this.core.dataPointer, index, 0), null, null])\n }\n\n deleteBitfieldPageRange(start, end) {\n this.changes.push([\n core.bitfield(this.core.dataPointer, start, 0),\n null,\n core.bitfield(this.core.dataPointer, end === -1 ? Infinity : end, 0)\n ])\n }\n\n putTreeNode(node) {\n this.changes.push([\n core.tree(this.core.dataPointer, node.index),\n encode(CORE_TREE_NODE, node),\n null\n ])\n }\n\n deleteTreeNode(index) {\n this.changes.push([core.tree(this.core.dataPointer, index), null, null])\n }\n\n deleteTreeNodeRange(start, end) {\n this.changes.push([\n core.tree(this.core.dataPointer, start),\n null,\n core.tree(this.core.dataPointer, end === -1 ? Infinity : end)\n ])\n }\n\n putUserData(key, value) {\n const buffer = typeof value === 'string' ? b4a.from(value) : value\n this.changes.push([core.userData(this.core.dataPointer, key), buffer, null])\n }\n\n deleteUserData(key) {\n this.changes.push([core.userData(this.core.dataPointer, key), null, null])\n }\n\n putLocal(key, value) {\n this.changes.push([core.local(this.core.dataPointer, key), value, null])\n }\n\n deleteLocal(key) {\n this.changes.push([core.local(this.core.dataPointer, key), null, null])\n }\n\n deleteLocalRange(start, end) {\n this.changes.push([\n core.local(this.core.dataPointer, start),\n null,\n end === null ? core.localEnd(this.core.dataPointer) : core.local(this.core.dataPointer, end)\n ])\n }\n\n flush() {\n const changes = this.changes\n if (changes === null) return Promise.resolve(!this.view)\n\n this.changes = null\n\n if (this.view) {\n this.view.apply(changes)\n return Promise.resolve(false)\n }\n\n return View.flush(changes, this.db)\n }\n}\n\nclass CoreRX {\n constructor(core, db, view) {\n this.core = core\n this.read = db.read({ autoDestroy: true })\n this.view = view\n\n view.readStart()\n }\n\n static async getAuth(db, c) {\n return await decode(CORE_AUTH, await db.get(core.auth(c.corePointer)))\n }\n\n async getAuth() {\n return await decode(CORE_AUTH, await this.view.get(this.read, core.auth(this.core.corePointer)))\n }\n\n async getSessions() {\n return await decode(\n CORE_SESSIONS,\n await this.view.get(this.read, core.sessions(this.core.corePointer))\n )\n }\n\n static async getHead(db, c) {\n return await decode(CORE_HEAD, await db.get(core.head(c.dataPointer)))\n }\n\n async getHead() {\n return await decode(CORE_HEAD, await this.view.get(this.read, core.head(this.core.dataPointer)))\n }\n\n async getDependency() {\n return await decode(\n CORE_DEPENDENCY,\n await this.view.get(this.read, core.dependency(this.core.dataPointer))\n )\n }\n\n static async getHints(db, c) {\n return await decode(CORE_HINTS, await db.get(core.hints(c.dataPointer)))\n }\n\n async getHints() {\n return await decode(\n CORE_HINTS,\n await this.view.get(this.read, core.hints(this.core.dataPointer))\n )\n }\n\n getBlock(index) {\n const dep = findBlockDependency(this.core.dependencies, index)\n const data = dep === null ? this.core.dataPointer : dep.dataPointer\n return this.view.get(this.read, core.block(data, index))\n }\n\n getBitfieldPage(index) {\n return this.view.get(this.read, core.bitfield(this.core.dataPointer, index, 0))\n }\n\n async getTreeNode(index) {\n const dep = findTreeDependency(this.core.dependencies, index)\n const data = dep === null ? this.core.dataPointer : dep.dataPointer\n return decode(CORE_TREE_NODE, await this.view.get(this.read, core.tree(data, index)))\n }\n\n async hasTreeNode(index) {\n return (await this.getTreeNode(index)) !== null\n }\n\n getUserData(key) {\n return this.view.get(this.read, core.userData(this.core.dataPointer, key))\n }\n\n getLocal(key) {\n return this.view.get(this.read, core.local(this.core.dataPointer, key))\n }\n\n tryFlush() {\n this.read.tryFlush()\n this._free()\n }\n\n destroy() {\n this.read.destroy()\n this._free()\n }\n\n _free() {\n if (this.view === null) return\n this.view.readStop()\n this.view = null\n }\n}\n\nclass CorestoreTX {\n constructor(view) {\n this.view = view\n this.changes = []\n }\n\n setHead(head) {\n this.changes.push([store.head(), encode(CORESTORE_HEAD, head), null])\n }\n\n putCore(discoveryKey, ptr) {\n this.changes.push([store.core(discoveryKey), encode(CORESTORE_CORE, ptr), null])\n }\n\n putCoreByAlias(alias, discoveryKey) {\n this.changes.push([store.coreByAlias(alias), discoveryKey, null])\n }\n\n clear() {\n const [start, end] = store.clear()\n this.changes.push([start, null, end])\n }\n\n apply() {\n if (this.changes === null) return\n this.view.apply(this.changes)\n this.changes = null\n }\n}\n\nclass CorestoreRX {\n constructor(db, view) {\n this.read = db.read({ autoDestroy: true })\n this.view = view\n\n view.readStart()\n }\n\n async getHead() {\n return decode(CORESTORE_HEAD, await this.view.get(this.read, store.head()))\n }\n\n async getCore(discoveryKey) {\n return decode(CORESTORE_CORE, await this.view.get(this.read, store.core(discoveryKey)))\n }\n\n getCoreByAlias(alias) {\n return this.view.get(this.read, store.coreByAlias(alias))\n }\n\n tryFlush() {\n this.read.tryFlush()\n this._free()\n }\n\n destroy() {\n this.read.destroy()\n this._free()\n }\n\n _free() {\n if (this.view === null) return\n this.view.readStop()\n this.view = null\n }\n}\n\nmodule.exports = { CorestoreTX, CorestoreRX, CoreTX, CoreRX }\n\nfunction findBlockDependency(dependencies, index) {\n for (let i = 0; i < dependencies.length; i++) {\n const dep = dependencies[i]\n if (index < dep.length) return dep\n }\n\n return null\n}\n\nfunction findTreeDependency(dependencies, index) {\n for (let i = 0; i < dependencies.length; i++) {\n const dep = dependencies[i]\n if (flat.rightSpan(index) <= (dep.length - 1) * 2) return dep\n }\n\n return null\n}\n\nfunction decode(enc, buffer) {\n if (buffer === null) return null\n return enc.decode({ start: 0, end: buffer.byteLength, buffer })\n}\n\nfunction encode(enc, m) {\n // TODO: use fancy slab for small messages\n const state = { start: 0, end: 0, buffer: null }\n enc.preencode(state, m)\n state.buffer = b4a.allocUnsafe(state.end)\n enc.encode(state, m)\n return state.buffer\n}\nconst { Readable, getStreamError } = require('streamx')\nconst CloseErrorStream = require('./close-error-stream.js')\nconst b4a = require('b4a')\n\nclass OverlayStream extends Readable {\n constructor(stream, start, end, reverse, changes, cleared) {\n super()\n\n this.start = start\n this.end = end\n this.reverse = reverse\n this.changes = changes\n this.cleared = cleared\n this.change = 0\n this.range = 0\n\n this._stream = stream\n this._drained = false\n\n this._stream.on('readable', this._drainMaybe.bind(this))\n this._stream.on('error', noop)\n this._stream.on('close', this._onclose.bind(this))\n }\n\n _drainMaybe() {\n if (this._drained === true) return\n this._drained = this._onreadable()\n }\n\n _onclose() {\n if (this.destroying) return\n\n const err = getStreamError(this._stream)\n\n if (err !== null) {\n this.destroy(err)\n return\n }\n\n while (this.change < this.changes.length) {\n const c = this.changes[this.change++]\n const key = c[0]\n const value = c[1]\n\n if (value !== null && this._inRange(key)) this.push({ key, value })\n }\n\n this.push(null)\n this._stream = null\n }\n\n _onreadable() {\n let data = this._stream.read()\n if (data === null) return false\n\n let drained = false\n\n do {\n if (this._push(data) === true) drained = true\n data = this._stream.read()\n } while (data !== null)\n\n return drained\n }\n\n _read(cb) {\n this._drained = this._onreadable()\n cb(null)\n }\n\n _predestroy() {\n this.stream.destroy()\n }\n\n _push(entry) {\n const key = entry.key\n\n while (this.range < this.cleared.length) {\n const c = this.cleared[this.range]\n\n // we moved past the range\n if (this.reverse ? b4a.compare(key, c[0]) < 0 : b4a.compare(c[2], key) <= 0) {\n this.range++\n continue\n }\n\n // we didnt move past and are in, drop\n if (b4a.compare(c[0], key) <= 0 && b4a.compare(key, c[2]) < 0) {\n return false\n }\n\n break\n }\n\n let updated = false\n\n while (this.change < this.changes.length) {\n const c = this.changes[this.change]\n const key = c[0]\n const value = typeof c[1] === 'string' ? b4a.from(c[1]) : c[1]\n const cmp = b4a.compare(key, entry.key)\n\n // same value, if not deleted, return new one\n if (cmp === 0) {\n this.change++\n if (value === null || this._inRange(key) === false) return updated\n this.push({ key, value })\n return true\n }\n\n // we moved past the change, push it\n if (this.reverse ? cmp > 0 : cmp < 0) {\n this.change++\n if (value === null || this._inRange(key) === false) continue\n this.push({ key, value })\n updated = true\n continue\n }\n\n this.push(entry)\n return true\n }\n\n this.push(entry)\n return true\n }\n\n _inRange(key) {\n return b4a.compare(this.start, key) <= 0 && b4a.compare(key, this.end) < 0\n }\n}\n\nclass Overlay {\n constructor() {\n this.indexed = 0\n this.changes = null\n this.cleared = null\n this.reverse = false\n }\n\n update(view, reverse) {\n if (view.indexed === this.indexed) return\n\n const changes = view.map === null ? [] : [...view.map.values()]\n const cleared = view.cleared === null ? [] : view.cleared.slice(0)\n\n const cmp = reverse ? cmpChangeReverse : cmpChange\n\n changes.sort(cmp)\n cleared.sort(cmp)\n\n this.indexed = view.indexed\n this.changes = changes\n this.cleared = cleared\n this.reverse = reverse\n }\n\n createStream(stream, start, end, reverse) {\n return new OverlayStream(\n stream,\n start,\n end,\n reverse,\n this.reverse === reverse ? this.changes : reverseArray(this.changes),\n this.reverse === reverse ? this.cleared : reverseArray(this.cleared)\n )\n }\n}\n\nclass View {\n constructor() {\n this.map = null\n this.indexed = 0\n this.changes = null\n this.cleared = null\n this.overlay = null\n this.snap = null\n this.readers = 0\n }\n\n snapshot() {\n if (this._attached()) return this.snap.snapshot()\n\n const snap = new View()\n\n snap.map = this.map\n snap.indexed = this.indexed\n snap.changes = this.changes\n snap.cleared = this.cleared\n\n if (this._frozen()) return snap\n\n this.readers++\n snap.snap = this\n\n return snap\n }\n\n readStart() {\n if (this.snap !== null) this.readers++\n }\n\n readStop() {\n if (this.snap !== null && --this.readers === 0) this.snap.readers--\n }\n\n size() {\n return this.changes === null ? 0 : this.changes.length\n }\n\n updated() {\n return this.changes === null\n }\n\n get(read, key) {\n return this.changes === null ? read.get(key) : this._indexAndGet(read, key)\n }\n\n reset() {\n this.indexed = 0\n this.snap = this.map = this.changes = this.cleared = this.overlay = null\n }\n\n iterator(db, start, end, reverse) {\n if (dbClosing(db)) return new CloseErrorStream(new Error('RocksDB session is closed'))\n\n const stream = db.iterator({ gte: start, lt: end, reverse })\n if (this.changes === null) return stream\n\n this._index()\n\n if (this.overlay === null) this.overlay = new Overlay()\n this.overlay.update(this, reverse)\n return this.overlay.createStream(stream, start, end, reverse)\n }\n\n _indexAndGet(read, key) {\n this._index()\n const change = this.map.get(b4a.toString(key, 'hex'))\n\n if (change === undefined) {\n return this.cleared === null ? read.get(key) : this._readAndMaybeDrop(read, key)\n }\n\n return Promise.resolve(change[1])\n }\n\n async _readAndMaybeDrop(read, key) {\n const cleared = this.cleared // in case its cleared\n const value = await read.get(key)\n if (value === null) return null\n\n for (let i = 0; i < cleared.length; i++) {\n const c = cleared[i]\n // check if in range\n if (b4a.compare(c[0], key) <= 0 && b4a.compare(key, c[2]) < 0) return null\n }\n\n return value\n }\n\n _attached() {\n return this.snap !== null && this.changes === this.snap.changes\n }\n\n _frozen() {\n return this.changes === null || (this.snap !== null && this.changes !== this.snap.changes)\n }\n\n _index() {\n // if we are a snap and we are still attached (ie no mutations), simply copy the refs\n if (this._attached()) {\n this.snap._index()\n this.map = this.snap.map\n this.cleared = this.snap.cleared\n this.indexed = this.snap.indexed\n return\n }\n\n if (this.map === null) this.map = new Map()\n if (this.changes.length === this.indexed) return\n\n while (this.indexed < this.changes.length) {\n const c = this.changes[this.indexed++]\n\n if (c[2] === null) this.map.set(b4a.toString(c[0], 'hex'), c)\n else this._indexRange(c)\n }\n }\n\n _indexRange(range) {\n const s = b4a.toString(range[0], 'hex')\n const e = b4a.toString(range[2], 'hex')\n\n for (const [key, c] of this.map) {\n if (s <= key && key < e) this.map.set(key, [c[0], null, null])\n }\n\n if (this.cleared === null) this.cleared = []\n this.cleared.push(range)\n }\n\n apply(changes) {\n if (this.snap !== null) throw new Error('Illegal to push changes to a snapshot')\n\n if (this.readers !== 0 && this.changes !== null) {\n this.changes = this.changes.slice(0)\n this.cleared = this.cleared === null ? null : this.cleared.slice(0)\n this.map = this.map === null ? null : new Map([...this.map])\n }\n\n if (this.changes === null) {\n this.changes = changes\n return\n }\n\n for (let i = 0; i < changes.length; i++) {\n this.changes.push(changes[i])\n }\n }\n\n static async flush(changes, db) {\n if (changes === null) return true\n\n const w = db.write({ autoDestroy: true })\n\n for (const [start, value, end] of changes) {\n if (end !== null) w.tryDeleteRange(start, end)\n else if (value !== null) w.tryPut(start, value)\n else w.tryDelete(start)\n }\n\n await w.flush()\n\n return true\n }\n}\n\nmodule.exports = View\n\nfunction cmpChange(a, b) {\n const c = b4a.compare(a[0], b[0])\n return c === 0 ? b4a.compare(a[2], b[2]) : c\n}\n\nfunction cmpChangeReverse(a, b) {\n return cmpChange(b, a)\n}\n\nfunction noop() {}\n\nfunction reverseArray(list) {\n const r = new Array(list.length)\n for (let i = 0; i < list.length; i++) r[r.length - 1 - i] = list[i]\n return r\n}\n\n// TODO: expose from rocks instead\nfunction dbClosing(db) {\n return db._state.closing || db._index === -1\n}\nconst fs = require('fs')\nconst path = require('path')\nconst { Readable } = require('streamx')\nconst b4a = require('b4a')\nconst flat = require('flat-tree')\nconst crypto = require('hypercore-crypto')\nconst c = require('compact-encoding')\nconst m = require('./messages.js')\nconst View = require('../../lib/view.js')\nconst { CorestoreTX, CoreTX, CorestoreRX } = require('../../lib/tx.js')\n\nconst EMPTY_NODE = b4a.alloc(40)\nconst EMPTY_PAGE = b4a.alloc(4096)\n\nlet TREE_01_SKIP = null\nlet TREE_04_SKIP = null\nlet TREE_16_SKIP = null\n\nclass CoreListStream extends Readable {\n constructor (storage) {\n super()\n\n this.storage = storage\n this.stack = []\n }\n\n async _open (cb) {\n for (const a of await readdir(path.join(this.storage, 'cores'))) {\n for (const b of await readdir(path.join(this.storage, 'cores', a))) {\n for (const dkey of await readdir(path.join(this.storage, 'cores', a, b))) {\n this.stack.push(path.join(this.storage, 'cores', a, b, dkey))\n }\n }\n }\n\n cb(null)\n }\n\n async _read (cb) {\n while (true) {\n const next = this.stack.pop()\n if (!next) {\n this.push(null)\n break\n }\n\n const oplog = path.join(next, 'oplog')\n const result = await readOplog(oplog)\n if (!result) continue\n\n this.push(result)\n break\n }\n\n cb(null)\n }\n}\n\nfunction decodeOplogHeader (state) {\n c.uint32.decode(state) // cksum, ignore for now\n\n const l = c.uint32.decode(state)\n const length = l >> 2\n const headerBit = l & 1\n const partialBit = l & 2\n\n if (state.end - state.start < length) return null\n\n const end = state.start + length\n const result = { header: headerBit, partial: partialBit !== 0, byteLength: length + 8, message: null }\n\n try {\n result.message = m.oplog.header.decode({ start: state.start, end, buffer: state.buffer })\n } catch {\n return null\n }\n\n state.start = end\n return result\n}\n\nfunction decodeOplogEntry (state) {\n if (state.end - state.start < 8) return null\n\n c.uint32.decode(state) // cksum, ignore for now\n\n const l = c.uint32.decode(state)\n const length = l >>> 2\n const headerBit = l & 1\n const partialBit = l & 2\n\n if (state.end - state.start < length) return null\n\n const end = state.start + length\n\n const result = { header: headerBit, partial: partialBit !== 0, byteLength: length + 8, message: null }\n\n try {\n result.message = m.oplog.entry.decode({ start: state.start, end, buffer: state.buffer })\n } catch {\n return null\n }\n\n state.start = end\n\n return result\n}\n\nmodule.exports = { store, core }\n\nasync function store (storage, { version, dryRun = true, gc = true }) {\n const stream = new CoreListStream(storage.path)\n const view = new View()\n\n const tx = new CorestoreTX(view)\n const head = await storage._getHead(view)\n const primaryKeyFile = path.join(storage.path, 'primary-key')\n\n const primaryKey = await readFile(primaryKeyFile)\n\n if (!head.seed) head.seed = primaryKey\n\n for await (const data of stream) {\n const key = data.header.key\n const discoveryKey = crypto.discoveryKey(data.header.key)\n const files = getFiles(data.path)\n\n if (head.defaultDiscoveryKey === null) head.defaultDiscoveryKey = discoveryKey\n\n const core = {\n version: 0, // need later migration\n corePointer: head.allocated.cores++,\n dataPointer: head.allocated.datas++,\n alias: null\n }\n\n const ptr = { version: 0, corePointer: core.corePointer, dataPointer: core.dataPointer, dependencies: [] }\n const ctx = new CoreTX(ptr, storage.db, view, [])\n const userData = new Map()\n const treeNodes = new Map()\n\n const auth = {\n key,\n discoveryKey,\n manifest: data.header.manifest,\n keyPair: data.header.keyPair,\n encryptionKey: null\n }\n\n const tree = {\n length: 0,\n fork: 0,\n rootHash: null,\n signature: null\n }\n\n if (data.header.tree && data.header.tree.length) {\n tree.length = data.header.tree.length\n tree.fork = data.header.tree.fork\n tree.rootHash = data.header.tree.rootHash\n tree.signature = data.header.tree.signature\n }\n\n for (const { key, value } of data.header.userData) {\n userData.set(key, value)\n }\n\n for (const e of data.entries) {\n if (e.userData) userData.set(e.userData.key, e.userData.value)\n\n if (e.treeNodes) {\n for (const node of e.treeNodes) {\n treeNodes.set(node.index, node)\n ctx.putTreeNode(node)\n }\n }\n\n if (e.treeUpgrade) {\n if (e.treeUpgrade.ancestors !== tree.length) {\n throw new Error('Unflushed truncations not migrate-able atm')\n }\n\n tree.length = e.treeUpgrade.length\n tree.fork = e.treeUpgrade.fork\n tree.rootHash = null\n tree.signature = e.treeUpgrade.signature\n }\n }\n\n if (userData.has('corestore/name') && userData.has('corestore/namespace')) {\n core.alias = {\n name: b4a.toString(userData.get('corestore/name')),\n namespace: userData.get('corestore/namespace')\n }\n userData.delete('corestore/name')\n userData.delete('corestore/namespace')\n }\n\n for (const [key, value] of userData) {\n ctx.putUserData(key, value)\n }\n\n ctx.setAuth(auth)\n\n const getTreeNode = (index) => (treeNodes.get(index) || getTreeNodeFromFile(files.tree, index))\n\n if (tree.length) {\n if (tree.rootHash === null) tree.rootHash = crypto.tree(await getRoots(tree.length, getTreeNode))\n ctx.setHead(tree)\n }\n\n tx.putCore(discoveryKey, core)\n if (core.alias) tx.putCoreByAlias(core.alias, discoveryKey)\n\n await ctx.flush()\n }\n\n head.version = version\n tx.setHead(head)\n tx.apply()\n\n if (dryRun) return\n\n await View.flush(view.changes, storage.db)\n\n if (gc) await rm(primaryKeyFile)\n}\n\nclass BlockSlicer {\n constructor (filename) {\n this.stream = fs.createReadStream(filename)\n this.closed = new Promise(resolve => this.stream.once('close', resolve))\n this.offset = 0\n this.overflow = null\n }\n\n async take (offset, size) {\n let buffer = null\n if (offset < this.offset) throw new Error('overread')\n\n while (true) {\n let data = null\n\n if (this.overflow) {\n data = this.overflow\n this.overflow = null\n } else {\n data = this.stream.read()\n\n if (!data) {\n await new Promise(resolve => this.stream.once('readable', resolve))\n continue\n }\n }\n\n let chunk = null\n\n if (this.offset === offset || buffer) {\n chunk = data\n } else if (this.offset + data.byteLength > offset) {\n chunk = data.subarray(offset - this.offset)\n }\n\n this.offset += data.byteLength\n if (!chunk) continue\n\n if (buffer) buffer = b4a.concat([buffer, chunk])\n else buffer = chunk\n\n if (buffer.byteLength < size) continue\n\n const result = buffer.subarray(0, size)\n this.overflow = size === buffer.byteLength ? null : buffer.subarray(result.byteLength)\n this.offset -= (this.overflow ? this.overflow.byteLength : 0)\n return result\n }\n }\n\n close () {\n this.stream.on('error', noop)\n this.stream.destroy()\n return this.closed\n }\n}\n\nclass TreeSlicer {\n constructor () {\n this.buffer = null\n this.offset = 0\n }\n\n get size () {\n return this.buffer === null ? 0 : this.buffer.byteLength\n }\n\n push (data) {\n if (this.buffer === null) this.buffer = data\n else this.buffer = b4a.concat([this.buffer, data])\n this.offset += data.byteLength\n }\n\n skip () {\n let skipped = 0\n\n if (TREE_01_SKIP === null) {\n TREE_16_SKIP = b4a.alloc(16 * 40 * 100)\n TREE_04_SKIP = TREE_16_SKIP.subarray(0, 4 * 40 * 100)\n TREE_01_SKIP = TREE_16_SKIP.subarray(0, 1 * 40 * 100)\n }\n\n while (true) {\n if (this.buffer.byteLength >= TREE_16_SKIP.byteLength) {\n if (b4a.equals(this.buffer.subarray(0, TREE_16_SKIP.byteLength), TREE_16_SKIP)) {\n this.buffer = this.buffer.subarray(TREE_16_SKIP.byteLength)\n skipped += 1600\n continue\n }\n }\n\n if (this.buffer.byteLength >= TREE_04_SKIP.byteLength) {\n if (b4a.equals(this.buffer.subarray(0, TREE_04_SKIP.byteLength), TREE_04_SKIP)) {\n this.buffer = this.buffer.subarray(TREE_04_SKIP.byteLength)\n skipped += 400\n continue\n }\n }\n\n if (this.buffer.byteLength >= TREE_01_SKIP.byteLength) {\n if (b4a.equals(this.buffer.subarray(0, TREE_01_SKIP.byteLength), TREE_01_SKIP)) {\n this.buffer = this.buffer.subarray(TREE_01_SKIP.byteLength)\n skipped += 100\n continue\n }\n }\n break\n }\n\n return skipped\n }\n\n take () {\n const len = 40\n\n if (len <= this.size) {\n const chunk = this.buffer.subarray(0, len)\n this.buffer = this.buffer.subarray(len)\n return chunk\n }\n\n return null\n }\n}\n\nasync function core (core, { version, dryRun = true, gc = true }) {\n if (dryRun) return // dryRun mode not supported atm\n\n const rx = core.read()\n\n const promises = [rx.getAuth(), rx.getHead()]\n rx.tryFlush()\n\n const [auth, head] = await Promise.all(promises)\n\n if (!auth) return\n\n const dk = b4a.toString(auth.discoveryKey, 'hex')\n const files = getFiles(path.join(core.store.path, 'cores', dk.slice(0, 2), dk.slice(2, 4), dk))\n\n if (head === null || head.length === 0) {\n await commitCoreMigration(auth, core, version)\n if (gc) await runGC()\n return // no data\n }\n\n const oplog = await readOplog(files.oplog)\n if (!oplog) {\n const writable = !!auth.keyPair\n\n if (writable) {\n throw new Error('No oplog available writable core for ' + files.oplog + ', length = ' + (head ? head.length : 0))\n }\n\n // if not writable, just nuke it to recover, some bad state happened here, prop corruption from earlier versions\n const w = core.write()\n\n w.deleteBlockRange(0, -1)\n w.deleteTreeNodeRange(0, -1)\n w.deleteBitfieldPageRange(0, -1)\n w.deleteHead()\n\n await w.flush()\n\n await commitCoreMigration(auth, core, version)\n if (gc) await runGC()\n return // no data\n }\n\n const treeData = new TreeSlicer()\n\n let treeIndex = 0\n\n if (await exists(files.tree)) {\n for await (const data of fs.createReadStream(files.tree)) {\n treeData.push(data)\n\n let write = null\n\n while (true) {\n const skip = treeData.skip()\n treeIndex += skip\n\n const buf = treeData.take()\n if (buf === null) break\n\n const index = treeIndex++\n if (b4a.equals(buf, EMPTY_NODE)) continue\n\n if (write === null) write = core.write()\n write.putTreeNode(decodeTreeNode(index, buf))\n }\n\n if (write !== null) await write.flush()\n }\n }\n\n const buf = []\n if (await exists(files.bitfield)) {\n for await (const data of fs.createReadStream(files.bitfield)) {\n buf.push(data)\n }\n }\n\n let bitfield = b4a.concat(buf)\n if (bitfield.byteLength & 4095) bitfield = b4a.concat([bitfield, b4a.alloc(4096 - (bitfield.byteLength & 4095))])\n\n const pages = new Map()\n const headerBits = new Map()\n\n const roots = await getRootsFromStorage(core, head.length)\n\n for (const e of oplog.entries) {\n if (!e.bitfield) continue\n\n for (let i = 0; i < e.bitfield.length; i++) {\n headerBits.set(i + e.bitfield.start, !e.bitfield.drop)\n }\n }\n\n let batch = []\n\n const cache = new Map()\n const blocks = new BlockSlicer(files.data)\n\n for (const index of allBits(bitfield)) {\n if (headerBits.get(index) === false) continue\n if (index >= head.length) continue\n\n setBitInPage(index)\n\n batch.push(index)\n if (batch.length < 1024) continue\n\n await writeBlocksBatch()\n continue\n }\n\n if (batch.length) await writeBlocksBatch()\n\n await blocks.close()\n\n const w = core.write()\n\n for (const [index, bit] of headerBits) {\n if (!bit) continue\n if (index >= head.length) continue\n\n setBitInPage(index)\n\n const blk = await getBlockFromFile(files.data, core, index, roots, cache)\n w.putBlock(index, blk)\n }\n\n for (const [index, page] of pages) {\n w.putBitfieldPage(index, b4a.from(page.buffer, page.byteOffset, page.byteLength))\n }\n\n await w.flush()\n\n let contiguousLength = 0\n for await (const data of core.createBlockStream()) {\n if (data.index === contiguousLength) contiguousLength++\n else break\n }\n\n if (contiguousLength) {\n const w = core.write()\n w.setHints({ contiguousLength })\n await w.flush()\n }\n\n await commitCoreMigration(auth, core, version)\n\n if (gc) await runGC()\n\n async function runGC () {\n await rm(files.path)\n await rmdir(path.join(files.path, '..'))\n await rmdir(path.join(files.path, '../..'))\n await rmdir(path.join(core.store.path, 'cores'))\n }\n\n function setBitInPage (index) {\n const n = index & 32767\n const p = (index - n) / 32768\n\n let page = pages.get(p)\n\n if (!page) {\n page = new Uint32Array(1024)\n pages.set(p, page)\n }\n\n const o = n & 31\n const b = (n - o) / 32\n const v = 1 << o\n\n page[b] |= v\n }\n\n async function writeBlocksBatch () {\n const read = core.read()\n const promises = []\n for (const index of batch) promises.push(getByteRangeFromStorage(read, 2 * index, roots, cache))\n read.tryFlush()\n\n const r = await Promise.all(promises)\n const tx = core.write()\n\n for (let i = 0; i < r.length; i++) {\n const index = batch[i]\n const [offset, size] = r[i]\n\n const blk = await blocks.take(offset, size)\n tx.putBlock(index, blk)\n }\n\n batch = []\n if (cache.size > 16384) cache.clear()\n\n await tx.flush()\n }\n}\n\nasync function commitCoreMigration (auth, core, version) {\n const view = new View()\n const rx = new CorestoreRX(core.db, view)\n\n const storeCorePromise = rx.getCore(auth.discoveryKey)\n rx.tryFlush()\n\n const storeCore = await storeCorePromise\n\n storeCore.version = version\n\n const tx = new CorestoreTX(view)\n\n tx.putCore(auth.discoveryKey, storeCore)\n tx.apply()\n\n await View.flush(view.changes, core.db)\n}\n\nasync function getBlockFromFile (file, core, index, roots, cache) {\n const rx = core.read()\n const promise = getByteRangeFromStorage(rx, 2 * index, roots, cache)\n rx.tryFlush()\n const [offset, size] = await promise\n\n return new Promise(function (resolve) {\n readAll(file, size, offset, function (err, buf) {\n if (err) return resolve(null)\n resolve(buf)\n })\n })\n}\n\nfunction getFiles (dir) {\n return {\n path: dir,\n oplog: path.join(dir, 'oplog'),\n data: path.join(dir, 'data'),\n tree: path.join(dir, 'tree'),\n bitfield: path.join(dir, 'bitfield')\n }\n}\n\nasync function getRootsFromStorage (core, length) {\n const all = []\n const rx = core.read()\n for (const index of flat.fullRoots(2 * length)) {\n all.push(rx.getTreeNode(index))\n }\n rx.tryFlush()\n return Promise.all(all)\n}\n\nasync function getRoots (length, getTreeNode) {\n const all = []\n for (const index of flat.fullRoots(2 * length)) {\n all.push(await getTreeNode(index))\n }\n return all\n}\n\nfunction getCached (read, cache, index) {\n if (cache.has(index)) return cache.get(index)\n const p = read.getTreeNode(index)\n cache.set(index, p)\n return p\n}\n\nasync function getByteRangeFromStorage (read, index, roots, cache) {\n const promises = [getCached(read, cache, index), getByteOffsetFromStorage(read, index, roots, cache)]\n const [node, offset] = await Promise.all(promises)\n if (!node) throw new Error('Node not found during migration: ' + index)\n return [offset, node.size]\n}\n\nasync function getByteOffsetFromStorage (rx, index, roots, cache) {\n if (index === 0) return 0\n if ((index & 1) === 1) index = flat.leftSpan(index)\n\n let head = 0\n let offset = 0\n\n for (const node of roots) { // all async ticks happen once we find the root so safe\n head += 2 * ((node.index - head) + 1)\n\n if (index >= head) {\n offset += node.size\n continue\n }\n\n const ite = flat.iterator(node.index)\n const promises = []\n\n while (ite.index !== index) {\n if (index < ite.index) {\n ite.leftChild()\n } else {\n promises.push(getCached(rx, cache, ite.leftChild()))\n ite.sibling()\n }\n }\n\n const nodes = await Promise.all(promises)\n for (const node of nodes) offset += node.size\n\n return offset\n }\n\n throw new Error('Failed to find offset')\n}\n\nfunction decodeTreeNode (index, buf) {\n return { index, size: c.decode(c.uint64, buf), hash: buf.subarray(8) }\n}\n\nasync function getTreeNodeFromFile (file, index) {\n return new Promise(function (resolve) {\n readAll(file, 40, index * 40, function (err, buf) {\n if (err) return resolve(null)\n resolve(decodeTreeNode(index, buf))\n })\n })\n}\n\nfunction readAll (filename, length, pos, cb) {\n const buf = b4a.alloc(length)\n\n fs.open(filename, 'r', function (err, fd) {\n if (err) return cb(err)\n\n let offset = 0\n\n fs.read(fd, buf, offset, buf.byteLength, pos, function loop (err, read) {\n if (err) return done(err)\n if (read === 0) return done(new Error('Partial read'))\n offset += read\n if (offset === buf.byteLength) return done(null, buf)\n fs.read(fd, offset, buf.byteLength - offset, buf, pos + offset, loop)\n })\n\n function done (err, value) {\n fs.close(fd, () => cb(err, value))\n }\n })\n}\n\nasync function readdir (dir) {\n try {\n return await fs.promises.readdir(dir)\n } catch {\n return []\n }\n}\n\nasync function exists (file) {\n try {\n await fs.promises.stat(file)\n return true\n } catch {\n return false\n }\n}\n\nasync function readFile (file) {\n try {\n return await fs.promises.readFile(file)\n } catch {\n return null\n }\n}\n\nasync function rm (dir) {\n try {\n await fs.promises.rm(dir, { recursive: true })\n } catch {}\n}\n\nasync function rmdir (dir) {\n try {\n await fs.promises.rmdir(dir)\n } catch {}\n}\n\nfunction * allBits (buffer) {\n for (let i = 0; i < buffer.byteLength; i += EMPTY_PAGE.byteLength) {\n const page = buffer.subarray(i, i + EMPTY_NODE.byteLength)\n if (b4a.equals(page, EMPTY_PAGE)) continue\n\n const view = new Uint32Array(page.buffer, page.byteOffset, EMPTY_PAGE.byteLength / 4)\n\n for (let j = 0; j < view.length; j++) {\n const n = view[j]\n if (n === 0) continue\n\n for (let k = 0; k < 32; k++) {\n const m = 1 << k\n if (n & m) yield i * 8 + j * 32 + k\n }\n }\n }\n}\n\nfunction readOplog (oplog) {\n return new Promise(function (resolve) {\n fs.readFile(oplog, function (err, buffer) {\n if (err) return resolve(null)\n\n const state = { start: 0, end: buffer.byteLength, buffer }\n const headers = [1, 0]\n\n const h1 = decodeOplogHeader(state)\n state.start = 4096\n\n const h2 = decodeOplogHeader(state)\n state.start = 4096 * 2\n\n if (!h1 && !h2) return resolve(null)\n\n if (h1 && !h2) {\n headers[0] = h1.header\n headers[1] = h1.header\n } else if (!h1 && h2) {\n headers[0] = (h2.header + 1) & 1\n headers[1] = h2.header\n } else {\n headers[0] = h1.header\n headers[1] = h2.header\n }\n\n const header = (headers[0] + headers[1]) & 1\n const result = { path: path.dirname(oplog), header: null, entries: [] }\n const decoded = []\n\n result.header = header ? h2.message : h1.message\n\n if (result.header.external) {\n fs.readFile(path.join(oplog, '../header'), function (err, buffer) {\n if (err) return resolve(null)\n const start = result.header.external.start\n const end = start + result.header.external.length\n result.header = m.oplog.header.decode({ buffer, start, end })\n finish()\n })\n return\n }\n\n finish()\n\n function finish () {\n while (true) {\n const entry = decodeOplogEntry(state)\n if (!entry) break\n if (entry.header !== header) break\n\n decoded.push(entry)\n }\n\n while (decoded.length > 0 && decoded[decoded.length - 1].partial) decoded.pop()\n\n for (const e of decoded) {\n result.entries.push(e.message)\n }\n\n resolve(result)\n }\n })\n })\n}\n\nfunction noop () {}\n// needed here for compat, copied from old hypercore, do not change this\n\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\n\nconst EMPTY = b4a.alloc(0)\nconst DEFAULT_NAMESPACE = b4a.from('4144eea531e483d54e0c14f4ca68e0644f355343ff6fcb0f005200e12cd747cb', 'hex')\n\nconst hashes = {\n preencode (state, m) {\n state.end++ // small uint\n },\n encode (state, m) {\n if (m === 'blake2b') {\n c.uint.encode(state, 0)\n return\n }\n\n throw new Error('Unknown hash: ' + m)\n },\n decode (state) {\n const n = c.uint.decode(state)\n if (n === 0) return 'blake2b'\n throw new Error('Unknown hash id: ' + n)\n }\n}\n\nconst signatures = {\n preencode (state, m) {\n state.end++ // small uint\n },\n encode (state, m) {\n if (m === 'ed25519') {\n c.uint.encode(state, 0)\n return\n }\n\n throw new Error('Unknown signature: ' + m)\n },\n decode (state) {\n const n = c.uint.decode(state)\n if (n === 0) return 'ed25519'\n throw new Error('Unknown signature id: ' + n)\n }\n}\n\nconst signer = {\n preencode (state, m) {\n signatures.preencode(state, m.signature)\n c.fixed32.preencode(state, m.namespace)\n c.fixed32.preencode(state, m.publicKey)\n },\n encode (state, m) {\n signatures.encode(state, m.signature)\n c.fixed32.encode(state, m.namespace)\n c.fixed32.encode(state, m.publicKey)\n },\n decode (state) {\n return {\n signature: signatures.decode(state),\n namespace: c.fixed32.decode(state),\n publicKey: c.fixed32.decode(state)\n }\n }\n}\n\nconst signerArray = c.array(signer)\n\nconst prologue = {\n preencode (state, p) {\n c.fixed32.preencode(state, p.hash)\n c.uint.preencode(state, p.length)\n },\n encode (state, p) {\n c.fixed32.encode(state, p.hash)\n c.uint.encode(state, p.length)\n },\n decode (state) {\n return {\n hash: c.fixed32.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst manifestv0 = {\n preencode (state, m) {\n hashes.preencode(state, m.hash)\n state.end++ // type\n\n if (m.prologue && m.signers.length === 0) {\n c.fixed32.preencode(state, m.prologue.hash)\n return\n }\n\n if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) {\n signer.preencode(state, m.signers[0])\n } else {\n state.end++ // flags\n c.uint.preencode(state, m.quorum)\n signerArray.preencode(state, m.signers)\n }\n },\n encode (state, m) {\n hashes.encode(state, m.hash)\n\n if (m.prologue && m.signers.length === 0) {\n c.uint.encode(state, 0)\n c.fixed32.encode(state, m.prologue.hash)\n return\n }\n\n if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) {\n c.uint.encode(state, 1)\n signer.encode(state, m.signers[0])\n } else {\n c.uint.encode(state, 2)\n c.uint.encode(state, m.allowPatch ? 1 : 0)\n c.uint.encode(state, m.quorum)\n signerArray.encode(state, m.signers)\n }\n },\n decode (state) {\n const hash = hashes.decode(state)\n const type = c.uint.decode(state)\n\n if (type > 2) throw new Error('Unknown type: ' + type)\n\n if (type === 0) {\n return {\n version: 0,\n hash,\n allowPatch: false,\n quorum: 0,\n signers: [],\n prologue: {\n hash: c.fixed32.decode(state),\n length: 0\n }\n }\n }\n\n if (type === 1) {\n return {\n version: 0,\n hash,\n allowPatch: false,\n quorum: 1,\n signers: [signer.decode(state)],\n prologue: null\n }\n }\n\n const flags = c.uint.decode(state)\n\n return {\n version: 0,\n hash,\n allowPatch: (flags & 1) !== 0,\n quorum: c.uint.decode(state),\n signers: signerArray.decode(state),\n prologue: null\n }\n }\n}\n\nconst manifest = exports.manifest = {\n preencode (state, m) {\n state.end++ // version\n if (m.version === 0) return manifestv0.preencode(state, m)\n\n state.end++ // flags\n hashes.preencode(state, m.hash)\n\n c.uint.preencode(state, m.quorum)\n signerArray.preencode(state, m.signers)\n if (m.prologue) prologue.preencode(state, m.prologue)\n },\n encode (state, m) {\n c.uint.encode(state, m.version)\n if (m.version === 0) return manifestv0.encode(state, m)\n\n c.uint.encode(state, (m.allowPatch ? 1 : 0) | (m.prologue ? 2 : 0) | (m.unencrypted ? 4 : 0))\n hashes.encode(state, m.hash)\n\n c.uint.encode(state, m.quorum)\n signerArray.encode(state, m.signers)\n if (m.prologue) prologue.encode(state, m.prologue)\n },\n decode (state) {\n const v = c.uint.decode(state)\n if (v === 0) return manifestv0.decode(state)\n if (v !== 1) throw new Error('Unknown version: ' + v)\n\n const flags = c.uint.decode(state)\n const hash = hashes.decode(state)\n const quorum = c.uint.decode(state)\n const signers = signerArray.decode(state)\n const unencrypted = (flags & 4) !== 0\n\n return {\n version: 1,\n hash,\n allowPatch: (flags & 1) !== 0,\n quorum,\n signers,\n prologue: (flags & 2) === 0 ? null : prologue.decode(state),\n unencrypted\n }\n }\n}\n\nconst node = {\n preencode (state, n) {\n c.uint.preencode(state, n.index)\n c.uint.preencode(state, n.size)\n c.fixed32.preencode(state, n.hash)\n },\n encode (state, n) {\n c.uint.encode(state, n.index)\n c.uint.encode(state, n.size)\n c.fixed32.encode(state, n.hash)\n },\n decode (state) {\n return {\n index: c.uint.decode(state),\n size: c.uint.decode(state),\n hash: c.fixed32.decode(state)\n }\n }\n}\n\nconst nodeArray = c.array(node)\n\nconst wire = exports.wire = {}\n\nwire.handshake = {\n preencode (state, m) {\n c.uint.preencode(state, 1)\n c.fixed32.preencode(state, m.capability)\n },\n encode (state, m) {\n c.uint.encode(state, m.seeks ? 1 : 0)\n c.fixed32.encode(state, m.capability)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n return {\n seeks: (flags & 1) !== 0,\n capability: c.fixed32.decode(state)\n }\n }\n}\n\nconst requestBlock = {\n preencode (state, b) {\n c.uint.preencode(state, b.index)\n c.uint.preencode(state, b.nodes)\n },\n encode (state, b) {\n c.uint.encode(state, b.index)\n c.uint.encode(state, b.nodes)\n },\n decode (state) {\n return {\n index: c.uint.decode(state),\n nodes: c.uint.decode(state)\n }\n }\n}\n\nconst requestSeek = {\n preencode (state, s) {\n c.uint.preencode(state, s.bytes)\n c.uint.preencode(state, s.padding)\n },\n encode (state, s) {\n c.uint.encode(state, s.bytes)\n c.uint.encode(state, s.padding)\n },\n decode (state) {\n return {\n bytes: c.uint.decode(state),\n padding: c.uint.decode(state)\n }\n }\n}\n\nconst requestUpgrade = {\n preencode (state, u) {\n c.uint.preencode(state, u.start)\n c.uint.preencode(state, u.length)\n },\n encode (state, u) {\n c.uint.encode(state, u.start)\n c.uint.encode(state, u.length)\n },\n decode (state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nwire.request = {\n preencode (state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.id)\n c.uint.preencode(state, m.fork)\n\n if (m.block) requestBlock.preencode(state, m.block)\n if (m.hash) requestBlock.preencode(state, m.hash)\n if (m.seek) requestSeek.preencode(state, m.seek)\n if (m.upgrade) requestUpgrade.preencode(state, m.upgrade)\n if (m.priority) c.uint.preencode(state, m.priority)\n },\n encode (state, m) {\n const flags = (m.block ? 1 : 0) | (m.hash ? 2 : 0) | (m.seek ? 4 : 0) | (m.upgrade ? 8 : 0) | (m.manifest ? 16 : 0) | (m.priority ? 32 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.id)\n c.uint.encode(state, m.fork)\n\n if (m.block) requestBlock.encode(state, m.block)\n if (m.hash) requestBlock.encode(state, m.hash)\n if (m.seek) requestSeek.encode(state, m.seek)\n if (m.upgrade) requestUpgrade.encode(state, m.upgrade)\n if (m.priority) c.uint.encode(state, m.priority)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n id: c.uint.decode(state),\n fork: c.uint.decode(state),\n block: flags & 1 ? requestBlock.decode(state) : null,\n hash: flags & 2 ? requestBlock.decode(state) : null,\n seek: flags & 4 ? requestSeek.decode(state) : null,\n upgrade: flags & 8 ? requestUpgrade.decode(state) : null,\n manifest: (flags & 16) !== 0,\n priority: flags & 32 ? c.uint.decode(state) : 0\n }\n }\n}\n\nwire.cancel = {\n preencode (state, m) {\n c.uint.preencode(state, m.request)\n },\n encode (state, m) {\n c.uint.encode(state, m.request)\n },\n decode (state, m) {\n return {\n request: c.uint.decode(state)\n }\n }\n}\n\nconst dataUpgrade = {\n preencode (state, u) {\n c.uint.preencode(state, u.start)\n c.uint.preencode(state, u.length)\n nodeArray.preencode(state, u.nodes)\n nodeArray.preencode(state, u.additionalNodes)\n c.buffer.preencode(state, u.signature)\n },\n encode (state, u) {\n c.uint.encode(state, u.start)\n c.uint.encode(state, u.length)\n nodeArray.encode(state, u.nodes)\n nodeArray.encode(state, u.additionalNodes)\n c.buffer.encode(state, u.signature)\n },\n decode (state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state),\n nodes: nodeArray.decode(state),\n additionalNodes: nodeArray.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst dataSeek = {\n preencode (state, s) {\n c.uint.preencode(state, s.bytes)\n nodeArray.preencode(state, s.nodes)\n },\n encode (state, s) {\n c.uint.encode(state, s.bytes)\n nodeArray.encode(state, s.nodes)\n },\n decode (state) {\n return {\n bytes: c.uint.decode(state),\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nconst dataBlock = {\n preencode (state, b) {\n c.uint.preencode(state, b.index)\n c.buffer.preencode(state, b.value)\n nodeArray.preencode(state, b.nodes)\n },\n encode (state, b) {\n c.uint.encode(state, b.index)\n c.buffer.encode(state, b.value)\n nodeArray.encode(state, b.nodes)\n },\n decode (state) {\n return {\n index: c.uint.decode(state),\n value: c.buffer.decode(state) || EMPTY,\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nconst dataHash = {\n preencode (state, b) {\n c.uint.preencode(state, b.index)\n nodeArray.preencode(state, b.nodes)\n },\n encode (state, b) {\n c.uint.encode(state, b.index)\n nodeArray.encode(state, b.nodes)\n },\n decode (state) {\n return {\n index: c.uint.decode(state),\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nwire.data = {\n preencode (state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.request)\n c.uint.preencode(state, m.fork)\n\n if (m.block) dataBlock.preencode(state, m.block)\n if (m.hash) dataHash.preencode(state, m.hash)\n if (m.seek) dataSeek.preencode(state, m.seek)\n if (m.upgrade) dataUpgrade.preencode(state, m.upgrade)\n if (m.manifest) manifest.preencode(state, m.manifest)\n },\n encode (state, m) {\n const flags = (m.block ? 1 : 0) | (m.hash ? 2 : 0) | (m.seek ? 4 : 0) | (m.upgrade ? 8 : 0) | (m.manifest ? 16 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.request)\n c.uint.encode(state, m.fork)\n\n if (m.block) dataBlock.encode(state, m.block)\n if (m.hash) dataHash.encode(state, m.hash)\n if (m.seek) dataSeek.encode(state, m.seek)\n if (m.upgrade) dataUpgrade.encode(state, m.upgrade)\n if (m.manifest) manifest.encode(state, m.manifest)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n request: c.uint.decode(state),\n fork: c.uint.decode(state),\n block: flags & 1 ? dataBlock.decode(state) : null,\n hash: flags & 2 ? dataHash.decode(state) : null,\n seek: flags & 4 ? dataSeek.decode(state) : null,\n upgrade: flags & 8 ? dataUpgrade.decode(state) : null,\n manifest: flags & 16 ? manifest.decode(state) : null\n }\n }\n}\n\nwire.noData = {\n preencode (state, m) {\n c.uint.preencode(state, m.request)\n },\n encode (state, m) {\n c.uint.encode(state, m.request)\n },\n decode (state, m) {\n return {\n request: c.uint.decode(state)\n }\n }\n}\n\nwire.want = {\n preencode (state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n },\n encode (state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n },\n decode (state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nwire.unwant = {\n preencode (state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n },\n encode (state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n },\n decode (state, m) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nwire.range = {\n preencode (state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.start)\n if (m.length !== 1) c.uint.preencode(state, m.length)\n },\n encode (state, m) {\n c.uint.encode(state, (m.drop ? 1 : 0) | (m.length === 1 ? 2 : 0))\n c.uint.encode(state, m.start)\n if (m.length !== 1) c.uint.encode(state, m.length)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n drop: (flags & 1) !== 0,\n start: c.uint.decode(state),\n length: (flags & 2) !== 0 ? 1 : c.uint.decode(state)\n }\n }\n}\n\nwire.bitfield = {\n preencode (state, m) {\n c.uint.preencode(state, m.start)\n c.uint32array.preencode(state, m.bitfield)\n },\n encode (state, m) {\n c.uint.encode(state, m.start)\n c.uint32array.encode(state, m.bitfield)\n },\n decode (state, m) {\n return {\n start: c.uint.decode(state),\n bitfield: c.uint32array.decode(state)\n }\n }\n}\n\nwire.sync = {\n preencode (state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.fork)\n c.uint.preencode(state, m.length)\n c.uint.preencode(state, m.remoteLength)\n },\n encode (state, m) {\n c.uint.encode(state, (m.canUpgrade ? 1 : 0) | (m.uploading ? 2 : 0) | (m.downloading ? 4 : 0) | (m.hasManifest ? 8 : 0))\n c.uint.encode(state, m.fork)\n c.uint.encode(state, m.length)\n c.uint.encode(state, m.remoteLength)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n\n return {\n fork: c.uint.decode(state),\n length: c.uint.decode(state),\n remoteLength: c.uint.decode(state),\n canUpgrade: (flags & 1) !== 0,\n uploading: (flags & 2) !== 0,\n downloading: (flags & 4) !== 0,\n hasManifest: (flags & 8) !== 0\n }\n }\n}\n\nwire.reorgHint = {\n preencode (state, m) {\n c.uint.preencode(state, m.from)\n c.uint.preencode(state, m.to)\n c.uint.preencode(state, m.ancestors)\n },\n encode (state, m) {\n c.uint.encode(state, m.from)\n c.uint.encode(state, m.to)\n c.uint.encode(state, m.ancestors)\n },\n decode (state) {\n return {\n from: c.uint.encode(state),\n to: c.uint.encode(state),\n ancestors: c.uint.encode(state)\n }\n }\n}\n\nwire.extension = {\n preencode (state, m) {\n c.string.preencode(state, m.name)\n c.raw.preencode(state, m.message)\n },\n encode (state, m) {\n c.string.encode(state, m.name)\n c.raw.encode(state, m.message)\n },\n decode (state) {\n return {\n name: c.string.decode(state),\n message: c.raw.decode(state)\n }\n }\n}\n\nconst keyValue = {\n preencode (state, p) {\n c.string.preencode(state, p.key)\n c.buffer.preencode(state, p.value)\n },\n encode (state, p) {\n c.string.encode(state, p.key)\n c.buffer.encode(state, p.value)\n },\n decode (state) {\n return {\n key: c.string.decode(state),\n value: c.buffer.decode(state)\n }\n }\n}\n\nconst treeUpgrade = {\n preencode (state, u) {\n c.uint.preencode(state, u.fork)\n c.uint.preencode(state, u.ancestors)\n c.uint.preencode(state, u.length)\n c.buffer.preencode(state, u.signature)\n },\n encode (state, u) {\n c.uint.encode(state, u.fork)\n c.uint.encode(state, u.ancestors)\n c.uint.encode(state, u.length)\n c.buffer.encode(state, u.signature)\n },\n decode (state) {\n return {\n fork: c.uint.decode(state),\n ancestors: c.uint.decode(state),\n length: c.uint.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst bitfieldUpdate = { // TODO: can maybe be folded into a HAVE later on with the most recent spec\n preencode (state, b) {\n state.end++ // flags\n c.uint.preencode(state, b.start)\n c.uint.preencode(state, b.length)\n },\n encode (state, b) {\n state.buffer[state.start++] = b.drop ? 1 : 0\n c.uint.encode(state, b.start)\n c.uint.encode(state, b.length)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n return {\n drop: (flags & 1) !== 0,\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst oplog = exports.oplog = {}\n\noplog.entry = {\n preencode (state, m) {\n state.end++ // flags\n if (m.userData) keyValue.preencode(state, m.userData)\n if (m.treeNodes) nodeArray.preencode(state, m.treeNodes)\n if (m.treeUpgrade) treeUpgrade.preencode(state, m.treeUpgrade)\n if (m.bitfield) bitfieldUpdate.preencode(state, m.bitfield)\n },\n encode (state, m) {\n const s = state.start++\n let flags = 0\n\n if (m.userData) {\n flags |= 1\n keyValue.encode(state, m.userData)\n }\n if (m.treeNodes) {\n flags |= 2\n nodeArray.encode(state, m.treeNodes)\n }\n if (m.treeUpgrade) {\n flags |= 4\n treeUpgrade.encode(state, m.treeUpgrade)\n }\n if (m.bitfield) {\n flags |= 8\n bitfieldUpdate.encode(state, m.bitfield)\n }\n\n state.buffer[s] = flags\n },\n decode (state) {\n const flags = c.uint.decode(state)\n return {\n userData: (flags & 1) !== 0 ? keyValue.decode(state) : null,\n treeNodes: (flags & 2) !== 0 ? nodeArray.decode(state) : null,\n treeUpgrade: (flags & 4) !== 0 ? treeUpgrade.decode(state) : null,\n bitfield: (flags & 8) !== 0 ? bitfieldUpdate.decode(state) : null\n }\n }\n}\n\nconst keyPair = {\n preencode (state, kp) {\n c.buffer.preencode(state, kp.publicKey)\n c.buffer.preencode(state, kp.secretKey)\n },\n encode (state, kp) {\n c.buffer.encode(state, kp.publicKey)\n c.buffer.encode(state, kp.secretKey)\n },\n decode (state) {\n return {\n publicKey: c.buffer.decode(state),\n secretKey: c.buffer.decode(state)\n }\n }\n}\n\nconst reorgHint = {\n preencode (state, r) {\n c.uint.preencode(state, r.from)\n c.uint.preencode(state, r.to)\n c.uint.preencode(state, r.ancestors)\n },\n encode (state, r) {\n c.uint.encode(state, r.from)\n c.uint.encode(state, r.to)\n c.uint.encode(state, r.ancestors)\n },\n decode (state) {\n return {\n from: c.uint.decode(state),\n to: c.uint.decode(state),\n ancestors: c.uint.decode(state)\n }\n }\n}\n\nconst reorgHintArray = c.array(reorgHint)\n\nconst hints = {\n preencode (state, h) {\n reorgHintArray.preencode(state, h.reorgs)\n c.uint.preencode(state, h.contiguousLength)\n },\n encode (state, h) {\n reorgHintArray.encode(state, h.reorgs)\n c.uint.encode(state, h.contiguousLength)\n },\n decode (state) {\n return {\n reorgs: reorgHintArray.decode(state),\n contiguousLength: state.start < state.end ? c.uint.decode(state) : 0\n }\n }\n}\n\nconst treeHeader = {\n preencode (state, t) {\n c.uint.preencode(state, t.fork)\n c.uint.preencode(state, t.length)\n c.buffer.preencode(state, t.rootHash)\n c.buffer.preencode(state, t.signature)\n },\n encode (state, t) {\n c.uint.encode(state, t.fork)\n c.uint.encode(state, t.length)\n c.buffer.encode(state, t.rootHash)\n c.buffer.encode(state, t.signature)\n },\n decode (state) {\n return {\n fork: c.uint.decode(state),\n length: c.uint.decode(state),\n rootHash: c.buffer.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst types = {\n preencode (state, t) {\n c.string.preencode(state, t.tree)\n c.string.preencode(state, t.bitfield)\n c.string.preencode(state, t.signer)\n },\n encode (state, t) {\n c.string.encode(state, t.tree)\n c.string.encode(state, t.bitfield)\n c.string.encode(state, t.signer)\n },\n decode (state) {\n return {\n tree: c.string.decode(state),\n bitfield: c.string.decode(state),\n signer: c.string.decode(state)\n }\n }\n}\n\nconst externalHeader = {\n preencode (state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n },\n encode (state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n },\n decode (state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst keyValueArray = c.array(keyValue)\n\noplog.header = {\n preencode (state, h) {\n state.end += 2 // version + flags\n if (h.external) {\n externalHeader.preencode(state, h.external)\n return\n }\n c.fixed32.preencode(state, h.key)\n if (h.manifest) manifest.preencode(state, h.manifest)\n if (h.keyPair) keyPair.preencode(state, h.keyPair)\n keyValueArray.preencode(state, h.userData)\n treeHeader.preencode(state, h.tree)\n hints.preencode(state, h.hints)\n },\n encode (state, h) {\n c.uint.encode(state, 1)\n if (h.external) {\n c.uint.encode(state, 1) // ONLY set the first big for clarity\n externalHeader.encode(state, h.external)\n return\n }\n c.uint.encode(state, (h.manifest ? 2 : 0) | (h.keyPair ? 4 : 0))\n c.fixed32.encode(state, h.key)\n if (h.manifest) manifest.encode(state, h.manifest)\n if (h.keyPair) keyPair.encode(state, h.keyPair)\n keyValueArray.encode(state, h.userData)\n treeHeader.encode(state, h.tree)\n hints.encode(state, h.hints)\n },\n decode (state) {\n const version = c.uint.decode(state)\n\n if (version > 1) {\n throw new Error('Invalid header version. Expected <= 1, got ' + version)\n }\n\n if (version === 0) {\n const old = {\n types: types.decode(state),\n userData: keyValueArray.decode(state),\n tree: treeHeader.decode(state),\n signer: keyPair.decode(state),\n hints: hints.decode(state)\n }\n\n return {\n external: null,\n key: old.signer.publicKey,\n manifest: {\n version: 0,\n hash: old.types.tree,\n allowPatch: false,\n quorum: 1,\n signers: [{\n signature: old.types.signer,\n namespace: DEFAULT_NAMESPACE,\n publicKey: old.signer.publicKey\n }],\n prologue: null\n },\n keyPair: old.signer.secretKey ? old.signer : null,\n userData: old.userData,\n tree: old.tree,\n hints: old.hints\n }\n }\n\n const flags = c.uint.decode(state)\n\n if (flags & 1) {\n return {\n external: externalHeader.decode(state),\n key: null,\n manifest: null,\n keyPair: null,\n userData: null,\n tree: null,\n hints: null\n }\n }\n\n return {\n external: null,\n key: c.fixed32.decode(state),\n manifest: (flags & 2) !== 0 ? manifest.decode(state) : null,\n keyPair: (flags & 4) !== 0 ? keyPair.decode(state) : null,\n userData: keyValueArray.decode(state),\n tree: treeHeader.decode(state),\n hints: hints.decode(state)\n }\n }\n}\n\nconst uintArray = c.array(c.uint)\n\nconst multisigInput = {\n preencode (state, inp) {\n c.uint.preencode(state, inp.signer)\n c.fixed64.preencode(state, inp.signature)\n c.uint.preencode(state, inp.patch)\n },\n encode (state, inp) {\n c.uint.encode(state, inp.signer)\n c.fixed64.encode(state, inp.signature)\n c.uint.encode(state, inp.patch)\n },\n decode (state) {\n return {\n signer: c.uint.decode(state),\n signature: c.fixed64.decode(state),\n patch: c.uint.decode(state)\n }\n }\n}\n\nconst patchEncodingv0 = {\n preencode (state, n) {\n c.uint.preencode(state, n.start)\n c.uint.preencode(state, n.length)\n uintArray.preencode(state, n.nodes)\n },\n encode (state, n) {\n c.uint.encode(state, n.start)\n c.uint.encode(state, n.length)\n uintArray.encode(state, n.nodes)\n },\n decode (state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state),\n nodes: uintArray.decode(state)\n }\n }\n}\n\nconst multisigInputv0 = {\n preencode (state, n) {\n state.end++\n c.uint.preencode(state, n.signer)\n c.fixed64.preencode(state, n.signature)\n if (n.patch) patchEncodingv0.preencode(state, n.patch)\n },\n encode (state, n) {\n c.uint.encode(state, n.patch ? 1 : 0)\n c.uint.encode(state, n.signer)\n c.fixed64.encode(state, n.signature)\n if (n.patch) patchEncodingv0.encode(state, n.patch)\n },\n decode (state) {\n const flags = c.uint.decode(state)\n return {\n signer: c.uint.decode(state),\n signature: c.fixed64.decode(state),\n patch: (flags & 1) ? patchEncodingv0.decode(state) : null\n }\n }\n}\n\nconst multisigInputArrayv0 = c.array(multisigInputv0)\nconst multisigInputArray = c.array(multisigInput)\n\nconst compactNode = {\n preencode (state, n) {\n c.uint.preencode(state, n.index)\n c.uint.preencode(state, n.size)\n c.fixed32.preencode(state, n.hash)\n },\n encode (state, n) {\n c.uint.encode(state, n.index)\n c.uint.encode(state, n.size)\n c.fixed32.encode(state, n.hash)\n },\n decode (state) {\n return {\n index: c.uint.decode(state),\n size: c.uint.decode(state),\n hash: c.fixed32.decode(state)\n }\n }\n}\n\nconst compactNodeArray = c.array(compactNode)\n\nexports.multiSignaturev0 = {\n preencode (state, s) {\n multisigInputArrayv0.preencode(state, s.proofs)\n compactNodeArray.preencode(state, s.patch)\n },\n encode (state, s) {\n multisigInputArrayv0.encode(state, s.proofs)\n compactNodeArray.encode(state, s.patch)\n },\n decode (state) {\n return {\n proofs: multisigInputArrayv0.decode(state),\n patch: compactNodeArray.decode(state)\n }\n }\n}\n\nexports.multiSignature = {\n preencode (state, s) {\n multisigInputArray.preencode(state, s.proofs)\n compactNodeArray.preencode(state, s.patch)\n },\n encode (state, s) {\n multisigInputArray.encode(state, s.proofs)\n compactNodeArray.encode(state, s.patch)\n },\n decode (state) {\n return {\n proofs: multisigInputArray.decode(state),\n patch: compactNodeArray.decode(state)\n }\n }\n}\n{\n \"name\": \"hypercore-storage\",\n \"version\": \"2.4.1\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/*.js\",\n \"spec/hyperschema/*.js\",\n \"migrations/0/*.js\"\n ],\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"node test/all.js\",\n \"test:bare\": \"bare test/all.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\"\n },\n \"author\": \"Holepunch Inc.\",\n \"license\": \"Apache-2.0\",\n \"description\": \"Storage engine for Hypercore\",\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"dependencies\": {\n \"b4a\": \"^1.6.7\",\n \"bare-fs\": \"^4.0.1\",\n \"bare-path\": \"^3.0.0\",\n \"compact-encoding\": \"^2.16.0\",\n \"device-file\": \"^2.1.2\",\n \"flat-tree\": \"^1.12.1\",\n \"hypercore-crypto\": \"^3.4.2\",\n \"hyperschema\": \"^1.7.0\",\n \"index-encoder\": \"^3.3.2\",\n \"resolve-reject-promise\": \"^1.0.0\",\n \"rocksdb-native\": \"^3.11.0\",\n \"scope-lock\": \"^1.2.4\",\n \"streamx\": \"^2.21.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.7.0\",\n \"lunte\": \"^1.2.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"test-tmp\": \"^1.3.1\"\n }\n}\n// This file is autogenerated by the hyperschema compiler\n// Schema Version: 1\n/* eslint-disable camelcase */\n/* eslint-disable quotes */\n/* eslint-disable space-before-function-paren */\n\nconst { c } = require('hyperschema/runtime')\n\nconst VERSION = 1\n\n// eslint-disable-next-line no-unused-vars\nlet version = VERSION\n\n// @corestore/allocated\nconst encoding0 = {\n preencode(state, m) {\n c.uint.preencode(state, m.cores)\n c.uint.preencode(state, m.datas)\n },\n encode(state, m) {\n c.uint.encode(state, m.cores)\n c.uint.encode(state, m.datas)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const r1 = c.uint.decode(state)\n\n return {\n cores: r0,\n datas: r1\n }\n }\n}\n\n// @corestore/head\nconst encoding1 = {\n preencode(state, m) {\n c.uint.preencode(state, m.version)\n state.end++ // max flag is 4 so always one byte\n\n if (m.allocated) encoding0.preencode(state, m.allocated)\n if (m.seed) c.fixed32.preencode(state, m.seed)\n if (m.defaultDiscoveryKey) c.fixed32.preencode(state, m.defaultDiscoveryKey)\n },\n encode(state, m) {\n const flags =\n (m.allocated ? 1 : 0) |\n (m.seed ? 2 : 0) |\n (m.defaultDiscoveryKey ? 4 : 0)\n\n c.uint.encode(state, m.version)\n c.uint.encode(state, flags)\n\n if (m.allocated) encoding0.encode(state, m.allocated)\n if (m.seed) c.fixed32.encode(state, m.seed)\n if (m.defaultDiscoveryKey) c.fixed32.encode(state, m.defaultDiscoveryKey)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const flags = c.uint.decode(state)\n\n return {\n version: r0,\n allocated: (flags & 1) !== 0 ? encoding0.decode(state) : null,\n seed: (flags & 2) !== 0 ? c.fixed32.decode(state) : null,\n defaultDiscoveryKey: (flags & 4) !== 0 ? c.fixed32.decode(state) : null\n }\n }\n}\n\n// @corestore/alias\nconst encoding2 = {\n preencode(state, m) {\n c.string.preencode(state, m.name)\n c.fixed32.preencode(state, m.namespace)\n },\n encode(state, m) {\n c.string.encode(state, m.name)\n c.fixed32.encode(state, m.namespace)\n },\n decode(state) {\n const r0 = c.string.decode(state)\n const r1 = c.fixed32.decode(state)\n\n return {\n name: r0,\n namespace: r1\n }\n }\n}\n\n// @corestore/core\nconst encoding3 = {\n preencode(state, m) {\n c.uint.preencode(state, m.version)\n c.uint.preencode(state, m.corePointer)\n c.uint.preencode(state, m.dataPointer)\n state.end++ // max flag is 1 so always one byte\n\n if (m.alias) encoding2.preencode(state, m.alias)\n },\n encode(state, m) {\n const flags = m.alias ? 1 : 0\n\n c.uint.encode(state, m.version)\n c.uint.encode(state, m.corePointer)\n c.uint.encode(state, m.dataPointer)\n c.uint.encode(state, flags)\n\n if (m.alias) encoding2.encode(state, m.alias)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const r1 = c.uint.decode(state)\n const r2 = c.uint.decode(state)\n const flags = c.uint.decode(state)\n\n return {\n version: r0,\n corePointer: r1,\n dataPointer: r2,\n alias: (flags & 1) !== 0 ? encoding2.decode(state) : null\n }\n }\n}\n\nconst encoding4_enum = {\n blake2b: 'blake2b'\n}\n\n// @core/hashes enum\nconst encoding4 = {\n preencode (state, m) {\n state.end++ // max enum is 0 so always one byte\n },\n encode (state, m) {\n switch (m) {\n case 'blake2b':\n c.uint.encode(state, 0)\n break\n default:\n throw new Error('Unknown enum')\n }\n },\n decode (state) {\n switch (c.uint.decode(state)) {\n case 0:\n return 'blake2b'\n default: return null\n }\n }\n}\n\nconst encoding5_enum = {\n ed25519: 'ed25519'\n}\n\n// @core/signatures enum\nconst encoding5 = {\n preencode (state, m) {\n state.end++ // max enum is 0 so always one byte\n },\n encode (state, m) {\n switch (m) {\n case 'ed25519':\n c.uint.encode(state, 0)\n break\n default:\n throw new Error('Unknown enum')\n }\n },\n decode (state) {\n switch (c.uint.decode(state)) {\n case 0:\n return 'ed25519'\n default: return null\n }\n }\n}\n\n// @core/tree-node\nconst encoding6 = {\n preencode(state, m) {\n c.uint.preencode(state, m.index)\n c.uint.preencode(state, m.size)\n c.fixed32.preencode(state, m.hash)\n },\n encode(state, m) {\n c.uint.encode(state, m.index)\n c.uint.encode(state, m.size)\n c.fixed32.encode(state, m.hash)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const r1 = c.uint.decode(state)\n const r2 = c.fixed32.decode(state)\n\n return {\n index: r0,\n size: r1,\n hash: r2\n }\n }\n}\n\n// @core/signer\nconst encoding7 = {\n preencode(state, m) {\n encoding5.preencode(state, m.signature)\n c.fixed32.preencode(state, m.namespace)\n c.fixed32.preencode(state, m.publicKey)\n },\n encode(state, m) {\n encoding5.encode(state, m.signature)\n c.fixed32.encode(state, m.namespace)\n c.fixed32.encode(state, m.publicKey)\n },\n decode(state) {\n const r0 = encoding5.decode(state)\n const r1 = c.fixed32.decode(state)\n const r2 = c.fixed32.decode(state)\n\n return {\n signature: r0,\n namespace: r1,\n publicKey: r2\n }\n }\n}\n\n// @core/prologue\nconst encoding8 = {\n preencode(state, m) {\n c.fixed32.preencode(state, m.hash)\n c.uint.preencode(state, m.length)\n },\n encode(state, m) {\n c.fixed32.encode(state, m.hash)\n c.uint.encode(state, m.length)\n },\n decode(state) {\n const r0 = c.fixed32.decode(state)\n const r1 = c.uint.decode(state)\n\n return {\n hash: r0,\n length: r1\n }\n }\n}\n\n// @core/manifest.signers\nconst encoding9_4 = c.array(encoding7)\n// @core/manifest.linked\nconst encoding9_6 = c.array(c.fixed32)\n\n// @core/manifest\nconst encoding9 = {\n preencode(state, m) {\n c.uint.preencode(state, m.version)\n state.end++ // max flag is 8 so always one byte\n encoding4.preencode(state, m.hash)\n c.uint.preencode(state, m.quorum)\n encoding9_4.preencode(state, m.signers)\n\n if (m.prologue) encoding8.preencode(state, m.prologue)\n if (m.linked) encoding9_6.preencode(state, m.linked)\n if (m.userData) c.buffer.preencode(state, m.userData)\n },\n encode(state, m) {\n const flags =\n (m.allowPatch ? 1 : 0) |\n (m.prologue ? 2 : 0) |\n (m.linked ? 4 : 0) |\n (m.userData ? 8 : 0)\n\n c.uint.encode(state, m.version)\n c.uint.encode(state, flags)\n encoding4.encode(state, m.hash)\n c.uint.encode(state, m.quorum)\n encoding9_4.encode(state, m.signers)\n\n if (m.prologue) encoding8.encode(state, m.prologue)\n if (m.linked) encoding9_6.encode(state, m.linked)\n if (m.userData) c.buffer.encode(state, m.userData)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const flags = c.uint.decode(state)\n\n return {\n version: r0,\n hash: encoding4.decode(state),\n quorum: c.uint.decode(state),\n allowPatch: (flags & 1) !== 0,\n signers: encoding9_4.decode(state),\n prologue: (flags & 2) !== 0 ? encoding8.decode(state) : null,\n linked: (flags & 4) !== 0 ? encoding9_6.decode(state) : null,\n userData: (flags & 8) !== 0 ? c.buffer.decode(state) : null\n }\n }\n}\n\n// @core/keyPair\nconst encoding10 = {\n preencode(state, m) {\n c.buffer.preencode(state, m.publicKey)\n c.buffer.preencode(state, m.secretKey)\n },\n encode(state, m) {\n c.buffer.encode(state, m.publicKey)\n c.buffer.encode(state, m.secretKey)\n },\n decode(state) {\n const r0 = c.buffer.decode(state)\n const r1 = c.buffer.decode(state)\n\n return {\n publicKey: r0,\n secretKey: r1\n }\n }\n}\n\n// @core/auth.manifest\nconst encoding11_2 = c.frame(encoding9)\n\n// @core/auth\nconst encoding11 = {\n preencode(state, m) {\n c.fixed32.preencode(state, m.key)\n c.fixed32.preencode(state, m.discoveryKey)\n state.end++ // max flag is 4 so always one byte\n\n if (m.manifest) encoding11_2.preencode(state, m.manifest)\n if (m.keyPair) encoding10.preencode(state, m.keyPair)\n if (m.encryptionKey) c.buffer.preencode(state, m.encryptionKey)\n },\n encode(state, m) {\n const flags =\n (m.manifest ? 1 : 0) |\n (m.keyPair ? 2 : 0) |\n (m.encryptionKey ? 4 : 0)\n\n c.fixed32.encode(state, m.key)\n c.fixed32.encode(state, m.discoveryKey)\n c.uint.encode(state, flags)\n\n if (m.manifest) encoding11_2.encode(state, m.manifest)\n if (m.keyPair) encoding10.encode(state, m.keyPair)\n if (m.encryptionKey) c.buffer.encode(state, m.encryptionKey)\n },\n decode(state) {\n const r0 = c.fixed32.decode(state)\n const r1 = c.fixed32.decode(state)\n const flags = c.uint.decode(state)\n\n return {\n key: r0,\n discoveryKey: r1,\n manifest: (flags & 1) !== 0 ? encoding11_2.decode(state) : null,\n keyPair: (flags & 2) !== 0 ? encoding10.decode(state) : null,\n encryptionKey: (flags & 4) !== 0 ? c.buffer.decode(state) : null\n }\n }\n}\n\n// @core/head\nconst encoding12 = {\n preencode(state, m) {\n c.uint.preencode(state, m.fork)\n c.uint.preencode(state, m.length)\n c.fixed32.preencode(state, m.rootHash)\n c.buffer.preencode(state, m.signature)\n },\n encode(state, m) {\n c.uint.encode(state, m.fork)\n c.uint.encode(state, m.length)\n c.fixed32.encode(state, m.rootHash)\n c.buffer.encode(state, m.signature)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const r1 = c.uint.decode(state)\n const r2 = c.fixed32.decode(state)\n const r3 = c.buffer.decode(state)\n\n return {\n fork: r0,\n length: r1,\n rootHash: r2,\n signature: r3\n }\n }\n}\n\n// @core/hints\nconst encoding13 = {\n preencode(state, m) {\n state.end++ // max flag is 2 so always one byte\n\n if (m.contiguousLength) c.uint.preencode(state, m.contiguousLength)\n if (m.remoteContiguousLength) c.uint.preencode(state, m.remoteContiguousLength)\n },\n encode(state, m) {\n const flags =\n (m.contiguousLength ? 1 : 0) |\n (m.remoteContiguousLength ? 2 : 0)\n\n c.uint.encode(state, flags)\n\n if (m.contiguousLength) c.uint.encode(state, m.contiguousLength)\n if (m.remoteContiguousLength) c.uint.encode(state, m.remoteContiguousLength)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n\n return {\n contiguousLength: (flags & 1) !== 0 ? c.uint.decode(state) : 0,\n remoteContiguousLength: (flags & 2) !== 0 ? c.uint.decode(state) : 0\n }\n }\n}\n\n// @core/session\nconst encoding14 = {\n preencode(state, m) {\n c.string.preencode(state, m.name)\n c.uint.preencode(state, m.dataPointer)\n },\n encode(state, m) {\n c.string.encode(state, m.name)\n c.uint.encode(state, m.dataPointer)\n },\n decode(state) {\n const r0 = c.string.decode(state)\n const r1 = c.uint.decode(state)\n\n return {\n name: r0,\n dataPointer: r1\n }\n }\n}\n\n// @core/sessions\nconst encoding15 = c.array(encoding14)\n\n// @core/dependency\nconst encoding16 = {\n preencode(state, m) {\n c.uint.preencode(state, m.dataPointer)\n c.uint.preencode(state, m.length)\n },\n encode(state, m) {\n c.uint.encode(state, m.dataPointer)\n c.uint.encode(state, m.length)\n },\n decode(state) {\n const r0 = c.uint.decode(state)\n const r1 = c.uint.decode(state)\n\n return {\n dataPointer: r0,\n length: r1\n }\n }\n}\n\nfunction setVersion(v) {\n version = v\n}\n\nfunction encode(name, value, v = VERSION) {\n version = v\n return c.encode(getEncoding(name), value)\n}\n\nfunction decode(name, buffer, v = VERSION) {\n version = v\n return c.decode(getEncoding(name), buffer)\n}\n\nfunction getEnum(name) {\n switch (name) {\n case '@core/hashes':\n return encoding4_enum\n case '@core/signatures':\n return encoding5_enum\n default:\n throw new Error('Enum not found ' + name)\n }\n}\n\nfunction getEncoding(name) {\n switch (name) {\n case '@corestore/allocated':\n return encoding0\n case '@corestore/head':\n return encoding1\n case '@corestore/alias':\n return encoding2\n case '@corestore/core':\n return encoding3\n case '@core/hashes':\n return encoding4\n case '@core/signatures':\n return encoding5\n case '@core/tree-node':\n return encoding6\n case '@core/signer':\n return encoding7\n case '@core/prologue':\n return encoding8\n case '@core/manifest':\n return encoding9\n case '@core/keyPair':\n return encoding10\n case '@core/auth':\n return encoding11\n case '@core/head':\n return encoding12\n case '@core/hints':\n return encoding13\n case '@core/session':\n return encoding14\n case '@core/sessions':\n return encoding15\n case '@core/dependency':\n return encoding16\n default:\n throw new Error('Encoder not found ' + name)\n }\n}\n\nfunction getStruct(name, v = VERSION) {\n const enc = getEncoding(name)\n return {\n preencode(state, m) {\n version = v\n enc.preencode(state, m)\n },\n encode(state, m) {\n version = v\n enc.encode(state, m)\n },\n decode(state) {\n version = v\n return enc.decode(state)\n }\n }\n}\n\nconst resolveStruct = getStruct // compat\n\nmodule.exports = {\n resolveStruct,\n getStruct,\n getEnum,\n getEncoding,\n encode,\n decode,\n setVersion,\n version\n}\nconst { EventEmitter } = require('events')\nconst isOptions = require('is-options')\nconst crypto = require('hypercore-crypto')\nconst CoreStorage = require('hypercore-storage')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst NoiseSecretStream = require('@hyperswarm/secret-stream')\nconst Protomux = require('protomux')\nconst id = require('hypercore-id-encoding')\nconst safetyCatch = require('safety-catch')\nconst unslab = require('unslab')\n\nconst inspect = require('./lib/inspect')\nconst Core = require('./lib/core')\nconst Info = require('./lib/info')\nconst Download = require('./lib/download')\nconst DefaultEncryption = require('./lib/default-encryption')\nconst caps = require('./lib/caps')\nconst Replicator = require('./lib/replicator')\nconst { manifestHash, createManifest } = require('./lib/verifier')\nconst { ReadStream, WriteStream, ByteStream } = require('./lib/streams')\nconst { MerkleTree } = require('./lib/merkle-tree')\nconst {\n ASSERTION,\n BAD_ARGUMENT,\n SESSION_CLOSED,\n SESSION_MOVED,\n SESSION_NOT_WRITABLE,\n SNAPSHOT_NOT_AVAILABLE,\n DECODING_ERROR\n} = require('hypercore-errors')\n\n// Hypercore actually does not have any notion of max/min block sizes\n// but we enforce 15mb to ensure smooth replication (each block is transmitted atomically)\nconst MAX_SUGGESTED_BLOCK_SIZE = 15 * 1024 * 1024\n\nclass Hypercore extends EventEmitter {\n constructor(storage, key, opts) {\n super()\n\n if (isOptions(storage) && !storage.db) {\n opts = storage\n storage = null\n key = opts.key || null\n } else if (isOptions(key)) {\n opts = key\n key = opts.key || null\n }\n\n if (key && typeof key === 'string') key = id.decode(key)\n if (!opts) opts = {}\n\n if (!storage) storage = opts.storage\n\n this.core = null\n this.state = null\n this.encryption = null\n this.extensions = new Map()\n\n this.valueEncoding = null\n this.encodeBatch = null\n this.activeRequests = []\n this.sessions = null\n this.ongc = null\n\n this.keyPair = opts.keyPair || null\n this.readable = true\n this.writable = false\n this.exclusive = false\n this.opened = false\n this.closed = false\n this.weak = !!opts.weak\n this.snapshotted = !!opts.snapshot\n this.onseq = opts.onseq || null\n this.onwait = opts.onwait || null\n this.wait = opts.wait !== false\n this.timeout = opts.timeout || 0\n this.preload = null\n this.closing = null\n this.opening = null\n\n this._readonly = opts.writable === false\n this._preappend = preappend.bind(this)\n this._snapshot = null\n this._findingPeers = 0\n this._active = opts.weak ? !!opts.active : opts.active !== false\n\n this._sessionIndex = -1\n this._stateIndex = -1 // maintained by session state\n this._monitorIndex = -1 // maintained by replication state\n\n this.opening = this._open(storage, key, opts)\n this.opening.catch(safetyCatch)\n\n this.on('newListener', maybeAddMonitor)\n }\n\n [Symbol.for('nodejs.util.inspect.custom')](depth, opts) {\n return inspect(this, depth, opts)\n }\n\n static MAX_SUGGESTED_BLOCK_SIZE = MAX_SUGGESTED_BLOCK_SIZE\n\n static DefaultEncryption = DefaultEncryption\n\n static key(manifest, { compat, version, namespace } = {}) {\n if (b4a.isBuffer(manifest)) {\n manifest = { version, signers: [{ publicKey: manifest, namespace }] }\n }\n return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest))\n }\n\n static discoveryKey(key) {\n return crypto.discoveryKey(key)\n }\n\n static blockEncryptionKey(key, encryptionKey) {\n return DefaultEncryption.blockEncryptionKey(key, encryptionKey)\n }\n\n static getProtocolMuxer(stream) {\n return stream.noiseStream.userData\n }\n\n static createCore(storage, opts) {\n return new Core(Hypercore.defaultStorage(storage), { autoClose: false, ...opts })\n }\n\n static createProtocolStream(isInitiator, opts = {}) {\n let outerStream = Protomux.isProtomux(isInitiator)\n ? isInitiator.stream\n : isStream(isInitiator)\n ? isInitiator\n : opts.stream\n\n let noiseStream = null\n\n if (outerStream) {\n noiseStream = outerStream.noiseStream\n } else {\n noiseStream = new NoiseSecretStream(isInitiator, null, opts)\n outerStream = noiseStream.rawStream\n }\n if (!noiseStream) throw BAD_ARGUMENT('Invalid stream', this.discoveryKey)\n\n if (!noiseStream.userData) {\n const protocol = Protomux.from(noiseStream)\n\n if (opts.keepAlive !== false && noiseStream.keepAlive === 0) {\n noiseStream.setKeepAlive(5000)\n }\n noiseStream.userData = protocol\n }\n\n if (opts.ondiscoverykey) {\n noiseStream.userData.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey)\n }\n\n return outerStream\n }\n\n static defaultStorage(storage, opts = {}) {\n if (CoreStorage.isCoreStorage(storage)) return storage\n\n const directory = storage\n return new CoreStorage(directory, opts)\n }\n\n static clearRequests(session, err) {\n return Replicator.clearRequests(session, err)\n }\n\n snapshot(opts) {\n return this.session({ ...opts, snapshot: true })\n }\n\n compact() {\n return this.core.compact()\n }\n\n session(opts = {}) {\n if (this.closing) {\n // This makes the closing logic a lot easier. If this turns out to be a problem\n // in practice, open an issue and we'll try to make a solution for it.\n throw SESSION_CLOSED('Cannot make sessions on a closing core', this.discoveryKey)\n }\n if (opts.checkout !== undefined && !opts.name && !opts.atom) {\n throw ASSERTION('Checkouts are only supported on atoms or named sessions', this.discoveryKey)\n }\n\n const wait = opts.wait === false ? false : this.wait\n const writable = opts.writable === undefined ? !this._readonly : opts.writable === true\n const onwait = opts.onwait === undefined ? this.onwait : opts.onwait\n const onseq = opts.onseq === undefined ? this.onseq : opts.onseq\n const timeout = opts.timeout === undefined ? this.timeout : opts.timeout\n const weak = opts.weak === undefined ? this.weak : opts.weak\n const Clz = opts.class || Hypercore\n const s = new Clz(null, this.key, {\n ...opts,\n wait,\n onwait,\n onseq,\n timeout,\n writable,\n weak,\n parent: this\n })\n\n return s\n }\n\n async setEncryptionKey(key, opts) {\n if (!this.opened) await this.opening\n const encryption = this._getEncryptionProvider({ key, block: !!(opts && opts.block) })\n return this.setEncryption(encryption)\n }\n\n async setEncryption(encryption) {\n if (!this.opened) await this.opening\n\n if (encryption === null) {\n this.encryption = encryption\n return\n }\n\n if (!isEncryptionProvider(encryption)) {\n throw ASSERTION('Provider does not satisfy HypercoreEncryption interface', this.discoveryKey)\n }\n\n this.encryption = encryption\n }\n\n setKeyPair(keyPair) {\n this.keyPair = keyPair\n }\n\n setActive(bool) {\n const active = !!bool\n if (active === this._active || this.closing) return\n this._active = active\n if (!this.opened) return\n this.core.replicator.updateActivity(this._active ? 1 : -1)\n }\n\n async _open(storage, key, opts) {\n const preload = opts.preload || (opts.parent && opts.parent.preload)\n\n if (preload) {\n this.sessions = [] // in case someone looks at it like with peers\n this.preload = preload\n opts = { ...opts, ...(await this.preload) }\n this.preload = null\n }\n\n const parent = opts.parent || null\n const core = opts.core || (parent && parent.core)\n const sessions = opts.sessions || (parent && parent.sessions)\n const ongc = opts.ongc || (parent && parent.ongc)\n\n if (core) this.core = core\n if (ongc) this.ongc = ongc\n if (sessions) this.sessions = sessions\n\n if (this.sessions === null) this.sessions = []\n this._sessionIndex = this.sessions.push(this) - 1\n\n if (this.core === null) initOnce(this, storage, key, opts)\n if (this._monitorIndex === -2) this.core.addMonitor(this)\n\n try {\n await this._openSession(opts)\n } catch (err) {\n if (this.core.autoClose && this.core.hasSession() === false) await this.core.close()\n\n if (this.exclusive) this.core.unlockExclusive()\n\n this.core.removeMonitor(this)\n this._removeSession()\n\n if (this.state !== null) this.state.removeSession(this)\n\n this.closed = true\n this.emit('close')\n throw err\n }\n\n this.emit('ready')\n\n // if we are a weak session the core might have closed...\n if (this.core.closing) this.close().catch(safetyCatch)\n }\n\n _removeSession() {\n if (this._sessionIndex === -1) return\n const head = this.sessions.pop()\n if (head !== this) this.sessions[(head._sessionIndex = this._sessionIndex)] = head\n this._sessionIndex = -1\n if (this.ongc !== null) this.ongc(this)\n }\n\n async _openSession(opts) {\n if (this.core.opened === false) await this.core.ready()\n\n if (this.keyPair === null) this.keyPair = opts.keyPair || this.core.header.keyPair\n\n const parent = opts.parent || null\n if (parent && parent.encryption) this.encryption = parent.encryption\n\n const e = getEncryptionOption(opts)\n if (!this.encryption) this.encryption = this._getEncryptionProvider(e)\n\n this.writable = this._isWritable()\n\n if (opts.valueEncoding) {\n this.valueEncoding = c.from(opts.valueEncoding)\n }\n if (opts.encodeBatch) {\n this.encodeBatch = opts.encodeBatch\n }\n\n // one session sets for pushOnly for all\n if (opts.pushOnly === true) {\n this.core.replicator.setPushOnly(true)\n }\n\n if (parent) {\n if (parent._stateIndex === -1) await parent.ready()\n if (!this.keyPair) this.keyPair = parent.keyPair\n\n const ps = parent.state\n\n if (ps) {\n const shouldSnapshot = this.snapshotted && !ps.isSnapshot()\n this.state = shouldSnapshot ? await ps.snapshot() : ps.ref()\n }\n\n if (this.snapshotted && this.core && !this._snapshot) {\n this._updateSnapshot()\n }\n }\n\n if (opts.exclusive && opts.writable !== false) {\n this.exclusive = true\n await this.core.lockExclusive()\n }\n\n const parentState = parent ? parent.state : this.core.state\n const checkout = opts.checkout === undefined ? -1 : opts.checkout\n const state = this.state\n\n if (opts.atom) {\n this.state = await parentState.createSession(null, false, opts.atom)\n if (state) state.unref()\n } else if (opts.name) {\n // todo: need to make named sessions safe before ready\n // atm we always copy the state in passCapabilities\n this.state = await parentState.createSession(opts.name, !!opts.overwrite, null)\n if (state) state.unref() // ref'ed above in setup session\n }\n\n if (this.state && checkout !== -1) {\n if (!opts.name && !opts.atom) {\n throw ASSERTION('Checkouts must be named or atomized', this.discoveryKey)\n }\n if (checkout > this.state.length) {\n throw ASSERTION(\n `Invalid checkout ${checkout} for ${opts.name}, length is ${this.state.length}`,\n this.discoveryKey\n )\n }\n if (this.state.prologue && checkout < this.state.prologue.length) {\n throw ASSERTION(\n `Invalid checkout ${checkout} for ${opts.name}, prologue length is ${this.state.prologue.length}`,\n this.discoveryKey\n )\n }\n if (checkout < this.state.length) await this.state.truncate(checkout, this.fork)\n }\n\n if (this.state === null) {\n this.state = this.core.state.ref()\n }\n\n this.writable = this._isWritable()\n\n if (this.snapshotted && this.core) this._updateSnapshot()\n\n this.state.addSession(this)\n // TODO: we need to rework the core reference flow, as the state and session do not always agree now due to moveTo\n this.core = this.state.core // in case it was wrong...\n\n if (opts.userData) {\n const tx = this.state.storage.write()\n for (const [key, value] of Object.entries(opts.userData)) {\n tx.putUserData(key, value)\n }\n await tx.flush()\n }\n\n if (opts.manifest && !this.core.header.manifest) {\n await this.core.setManifest(createManifest(opts.manifest))\n }\n\n this.core.replicator.updateActivity(this._active ? 1 : 0)\n\n this.opened = true\n }\n\n get replicator() {\n return this.core === null ? null : this.core.replicator\n }\n\n _getSnapshot() {\n return {\n length: this.state.length,\n byteLength: this.state.byteLength,\n fork: this.state.fork\n }\n }\n\n _updateSnapshot() {\n const prev = this._snapshot\n const next = (this._snapshot = this._getSnapshot())\n\n if (!prev) return true\n return prev.length !== next.length || prev.fork !== next.fork\n }\n\n _isWritable() {\n if (this._readonly) return false\n if (this.state && !this.state.isDefault()) return true\n return !!(this.keyPair && this.keyPair.secretKey)\n }\n\n close({ error } = {}) {\n if (this.closing) return this.closing\n\n this.closing = this._close(error || null)\n return this.closing\n }\n\n clearRequests(activeRequests, error) {\n if (!activeRequests.length) return\n if (this.core) this.core.replicator.clearRequests(activeRequests, error)\n }\n\n async _close(error) {\n if (this.opened === false) {\n try {\n await this.opening\n } catch (err) {\n if (!this.closed) throw err\n }\n }\n\n if (this.closed === true) return\n\n this.core.removeMonitor(this)\n this.state.removeSession(this)\n this._removeSession()\n\n this.readable = false\n this.writable = false\n this.opened = false\n\n const gc = []\n for (const ext of this.extensions.values()) {\n if (ext.session === this) gc.push(ext)\n }\n for (const ext of gc) ext.destroy()\n\n this.core.replicator.findingPeers -= this._findingPeers\n this.core.replicator.clearRequests(this.activeRequests, error)\n this.core.replicator.updateActivity(this._active ? -1 : 0)\n\n this._findingPeers = 0\n\n this.state.unref()\n\n if (this.exclusive) this.core.unlockExclusive()\n\n if (this.core.hasSession()) {\n // emit \"fake\" close as this is a session\n this.closed = true\n this.emit('close')\n return\n }\n\n if (this.core.autoClose) await this.core.close()\n\n this.closed = true\n this.emit('close')\n }\n\n async commit(session, opts) {\n await this.ready()\n await session.ready()\n\n return this.state.commit(session.state, { keyPair: this.keyPair, ...opts })\n }\n\n replicate(isInitiator, opts = {}) {\n // Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,\n // because it doesn't really make a lot of sense.\n if (Protomux.isProtomux(isInitiator)) return this._attachToMuxer(isInitiator)\n\n // if same stream is passed twice, ignore the 2nd one before we make sessions etc\n if (isStream(isInitiator) && this._isAttached(isInitiator)) return isInitiator\n\n const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)\n const noiseStream = protocolStream.noiseStream\n const protocol = noiseStream.userData\n\n this._attachToMuxer(protocol)\n\n return protocolStream\n }\n\n _isAttached(stream) {\n return (\n stream.userData &&\n this.core &&\n this.core.replicator &&\n this.core.replicator.attached(stream.userData)\n )\n }\n\n _attachToMuxer(mux) {\n if (this.opened) {\n this.core.replicator.attachTo(mux)\n } else {\n this.opening.then(() => this.core.replicator.attachTo(mux), mux.destroy.bind(mux))\n }\n\n return mux\n }\n\n get id() {\n return this.core === null ? null : this.core.id\n }\n\n get key() {\n return this.core === null ? null : this.core.key\n }\n\n get discoveryKey() {\n return this.core === null ? null : this.core.discoveryKey\n }\n\n get manifest() {\n return this.core === null ? null : this.core.manifest\n }\n\n get length() {\n if (this._snapshot) return this._snapshot.length\n return this.opened === false ? 0 : this.state.length\n }\n\n get signedLength() {\n return this.opened === false ? 0 : this.state.signedLength()\n }\n\n /**\n * Deprecated. Use `const { byteLength } = await core.info()`.\n */\n get byteLength() {\n if (this.opened === false) return 0\n if (this._snapshot) return this._snapshot.byteLength\n return this.state.byteLength - this.state.length * this.padding\n }\n\n get remoteContiguousLength() {\n if (this.opened === false) return 0\n return Math.min(this.core.state.length, this.core.header.hints.remoteContiguousLength)\n }\n\n get contiguousLength() {\n if (this.opened === false) return 0\n return Math.min(this.core.state.length, this.core.header.hints.contiguousLength)\n }\n\n get contiguousByteLength() {\n return 0\n }\n\n get fork() {\n if (this.opened === false) return 0\n return this.state.fork\n }\n\n get padding() {\n if (this.encryption && this.key && this.manifest) {\n return this.encryption.padding(this.core, this.length)\n }\n\n return 0\n }\n\n get peers() {\n return this.opened === false ? [] : this.core.replicator.peers\n }\n\n get globalCache() {\n return this.opened === false ? null : this.core.globalCache\n }\n\n ready() {\n return this.opening\n }\n\n async setUserData(key, value) {\n if (this.opened === false) await this.opening\n const existing = await this.getUserData(key)\n if (existing && b4a.isBuffer(value) && b4a.equals(existing, value)) return\n await this.state.setUserData(key, value)\n }\n\n async getUserData(key) {\n if (this.opened === false) await this.opening\n const batch = this.state.storage.read()\n const p = batch.getUserData(key)\n batch.tryFlush()\n return p\n }\n\n transferSession(core) {\n // todo: validate we can move\n\n if (this.weak === false) {\n this.core.activeSessions--\n core.activeSessions++\n }\n\n if (this._monitorIndex >= 0) {\n this.core.removeMonitor(this)\n core.addMonitor(this)\n }\n\n const old = this.core\n\n this.core = core\n\n old.replicator.clearRequests(this.activeRequests, SESSION_MOVED())\n\n this.emit('migrate', this.key)\n }\n\n findingPeers() {\n this._findingPeers++\n if (this.core !== null && !this.closing) this.core.replicator.findingPeers++\n\n let once = true\n\n return () => {\n if (this.closing || !once) return\n once = false\n this._findingPeers--\n if (this.core !== null && --this.core.replicator.findingPeers === 0) {\n this.core.replicator.updateAll()\n }\n }\n }\n\n async info(opts) {\n if (this.opened === false) await this.opening\n\n return Info.from(this, opts)\n }\n\n async update(opts) {\n if (this.opened === false) await this.opening\n if (this.closing !== null) return false\n if (this.snapshotted) return false\n\n if (this.writable && (!opts || opts.force !== true)) return false\n\n const remoteWait = this._shouldWait(opts, this.core.replicator.findingPeers > 0)\n\n let upgraded = false\n\n if (await this.core.replicator.applyPendingReorg()) {\n upgraded = true\n }\n\n if (!upgraded && remoteWait) {\n const activeRequests = (opts && opts.activeRequests) || this.activeRequests\n const req = this.core.replicator.addUpgrade(activeRequests)\n\n try {\n upgraded = await req.promise\n } catch (err) {\n if (isSessionMoved(err)) return this.update(opts)\n throw err\n }\n }\n\n if (!upgraded) return false\n return true\n }\n\n async seek(bytes, opts) {\n if (this.opened === false) await this.opening\n if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid', this.discoveryKey)\n\n const activeRequests = (opts && opts.activeRequests) || this.activeRequests\n\n if (this.encryption && !this.core.manifest) {\n const req = this.replicator.addUpgrade(activeRequests)\n try {\n await req.promise\n } catch (err) {\n if (isSessionMoved(err)) return this.seek(bytes, opts)\n throw err\n }\n }\n\n const s = MerkleTree.seek(this.state, bytes, this.padding)\n\n const offset = await s.update()\n if (offset) return offset\n\n if (this.closing !== null) {\n throw SESSION_CLOSED('cannot seek on a closed session', this.discoveryKey)\n }\n\n if (!this._shouldWait(opts, this.wait)) return null\n\n const req = this.core.replicator.addSeek(activeRequests, s)\n\n const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout\n if (timeout) req.context.setTimeout(req, timeout)\n\n try {\n return await req.promise\n } catch (err) {\n if (isSessionMoved(err)) return this.seek(bytes, opts)\n throw err\n }\n }\n\n async has(start, end = start + 1) {\n if (this.opened === false) await this.opening\n if (!isValidIndex(start) || !isValidIndex(end)) {\n throw ASSERTION('has range is invalid', this.discoveryKey)\n }\n\n if (this.state.isDefault()) {\n if (end === start + 1) return this.core.bitfield.get(start)\n\n const i = this.core.bitfield.firstUnset(start)\n return i === -1 || i >= end\n }\n\n if (end === start + 1) {\n const rx = this.state.storage.read()\n const block = rx.getBlock(start)\n rx.tryFlush()\n\n return (await block) !== null\n }\n\n let count = 0\n\n const stream = this.state.storage.createBlockStream({ gte: start, lt: end })\n for await (const block of stream) {\n if (block === null) return false\n count++\n }\n\n return count === end - start\n }\n\n async get(index, opts) {\n if (this.opened === false) await this.opening\n if (!isValidIndex(index)) throw ASSERTION('block index is invalid', this.discoveryKey)\n\n if (this.closing !== null) {\n throw SESSION_CLOSED('cannot get on a closed session', this.discoveryKey)\n }\n\n const encoding =\n (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding\n\n if (this.onseq !== null) this.onseq(index, this)\n\n const req = this._get(index, opts)\n\n let block = await req\n if (!block) return null\n\n if (opts && opts.raw) return block\n\n if (this.encryption && (!opts || opts.decrypt !== false)) {\n // Copy the block as it might be shared with other sessions.\n block = b4a.from(block)\n\n await this.encryption.decrypt(index, block, this.core)\n }\n\n return this._decode(encoding, block, index)\n }\n\n async clear(start, end = start + 1, opts) {\n if (this.opened === false) await this.opening\n if (this.closing !== null) {\n throw SESSION_CLOSED('cannot clear on a closed session', this.discoveryKey)\n }\n\n if (typeof end === 'object') {\n opts = end\n end = start + 1\n }\n\n if (!isValidIndex(start) || !isValidIndex(end)) {\n throw ASSERTION('clear range is invalid', this.discoveryKey)\n }\n\n const cleared = opts && opts.diff ? { blocks: 0 } : null\n\n if (start >= end) return cleared\n if (start >= this.length) return cleared\n\n await this.state.clear(start, end, cleared)\n\n return cleared\n }\n\n async purge() {\n await this._closeAllSessions(null)\n await this.core.purge()\n }\n\n async _get(index, opts) {\n const block = await readBlock(this.state.storage.read(), index)\n\n if (block !== null) return block\n\n if (this.closing !== null) {\n throw SESSION_CLOSED('cannot get on a closed session', this.discoveryKey)\n }\n\n // snapshot should check if core has block\n if (this._snapshot !== null) {\n checkSnapshot(this, index)\n const coreBlock = await readBlock(this.core.state.storage.read(), index)\n\n checkSnapshot(this, index)\n if (coreBlock !== null) return coreBlock\n }\n\n // lets check the bitfield to see if we got it during the above async calls\n // this is the last resort before replication, so always safe.\n if (this.core.bitfield.get(index)) {\n const coreBlock = await readBlock(this.state.storage.read(), index)\n // TODO: this should not be needed, only needed atm in case we are doing a moveTo during this (we should fix)\n if (coreBlock !== null) return coreBlock\n }\n\n if (!this._shouldWait(opts, this.wait)) return null\n\n if (opts && opts.onwait) opts.onwait(index, this)\n if (this.onwait) this.onwait(index, this)\n\n const activeRequests = (opts && opts.activeRequests) || this.activeRequests\n\n const force = opts ? opts.force === true : false\n const req = this.core.replicator.addBlock(activeRequests, index, force)\n req.snapshot = index < this.length\n\n const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout\n if (timeout) req.context.setTimeout(req, timeout)\n\n let replicatedBlock = null\n\n try {\n replicatedBlock = await req.promise\n } catch (err) {\n if (isSessionMoved(err)) return this._get(index, opts)\n throw err\n }\n\n if (this._snapshot !== null) checkSnapshot(this, index)\n return maybeUnslab(replicatedBlock)\n }\n\n _shouldWait(opts, defaultValue) {\n if (opts) {\n if (opts.wait === false) return false\n if (opts.wait === true) return true\n }\n return defaultValue\n }\n\n createReadStream(opts) {\n return new ReadStream(this, opts)\n }\n\n createWriteStream() {\n return new WriteStream(this)\n }\n\n createByteStream(opts) {\n return new ByteStream(this, opts)\n }\n\n download(range) {\n return new Download(this, range)\n }\n\n // TODO: get rid of this / deprecate it?\n undownload(range) {\n range.destroy(null)\n }\n\n // TODO: get rid of this / deprecate it?\n cancel(request) {\n // Do nothing for now\n }\n\n async truncate(newLength = 0, opts = {}) {\n if (this.opened === false) await this.opening\n\n const {\n fork = this.state.fork + 1,\n keyPair = this.keyPair,\n signature = null\n } = typeof opts === 'number' ? { fork: opts } : opts\n\n const isDefault = this.state === this.core.state\n const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))\n if (isDefault && writable === false && (newLength > 0 || fork !== this.state.fork)) {\n throw SESSION_NOT_WRITABLE('cannot append to a non-writable core', this.discoveryKey)\n }\n\n await this.state.truncate(newLength, fork, { keyPair, signature })\n\n // TODO: Should propagate from an event triggered by the oplog\n if (this.state === this.core.state) this.core.replicator.updateAll()\n }\n\n async append(blocks, opts = {}) {\n if (this.opened === false) await this.opening\n\n const isDefault = this.state === this.core.state\n const defaultKeyPair = this.state.name === null ? this.keyPair : null\n\n const { keyPair = defaultKeyPair, signature = null, maxLength, postappend = null } = opts\n const writable =\n !isDefault || !!signature || !!(keyPair && keyPair.secretKey) || opts.writable === true\n\n if (this._readonly || writable === false) {\n throw SESSION_NOT_WRITABLE('cannot append to a readonly core', this.discoveryKey)\n }\n\n blocks = Array.isArray(blocks) ? blocks : [blocks]\n\n const preappend = this.encryption && this._preappend\n\n const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length)\n\n if (this.encodeBatch === null) {\n for (let i = 0; i < blocks.length; i++) {\n buffers[i] = this._encode(this.valueEncoding, blocks[i])\n }\n }\n for (const b of buffers) {\n if (b.byteLength > MAX_SUGGESTED_BLOCK_SIZE) {\n throw BAD_ARGUMENT(\n 'Appended block exceeds the maximum suggested block size',\n this.discoveryKey\n )\n }\n }\n\n return this.state.append(buffers, { keyPair, signature, preappend, postappend, maxLength })\n }\n\n async signable(length = -1, fork = -1) {\n if (this.opened === false) await this.opening\n if (length === -1) length = this.length\n if (fork === -1) fork = this.fork\n\n return caps.treeSignable(this.key, await this.treeHash(length), length, fork)\n }\n\n async treeHash(length = -1) {\n if (this.opened === false) await this.opening\n if (length === -1) length = this.length\n if (length > 0 && !(await this.has(length - 1))) await this.get(length - 1)\n\n const roots = await MerkleTree.getRoots(this.state, length)\n return crypto.tree(roots)\n }\n\n async missingNodes(index) {\n if (this.opened === false) await this.opening\n return await MerkleTree.missingNodes(this.core.state, 2 * index, this.core.state.length)\n }\n\n async proof(opts) {\n if (this.opened === false) await this.opening\n const rx = this.state.storage.read()\n const proofPromise = MerkleTree.proof(this.state, rx, opts)\n const blockPromise = opts && opts.block ? rx.getBlock(opts.block.index) : null\n rx.tryFlush()\n const [proof, block] = await Promise.all([proofPromise, blockPromise])\n const settled = await proof.settle()\n if (block) settled.block.value = block\n return settled\n }\n\n async applyProof(proof, from) {\n if (this.opened === false) await this.opening\n return this.core.verify(proof, from)\n }\n\n async verifyFullyRemote(proof) {\n if (this.opened === false) await this.opening\n const batch = await MerkleTree.verifyFullyRemote(this.state, proof)\n await this.core._verifyBatchUpgrade(batch, proof.manifest)\n return batch\n }\n\n registerExtension(name, handlers = {}) {\n if (this.extensions.has(name)) {\n const ext = this.extensions.get(name)\n ext.handlers = handlers\n ext.encoding = c.from(handlers.encoding || c.buffer)\n ext.session = this\n return ext\n }\n\n const ext = {\n name,\n handlers,\n encoding: c.from(handlers.encoding || c.buffer),\n session: this,\n send(message, peer) {\n const buffer = c.encode(this.encoding, message)\n peer.extension(name, buffer)\n },\n broadcast(message) {\n const buffer = c.encode(this.encoding, message)\n for (const peer of this.session.peers) {\n peer.extension(name, buffer)\n }\n },\n destroy() {\n for (const peer of this.session.peers) {\n if (peer.extensions.get(name) === ext) peer.extensions.delete(name)\n }\n this.session.extensions.delete(name)\n },\n _onmessage(state, peer) {\n const m = this.encoding.decode(state)\n if (this.handlers.onmessage) this.handlers.onmessage(m, peer)\n }\n }\n\n this.extensions.set(name, ext)\n\n if (this.core === null) this._monitorIndex = -2\n else this.core.addMonitor(this)\n\n for (const peer of this.peers) {\n peer.extensions.set(name, ext)\n }\n\n return ext\n }\n\n _encode(enc, val) {\n const state = { start: this.padding, end: this.padding, buffer: null }\n\n if (b4a.isBuffer(val)) {\n if (state.start === 0) return val\n state.end += val.byteLength\n } else if (enc) {\n enc.preencode(state, val)\n } else {\n val = b4a.from(val)\n if (state.start === 0) return val\n state.end += val.byteLength\n }\n\n state.buffer = b4a.allocUnsafe(state.end)\n\n if (enc) enc.encode(state, val)\n else state.buffer.set(val, state.start)\n\n return state.buffer\n }\n\n _decode(enc, block, index) {\n if (this.encryption) block = block.subarray(this.encryption.padding(this.core, index))\n try {\n if (enc) return c.decode(enc, block)\n } catch (err) {\n throw DECODING_ERROR(err.message, this.discoveryKey)\n }\n return block\n }\n\n _getEncryptionProvider(e) {\n if (isEncryptionProvider(e)) return e\n if (!e || !e.key) return null\n return new DefaultEncryption(e.key, this.key, { block: e.block, compat: this.core.compat })\n }\n}\n\nmodule.exports = Hypercore\n\nfunction isStream(s) {\n return typeof s === 'object' && s && typeof s.pipe === 'function'\n}\n\nasync function preappend(blocks) {\n const offset = this.state.length\n const fork = this.state.encryptionFork\n\n for (let i = 0; i < blocks.length; i++) {\n await this.encryption.encrypt(offset + i, blocks[i], fork, this.core)\n }\n}\n\nfunction isValidIndex(index) {\n return index === 0 || index > 0\n}\n\nfunction maybeUnslab(block) {\n // Unslab only when it takes up less then half the slab\n return block !== null && 2 * block.byteLength < block.buffer.byteLength ? unslab(block) : block\n}\n\nfunction checkSnapshot(snapshot, index) {\n if (index >= snapshot.state.snapshotCompatLength) {\n throw SNAPSHOT_NOT_AVAILABLE(\n `snapshot at index ${index} not available (max compat length ${snapshot.state.snapshotCompatLength})`,\n snapshot.discoveryKey\n )\n }\n}\n\nfunction readBlock(rx, index) {\n const promise = rx.getBlock(index)\n rx.tryFlush()\n return promise\n}\n\nfunction initOnce(session, storage, key, opts) {\n if (storage === null) storage = opts.storage || null\n if (key === null) key = opts.key || null\n\n session.core = new Core(Hypercore.defaultStorage(storage), {\n preopen: opts.preopen,\n eagerUpgrade: opts.eagerUpgrade !== false,\n notDownloadingLinger: opts.notDownloadingLinger,\n allowFork: opts.allowFork !== false,\n allowPush: !!opts.allowPush,\n pushOnly: !!opts.pushOnly,\n alwaysLatestBlock: !!opts.allowLatestBlock,\n inflightRange: opts.inflightRange,\n compat: opts.compat === true,\n force: opts.force,\n createIfMissing: opts.createIfMissing,\n discoveryKey: opts.discoveryKey,\n overwrite: opts.overwrite,\n key,\n keyPair: opts.keyPair,\n legacy: opts.legacy,\n manifest: opts.manifest,\n globalCache: opts.globalCache || null // session is a temp option, not to be relied on unless you know what you are doing (no semver guarantees)\n })\n}\n\nfunction maybeAddMonitor(name) {\n if (name === 'append' || name === 'truncate') return\n if (this._monitorIndex >= 0 || this.closing) return\n\n if (this.core === null) {\n this._monitorIndex = -2\n } else {\n this.core.addMonitor(this)\n }\n}\n\nfunction isSessionMoved(err) {\n return err.code === 'SESSION_MOVED'\n}\n\nfunction getEncryptionOption(opts) {\n // old style, supported for now but will go away\n if (opts.encryptionKey) return { key: opts.encryptionKey, block: !!opts.isBlockKey }\n if (!opts.encryption) return null\n return b4a.isBuffer(opts.encryption) ? { key: opts.encryption } : opts.encryption\n}\n\nfunction isEncryptionProvider(e) {\n return e && isFunction(e.padding) && isFunction(e.encrypt) && isFunction(e.decrypt)\n}\n\nfunction isFunction(fn) {\n return !!fn && typeof fn === 'function'\n}\nconst crypto = require('hypercore-crypto')\nconst flat = require('flat-tree')\nconst b4a = require('b4a')\nconst { MerkleTree } = require('./merkle-tree')\n\nmodule.exports = async function auditCore(\n core,\n { tree = true, blocks = true, bitfield = true, dryRun = false } = {}\n) {\n const length = core.state.length\n const stats = {\n treeNodes: 0,\n blocks: 0,\n bits: 0,\n droppedTreeNodes: 0,\n droppedBlocks: 0,\n droppedBits: 0,\n corrupt: false\n }\n\n // audit the tree\n if (tree) {\n let tx = null\n\n const roots = await MerkleTree.getRootsFromStorage(core.state.storage, length)\n const stack = []\n\n for (const r of roots) {\n if (r === null) {\n if (!dryRun) {\n const storage = core.state.storage\n await storage.store.deleteCore(storage.core)\n return null\n }\n\n stats.corrupt = true\n }\n\n stack.push(r)\n }\n\n stats.treeNodes += roots.length\n\n while (stack.length > 0) {\n const node = stack.pop()\n\n if ((node.index & 1) === 0) continue\n\n const [left, right] = flat.children(node.index)\n\n const rx = core.state.storage.read()\n const leftNodePromise = rx.getTreeNode(left)\n const rightNodePromise = rx.getTreeNode(right)\n\n rx.tryFlush()\n\n const [leftNode, rightNode] = await Promise.all([leftNodePromise, rightNodePromise])\n\n if (isBadTree(node, leftNode, rightNode)) {\n if (!tx && !stats.corrupt) tx = core.state.storage.write()\n const [l, r] = flat.spans(node.index)\n tx.deleteTreeNodeRange(l, r + 1)\n stats.droppedTreeNodes++\n continue\n }\n\n if (!leftNode) continue\n\n stats.treeNodes += 2\n stack.push(leftNode, rightNode)\n }\n\n if (tx && !dryRun) await tx.flush()\n }\n\n // audit the blocks\n if (blocks) {\n let tx = null\n\n for await (const block of core.state.storage.createBlockStream()) {\n if (!core.bitfield.get(block.index)) {\n if (!tx && !stats.corrupt) tx = core.state.storage.write()\n tx.deleteBlock(block.index)\n stats.droppedBlocks++\n }\n\n const rx = core.state.storage.read()\n const treeNodePromise = rx.getTreeNode(2 * block.index)\n\n rx.tryFlush()\n\n const treeNode = await treeNodePromise\n\n if (isBadBlock(treeNode, block.value)) {\n if (!tx && !stats.corrupt) tx = core.state.storage.write()\n tx.deleteBlock(block.index)\n stats.droppedBlocks++\n continue\n }\n\n stats.blocks++\n }\n\n if (tx && !dryRun) await tx.flush()\n }\n\n if (bitfield) {\n let tx = null\n\n for (const index of allBits(core.bitfield)) {\n const rx = core.state.storage.read()\n const blockPromise = rx.getBlock(index)\n\n rx.tryFlush()\n\n const block = await blockPromise\n if (!block) {\n stats.droppedBits++\n if (dryRun) continue\n\n if (!tx && !stats.corrupt) tx = core.state.storage.write()\n\n core.bitfield.set(index, false)\n\n const page = core.bitfield.getBitfield(index)\n if (page.bitfield) tx.putBitfieldPage(page.index, page.bitfield)\n else tx.deleteBitfieldPage(page.index)\n continue\n }\n\n stats.bits++\n }\n\n if (tx && !dryRun) await tx.flush()\n }\n\n return stats\n}\n\nfunction isBadBlock(node, block) {\n if (!node) return true\n const hash = crypto.data(block)\n return !b4a.equals(hash, node.hash) || node.size !== block.byteLength\n}\n\nfunction isBadTree(parent, left, right) {\n if (!left && !right) return false\n if (!left || !right) return true\n const hash = crypto.parent(left, right)\n return !b4a.equals(hash, parent.hash) || parent.size !== left.size + right.size\n}\n\nfunction* allBits(bitfield) {\n let i = 0\n if (bitfield.get(0)) yield 0\n while (true) {\n i = bitfield.findFirst(true, i + 1)\n if (i === -1) break\n yield i\n }\n}\nconst b4a = require('b4a')\nconst quickbit = require('./compat').quickbit\n\nmodule.exports = class BitInterlude {\n constructor() {\n this.ranges = []\n }\n\n contiguousLength(from) {\n for (const r of this.ranges) {\n if (r.start > from) break\n if (!r.value && r.start <= from) return r.start\n }\n\n // TODO: be smarter\n while (this.get(from) === true) from++\n return from\n }\n\n get(index) {\n let start = 0\n let end = this.ranges.length\n\n while (start < end) {\n const mid = (start + end) >> 1\n const r = this.ranges[mid]\n\n if (index < r.start) {\n end = mid\n continue\n }\n\n if (index >= r.end) {\n if (mid === start) break\n start = mid\n continue\n }\n\n return r.value\n }\n\n return false\n }\n\n setRange(start, end, value) {\n if (start === end) return\n\n let r = null\n\n for (let i = 0; i < this.ranges.length; i++) {\n r = this.ranges[i]\n\n // if already inside, stop\n if (r.start <= start && end <= r.end) {\n if (value === r.value) return\n\n const ranges = mergeRanges(r, { start, end, value })\n this.ranges.splice(i, 1, ...ranges)\n\n return\n }\n\n // we wanna overun the interval\n if (start > r.end) {\n continue\n }\n\n // we overran but this interval is ending after us, move it back\n if (end >= r.start && end <= r.end) {\n r.start = r.value === value ? start : end\n if (r.value !== value) this.ranges.splice(i, 0, { start, end, value })\n return\n }\n\n // we overran but our start is contained in this interval, move start back\n if (start >= r.start && start <= r.end) {\n if (r.value !== value) {\n r.end = start // Crop current comparison\n\n // If there's a next range\n if (this.ranges.length - 1 > i) {\n const next = this.ranges[i + 1]\n // If the same, go next\n if (next.value === value) continue\n // Else we overlap so update next range\n if (next.start < end) next.start = end\n }\n\n this.ranges.splice(++i, 0, { start, end, value })\n return\n }\n\n start = r.start\n }\n\n let remove = 0\n\n for (let j = i; j < this.ranges.length; j++) {\n const n = this.ranges[j]\n if (n.start > end || n.value !== value) break\n if (n.start <= end && n.end > end) end = n.end\n remove++\n }\n\n this.ranges.splice(i, remove, { start, end, value })\n return\n }\n\n if (r !== null) {\n if (start <= r.end && end > r.end) {\n r.end = end\n return\n }\n\n // we never\n if (r.end > start) return\n }\n\n this.ranges.push({ start, end, value })\n }\n\n flush(tx, bitfield) {\n if (!this.ranges.length) return []\n\n let index = this.ranges[0].start\n const final = this.ranges[this.ranges.length - 1].end\n\n let i = 0\n\n while (index < final) {\n const page = bitfield.getBitfield(index) // read only\n const pageIndex = page ? page.index : bitfield.getPageIndex(index)\n\n const buf = b4a.allocUnsafe(bitfield.getPageByteLength())\n\n if (page) {\n const src = page.bitfield // Uint32Array\n buf.set(b4a.from(src.buffer, src.byteOffset, src.byteLength), 0)\n } else {\n b4a.fill(buf, 0)\n }\n\n const last = (pageIndex + 1) * (buf.byteLength << 3)\n const offset = pageIndex * (buf.byteLength << 3)\n\n let hasValue = false\n\n while (i < this.ranges.length) {\n const { start, end, value } = this.ranges[i]\n\n if (!hasValue && value) hasValue = true\n\n const from = start < index ? index : start\n const to = end < last ? end : last\n\n quickbit.fill(buf, value, from - offset, to - offset)\n\n index = to\n\n if (to === last) break\n\n i++\n }\n\n if (page || hasValue) tx.putBitfieldPage(pageIndex, buf)\n }\n\n return this.ranges\n }\n}\n\nfunction mergeRanges(a, b) {\n const ranges = []\n if (a.start < b.start) ranges.push({ start: a.start, end: b.start, value: a.value })\n ranges.push({ start: b.start, end: b.end, value: b.value })\n if (b.end < a.end) ranges.push({ start: b.end, end: a.end, value: a.value })\n\n return ranges\n}\nconst BigSparseArray = require('big-sparse-array')\nconst b4a = require('b4a')\nconst quickbit = require('./compat').quickbit\n\nconst BITS_PER_PAGE = 32768\nconst BYTES_PER_PAGE = BITS_PER_PAGE / 8\nconst WORDS_PER_PAGE = BYTES_PER_PAGE / 4\nconst BITS_PER_SEGMENT = 2097152\nconst BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8\nconst WORDS_PER_SEGMENT = BYTES_PER_SEGMENT / 4\nconst INITIAL_WORDS_PER_SEGMENT = 1024\nconst PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE\nconst SEGMENT_GROWTH_FACTOR = 4\n\nclass BitfieldPage {\n constructor(index, segment) {\n this.index = index\n this.offset = index * BYTES_PER_PAGE - segment.offset\n this.bitfield = null\n this.segment = segment\n\n segment.add(this)\n }\n\n get tree() {\n return this.segment.tree\n }\n\n get(index, dirty) {\n return quickbit.get(this.bitfield, index)\n }\n\n set(index, val) {\n if (quickbit.set(this.bitfield, index, val)) {\n this.tree.update(this.offset * 8 + index)\n }\n }\n\n setRange(start, end, val) {\n quickbit.fill(this.bitfield, val, start, end)\n\n let i = Math.floor(start / 128)\n const n = i + Math.ceil((end - start) / 128)\n\n while (i <= n) this.tree.update(this.offset * 8 + i++ * 128)\n }\n\n findFirst(val, position) {\n return quickbit.findFirst(this.bitfield, val, position)\n }\n\n findLast(val, position) {\n return quickbit.findLast(this.bitfield, val, position)\n }\n\n count(start, length, val) {\n const end = start + length\n\n let i = start\n let c = 0\n\n while (length > 0) {\n const l = this.findFirst(val, i)\n if (l === -1 || l >= end) return c\n\n const h = this.findFirst(!val, l + 1)\n if (h === -1 || h >= end) return c + end - l\n\n c += h - l\n length -= h - i\n i = h\n }\n\n return c\n }\n}\n\nclass BitfieldSegment {\n constructor(index, bitfield) {\n this.index = index\n this.offset = index * BYTES_PER_SEGMENT\n this.tree = quickbit.Index.from(bitfield, BYTES_PER_SEGMENT)\n this.pages = new Array(PAGES_PER_SEGMENT)\n }\n\n get bitfield() {\n return this.tree.field\n }\n\n add(page) {\n const i = page.index - this.index * PAGES_PER_SEGMENT\n this.pages[i] = page\n\n const start = i * WORDS_PER_PAGE\n const end = start + WORDS_PER_PAGE\n\n if (end >= this.bitfield.length) this.reallocate(end)\n\n page.bitfield = this.bitfield.subarray(start, end)\n }\n\n reallocate(length) {\n let target = this.bitfield.length\n while (target < length) target *= SEGMENT_GROWTH_FACTOR\n\n const bitfield = new Uint32Array(target)\n bitfield.set(this.bitfield)\n\n this.tree = quickbit.Index.from(bitfield, BYTES_PER_SEGMENT)\n\n for (let i = 0; i < this.pages.length; i++) {\n const page = this.pages[i]\n if (!page) continue\n\n const start = i * WORDS_PER_PAGE\n const end = start + WORDS_PER_PAGE\n\n page.bitfield = bitfield.subarray(start, end)\n }\n }\n\n findFirst(val, position) {\n position = this.tree.skipFirst(!val, position)\n\n let j = position & (BITS_PER_PAGE - 1)\n let i = (position - j) / BITS_PER_PAGE\n\n if (i >= PAGES_PER_SEGMENT) return -1\n\n while (i < this.pages.length) {\n const p = this.pages[i]\n\n let index = -1\n\n if (p) index = p.findFirst(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_PAGE + index\n\n j = 0\n i++\n }\n\n return -1\n }\n\n findLast(val, position) {\n position = this.tree.skipLast(!val, position)\n\n let j = position & (BITS_PER_PAGE - 1)\n let i = (position - j) / BITS_PER_PAGE\n\n if (i >= PAGES_PER_SEGMENT) return -1\n\n while (i >= 0) {\n const p = this.pages[i]\n\n let index = -1\n\n if (p) index = p.findLast(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_PAGE + index\n\n j = BITS_PER_PAGE - 1\n i--\n }\n\n return -1\n }\n}\n\nmodule.exports = class Bitfield {\n static BITS_PER_PAGE = BITS_PER_PAGE\n static BYTES_PER_PAGE = BYTES_PER_PAGE\n\n constructor(buffer) {\n this.resumed = !!(buffer && buffer.byteLength >= 0)\n\n this._pages = new BigSparseArray()\n this._segments = new BigSparseArray()\n\n const view = this.resumed\n ? new Uint32Array(buffer.buffer, buffer.byteOffset, Math.floor(buffer.byteLength / 4))\n : new Uint32Array(INITIAL_WORDS_PER_SEGMENT)\n\n for (let i = 0; i < view.length; i += WORDS_PER_SEGMENT) {\n let bitfield = view.subarray(i, i + WORDS_PER_SEGMENT)\n let length = WORDS_PER_SEGMENT\n\n if (i === 0) {\n length = INITIAL_WORDS_PER_SEGMENT\n while (length < bitfield.length) length *= SEGMENT_GROWTH_FACTOR\n }\n\n if (bitfield.length !== length) {\n const copy = new Uint32Array(length)\n copy.set(bitfield, 0)\n bitfield = copy\n }\n\n const segment = new BitfieldSegment(i / WORDS_PER_SEGMENT, bitfield)\n this._segments.set(segment.index, segment)\n\n for (let j = 0; j < bitfield.length; j += WORDS_PER_PAGE) {\n const page = new BitfieldPage((i + j) / WORDS_PER_PAGE, segment)\n this._pages.set(page.index, page)\n }\n }\n }\n\n static from(bitfield) {\n return new Bitfield(bitfield.toBuffer(bitfield._pages.maxLength * BITS_PER_PAGE))\n }\n\n toBuffer(length) {\n const pages = Math.ceil(length / BITS_PER_PAGE)\n const buffer = b4a.allocUnsafe(pages * BYTES_PER_PAGE)\n\n for (let i = 0; i < pages; i++) {\n const page = this._pages.get(i)\n const offset = i * BYTES_PER_PAGE\n\n if (page) {\n const buf = b4a.from(\n page.bitfield.buffer,\n page.bitfield.byteOffset,\n page.bitfield.byteLength\n )\n\n buffer.set(buf, offset)\n } else {\n buffer.fill(0, offset, offset + BYTES_PER_PAGE)\n }\n }\n\n return buffer\n }\n\n getBitfield(index) {\n const i = this.getPageIndex(index)\n\n const p = this._pages.get(i)\n return p || null\n }\n\n merge(bitfield, length) {\n let i = 0\n\n while (i < length) {\n const start = bitfield.firstSet(i)\n if (start === -1) break\n\n i = bitfield.firstUnset(start)\n\n if (i === -1 || i > length) i = length\n\n this.setRange(start, i, true)\n\n if (i >= length) break\n }\n }\n\n get(index) {\n const j = index & (BITS_PER_PAGE - 1)\n const i = (index - j) / BITS_PER_PAGE\n\n const p = this._pages.get(i)\n\n return p ? p.get(j) : false\n }\n\n getPageByteLength() {\n return BYTES_PER_PAGE\n }\n\n getPageIndex(index) {\n const j = index & (BITS_PER_PAGE - 1)\n return (index - j) / BITS_PER_PAGE\n }\n\n getPage(index, create) {\n const i = this.getPageIndex(index)\n\n let p = this._pages.get(i)\n\n if (p) return p\n\n if (!create) return null\n\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s =\n this._segments.get(k) ||\n this._segments.set(\n k,\n new BitfieldSegment(\n k,\n new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)\n )\n )\n\n p = this._pages.set(i, new BitfieldPage(i, s))\n\n return p\n }\n\n set(index, val) {\n const j = index & (BITS_PER_PAGE - 1)\n const i = (index - j) / BITS_PER_PAGE\n\n let p = this._pages.get(i)\n\n if (!p && val) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s =\n this._segments.get(k) ||\n this._segments.set(\n k,\n new BitfieldSegment(\n k,\n new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)\n )\n )\n\n p = this._pages.set(i, new BitfieldPage(i, s))\n }\n\n if (p) p.set(j, val)\n }\n\n setRange(start, end, val) {\n let j = start & (BITS_PER_PAGE - 1)\n let i = (start - j) / BITS_PER_PAGE\n\n while (start < end) {\n let p = this._pages.get(i)\n\n if (!p && val) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s =\n this._segments.get(k) ||\n this._segments.set(\n k,\n new BitfieldSegment(\n k,\n new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)\n )\n )\n\n p = this._pages.set(i, new BitfieldPage(i, s))\n }\n\n const offset = i * BITS_PER_PAGE\n const last = Math.min(end - offset, BITS_PER_PAGE)\n const range = last - j\n\n if (p) p.setRange(j, last, val)\n\n j = 0\n i++\n start += range\n }\n }\n\n findFirst(val, position) {\n let j = position & (BITS_PER_SEGMENT - 1)\n let i = (position - j) / BITS_PER_SEGMENT\n\n while (i < this._segments.maxLength) {\n const s = this._segments.get(i)\n\n let index = -1\n\n if (s) index = s.findFirst(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_SEGMENT + index\n\n j = 0\n i++\n }\n\n return val ? -1 : this._segments.maxLength * BITS_PER_SEGMENT\n }\n\n firstSet(position) {\n return this.findFirst(true, position)\n }\n\n firstUnset(position) {\n return this.findFirst(false, position)\n }\n\n findLast(val, position) {\n let j = position & (BITS_PER_SEGMENT - 1)\n let i = (position - j) / BITS_PER_SEGMENT\n\n while (i >= 0) {\n const s = this._segments.get(i)\n\n let index = -1\n\n if (s) index = s.findLast(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_SEGMENT + index\n\n j = BITS_PER_SEGMENT - 1\n i--\n }\n\n return -1\n }\n\n lastSet(position) {\n return this.findLast(true, position)\n }\n\n lastUnset(position) {\n return this.findLast(false, position)\n }\n\n hasUnset(start, length) {\n const end = start + length\n\n let j = start & (BITS_PER_SEGMENT - 1)\n let i = (start - j) / BITS_PER_SEGMENT\n\n while (i < this._segments.maxLength) {\n const s = this._segments.get(i)\n const index = s ? s.findFirst(false, j) : j\n if (index !== -1) return i * BITS_PER_SEGMENT + index < end\n\n j = 0\n i++\n\n if (i * BITS_PER_SEGMENT >= end) return false\n }\n\n return true\n }\n\n hasSet(start, length) {\n const end = start + length\n\n let j = start & (BITS_PER_SEGMENT - 1)\n let i = (start - j) / BITS_PER_SEGMENT\n\n while (i < this._segments.maxLength) {\n const s = this._segments.get(i)\n\n let index = -1\n\n if (s) index = s.findFirst(true, j)\n\n if (index !== -1) return i * BITS_PER_SEGMENT + index < end\n\n j = 0\n i++\n\n if (i * BITS_PER_SEGMENT >= end) return false\n }\n\n return false\n }\n\n count(start, length, val) {\n let j = start & (BITS_PER_PAGE - 1)\n let i = (start - j) / BITS_PER_PAGE\n let c = 0\n\n while (length > 0) {\n const p = this._pages.get(i)\n\n const end = Math.min(j + length, BITS_PER_PAGE)\n const range = end - j\n\n if (p) c += p.count(j, range, val)\n else if (!val) c += range\n\n j = 0\n i++\n length -= range\n }\n\n return c\n }\n\n countSet(start, length) {\n return this.count(start, length, true)\n }\n\n countUnset(start, length) {\n return this.count(start, length, false)\n }\n\n *want(start, length) {\n if (start & 31) {\n const d = start & 31\n start -= d\n length += d\n }\n\n let j = start & (BITS_PER_SEGMENT - 1)\n let i = (start - j) / BITS_PER_SEGMENT\n\n while (length > 0) {\n const s = this._segments.get(i)\n\n if (s) {\n let end = Math.min(j + length, BITS_PER_SEGMENT)\n if (end & 31) end += 32 - (end & 31)\n\n yield {\n start: i * BITS_PER_SEGMENT + j,\n bitfield: s.bitfield.subarray(j / 32, end / 32)\n }\n }\n\n j = 0\n i++\n length -= BITS_PER_SEGMENT\n }\n }\n\n clear(tx) {\n return tx.deleteBitfieldPageRange(0, -1)\n }\n\n onupdate(ranges) {\n for (const { start, end, value } of ranges) {\n this.setRange(start, end, value)\n }\n }\n\n static async open(storage, length) {\n if (length === 0) return new Bitfield(storage, null)\n\n const pages = Math.ceil(length / BITS_PER_PAGE)\n const buffer = b4a.alloc(pages * BYTES_PER_PAGE)\n const stream = storage.createBitfieldStream({ lt: pages })\n\n for await (const { index, page } of stream) {\n buffer.set(page, index * BYTES_PER_PAGE)\n }\n\n return new Bitfield(buffer)\n }\n}\nconst crypto = require('hypercore-crypto')\nconst sodium = require('sodium-universal')\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\n\n// TODO: rename this to \"crypto\" and move everything hashing related etc in here\n// Also lets move the tree stuff from hypercore-crypto here\n\nconst [\n TREE,\n REPLICATE_INITIATOR,\n REPLICATE_RESPONDER,\n MANIFEST,\n DEFAULT_NAMESPACE,\n DEFAULT_ENCRYPTION\n] = crypto.namespace('hypercore', 6)\n\nexports.MANIFEST = MANIFEST\nexports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE\nexports.DEFAULT_ENCRYPTION = DEFAULT_ENCRYPTION\n\nexports.replicate = function (isInitiator, key, handshakeHash) {\n const out = b4a.allocUnsafe(32)\n sodium.crypto_generichash_batch(\n out,\n [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key],\n handshakeHash\n )\n return out\n}\n\nexports.treeSignable = function (manifestHash, treeHash, length, fork) {\n const state = { start: 0, end: 112, buffer: b4a.allocUnsafe(112) }\n c.fixed32.encode(state, TREE)\n c.fixed32.encode(state, manifestHash)\n c.fixed32.encode(state, treeHash)\n c.uint64.encode(state, length)\n c.uint64.encode(state, fork)\n return state.buffer\n}\n\nexports.treeSignableCompat = function (hash, length, fork, noHeader) {\n const end = noHeader ? 48 : 80\n const state = { start: 0, end, buffer: b4a.allocUnsafe(end) }\n if (!noHeader) c.fixed32.encode(state, TREE) // ultra legacy mode, kill in future major\n c.fixed32.encode(state, hash)\n c.uint64.encode(state, length)\n c.uint64.encode(state, fork)\n return state.buffer\n}\n// Export the appropriate version of `quickbit-universal` as the plain import\n// may resolve to an older version in some environments\nlet quickbit = require('quickbit-universal')\nif (\n typeof quickbit.findFirst !== 'function' ||\n typeof quickbit.findLast !== 'function' ||\n typeof quickbit.clear !== 'function'\n) {\n // This should always load the fallback from the locally installed version\n quickbit = require('quickbit-universal/fallback')\n}\nexports.quickbit = quickbit\nconst crypto = require('hypercore-crypto')\nconst flat = require('flat-tree')\nconst b4a = require('b4a')\nconst quickbit = require('quickbit-universal')\nconst Bitfield = require('./bitfield')\n\nconst MAX_BATCH_USED = 4 * 1024 * 1024\nconst MIN_BATCH_USED = 512 * 1024\n\n// just in its own file as its a bit involved\n\nmodule.exports = copyPrologue\n\nasync function copyPrologue(src, dst) {\n const prologue = dst.header.manifest.prologue\n\n if (src.length < prologue.length || prologue.length === 0) return\n\n const stack = []\n const roots = flat.fullRoots(prologue.length * 2)\n const batch = { roots, first: true, last: false, contig: 0, used: 0, tree: [], blocks: [] }\n\n for (let i = 0; i < roots.length; i++) {\n const node = roots[i]\n batch.tree.push(node)\n stack.push(node)\n }\n\n let lastPage = -1\n let lastBlock = -1\n\n for await (const data of src.storage.createBlockStream({\n gte: 0,\n lt: prologue.length,\n reverse: true\n })) {\n if (walkTree(stack, data.index * 2, batch) === false) {\n throw new Error('Missing block or tree node for ' + data.index)\n }\n\n batch.contig = data.index + 1 === lastBlock ? batch.contig + 1 : 1\n lastBlock = data.index\n\n const page = getBitfieldPage(data.index)\n batch.blocks.push(data)\n\n if (lastPage !== page) batch.used += 4096\n batch.used += Math.max(data.value.byteLength, 128) // 128 is just a sanity number to avoid mega batches\n\n // always safe to partially flush so we do that ondemand to reduce memory usage...\n if ((batch.used >= MIN_BATCH_USED && page !== lastPage) || batch.used >= MAX_BATCH_USED) {\n await flushBatch(prologue, src, dst, batch)\n }\n\n lastPage = page\n }\n\n if (lastBlock !== 0) batch.contig = 0\n\n batch.last = true\n await flushBatch(prologue, src, dst, batch)\n}\n\nasync function flushBatch(prologue, src, dst, batch) {\n const nodePromises = []\n\n const srcReader = src.storage.read()\n for (const index of batch.tree) {\n nodePromises.push(srcReader.getTreeNode(index))\n }\n srcReader.tryFlush()\n\n const nodes = await Promise.all(nodePromises)\n\n const pagePromises = []\n const dstReader = dst.storage.read()\n\n const headPromise = batch.first ? dstReader.getHead() : null\n if (headPromise) headPromise.catch(noop)\n\n let lastPage = -1\n for (const { index } of batch.blocks) {\n const page = getBitfieldPage(index)\n if (page === lastPage) continue\n lastPage = page\n pagePromises.push(dstReader.getBitfieldPage(page))\n }\n\n dstReader.tryFlush()\n\n const pages = await Promise.all(pagePromises)\n const head = headPromise === null ? null : await headPromise\n const userData = []\n\n // reads done!\n\n if (batch.first) {\n const roots = nodes.slice(0, batch.roots.length)\n\n for (const node of roots) {\n if (!node) throw new Error('Missing nodes for prologue hash')\n }\n\n const treeHash = crypto.tree(roots)\n if (!b4a.equals(treeHash, prologue.hash)) throw new Error('Prologue does not match source')\n }\n\n if (batch.first) {\n for await (const data of src.storage.createUserDataStream()) userData.push(data)\n }\n\n for (let i = 0; i < pages.length; i++) {\n if (!pages[i]) pages[i] = b4a.alloc(4096)\n }\n\n const tx = dst.storage.write()\n\n for (const node of nodes) tx.putTreeNode(node)\n\n lastPage = -1\n let pageIndex = -1\n\n for (const { index, value } of batch.blocks) {\n const page = getBitfieldPage(index)\n\n if (page !== lastPage) {\n lastPage = page\n pageIndex++\n // queue the page now, we mutate it below but its the same ref\n tx.putBitfieldPage(pageIndex, pages[pageIndex])\n }\n\n const pageBuffer = pages[pageIndex]\n quickbit.set(pageBuffer, getBitfieldOffset(index), true)\n tx.putBlock(index, value)\n }\n\n for (const { key, value } of userData) {\n tx.putUserData(key, value)\n }\n\n let upgraded = batch.first && !head\n if (upgraded) {\n tx.setHead(prologueToTree(prologue))\n }\n\n await tx.flush()\n\n if (upgraded) {\n const roots = nodes.slice(0, batch.roots.length)\n dst.state.setRoots(roots)\n dst.header.tree = prologueToTree(prologue)\n }\n\n if (userData.length > 0) {\n dst.header.userData = userData.concat(dst.header.userData)\n }\n\n if (batch.contig) {\n // TODO: we need to persist this somehow\n dst.header.hints.contiguousLength = batch.contig\n }\n\n let start = 0\n let length = 0\n\n // update in memory bitfield\n for (const { index } of batch.blocks) {\n if (start === 0 || start - 1 === index) {\n length++\n } else {\n if (length > 0) signalReplicator(dst, upgraded, start, length)\n upgraded = false\n length = 1\n }\n\n start = index\n dst.bitfield.set(index, true)\n }\n\n if (length > 0) signalReplicator(dst, upgraded, start, length)\n\n // unlink\n batch.tree = []\n batch.blocks = []\n batch.first = false\n batch.used = 0\n}\n\nfunction signalReplicator(core, upgraded, start, length) {\n if (upgraded) {\n core.replicator.cork()\n core.replicator.onhave(start, length, false)\n core.replicator.onupgrade()\n core.replicator.uncork()\n } else {\n core.replicator.onhave(start, length, false)\n }\n}\n\nfunction prologueToTree(prologue) {\n return {\n fork: 0,\n length: prologue.length,\n rootHash: prologue.hash,\n signature: null\n }\n}\n\nfunction getBitfieldPage(index) {\n return Math.floor(index / Bitfield.BITS_PER_PAGE)\n}\n\nfunction getBitfieldOffset(index) {\n return index & (Bitfield.BITS_PER_PAGE - 1)\n}\n\nfunction walkTree(stack, target, batch) {\n while (stack.length > 0) {\n const node = stack.pop()\n\n if ((node & 1) === 0) {\n if (node === target) return true\n continue\n }\n\n const ite = flat.iterator(node)\n if (!ite.contains(target)) continue\n\n while ((ite.index & 1) !== 0) {\n const left = ite.leftChild()\n const right = ite.sibling() // is right child\n\n batch.tree.push(left, right)\n\n if (ite.contains(target)) stack.push(left)\n else ite.sibling()\n }\n\n if (ite.index === target) return true\n }\n\n return false\n}\n\nfunction noop() {}\nconst crypto = require('hypercore-crypto')\nconst b4a = require('b4a')\nconst unslab = require('unslab')\nconst z32 = require('z32')\nconst Mutex = require('./mutex')\nconst { MerkleTree, ReorgBatch } = require('./merkle-tree')\nconst BitInterlude = require('./bit-interlude')\nconst Bitfield = require('./bitfield')\nconst RemoteBitfield = require('./remote-bitfield')\nconst {\n BAD_ARGUMENT,\n STORAGE_EMPTY,\n STORAGE_CONFLICT,\n INVALID_SIGNATURE,\n INVALID_CHECKSUM\n} = require('hypercore-errors')\nconst Verifier = require('./verifier')\nconst audit = require('./audit')\nconst copyPrologue = require('./copy-prologue')\nconst SessionState = require('./session-state')\nconst Replicator = require('./replicator')\n\nmodule.exports = class Core {\n constructor(db, opts = {}) {\n this.db = db\n this.storage = null\n this.replicator = new Replicator(this, opts)\n this.sessionStates = []\n this.monitors = []\n this.activeSessions = 0\n this.gc = 0 // corestore uses this to main a gc strike pool\n\n this.id = opts.key ? z32.encode(opts.key) : null\n this.key = opts.key || null\n this.discoveryKey = opts.discoveryKey || (opts.key && crypto.discoveryKey(opts.key)) || null\n this.manifest = null\n this.opening = null\n this.closing = null\n this.exclusive = null\n\n this.preupdate = null\n this.header = null\n this.compat = false\n this.bitfield = null\n this.verifier = null\n this.truncating = 0\n this.updating = false\n this.skipBitfield = null\n this.globalCache = opts.globalCache || null\n this.autoClose = opts.autoClose !== false\n this.onidle = noop\n\n this.state = null\n this.opened = false\n this.destroyed = false\n this.closed = false\n\n this.hintsChanged = false\n\n this._bitfield = null\n this._verifies = null\n this._verifiesFlushed = null\n this._legacy = !!opts.legacy\n\n this.opening = this._open(opts)\n this.opening.catch(noop)\n }\n\n ready() {\n return this.opening\n }\n\n addMonitor(s) {\n if (s._monitorIndex >= 0) return\n s._monitorIndex = this.monitors.push(s) - 1\n }\n\n removeMonitor(s) {\n if (s._monitorIndex < 0) return\n const head = this.monitors.pop()\n if (head !== s) this.monitors[(head._monitorIndex = s._monitorIndex)] = head\n s._monitorIndex = -1\n }\n\n emitManifest() {\n for (let i = this.monitors.length - 1; i >= 0; i--) {\n this.monitors[i].emit('manifest')\n }\n }\n\n createUserDataStream(opts, session = this.state) {\n return session.storage.createUserDataStream(opts)\n }\n\n allSessions() {\n const sessions = []\n for (const state of this.sessionStates) {\n if (state.sessions.length) sessions.push(...state.sessions)\n }\n return sessions\n }\n\n hasSession() {\n return this.activeSessions !== 0\n }\n\n compact() {\n const compacting = []\n for (const s of this.sessionStates) {\n compacting.push(s.storage.compact())\n }\n return Promise.all(compacting)\n }\n\n checkIfIdle() {\n if (!this.opened || this.destroyed === true || this.hasSession() === true) return\n if (this.replicator.idle() === false) return\n if (this.state === null || this.state.mutex.idle() === false) return\n this.onidle()\n }\n\n async lockExclusive() {\n if (this.exclusive === null) this.exclusive = new Mutex()\n await this.exclusive.lock()\n }\n\n unlockExclusive() {\n if (this.exclusive !== null) this.exclusive.unlock()\n }\n\n async _open(opts) {\n try {\n await this._tryOpen(opts)\n } catch (err) {\n this.onidle()\n throw err\n }\n\n this.opened = true\n }\n\n async _tryOpen(opts) {\n if (opts.preopen) await opts.preopen // just a hook to allow exclusive access here...\n\n let storage = await this.db.resumeCore(this.discoveryKey)\n\n let overwrite = opts.overwrite === true\n\n const force = opts.force === true\n const createIfMissing = opts.createIfMissing !== false\n // kill this flag soon\n const legacy = !!opts.legacy\n\n // default to true for now if no manifest is provided\n let compat = opts.compat === true || (opts.compat !== false && !opts.manifest)\n\n let header = storage ? parseHeader(await getCoreInfo(storage)) : null\n\n const hadManifest = !!header && !!header.manifest && !!header.key\n const hadKeyPair = !!header && !!header.keyPair\n\n if (force && opts.key && header && !b4a.equals(header.key, opts.key)) {\n overwrite = true\n }\n\n if (!header && opts.discoveryKey && !(opts.key || opts.manifest)) {\n throw STORAGE_EMPTY('No Hypercore is stored here', this.discoveryKey)\n }\n\n if (!header || overwrite) {\n if (!createIfMissing) {\n throw STORAGE_EMPTY('No Hypercore is stored here', this.discoveryKey)\n }\n\n if (compat) {\n if (opts.key && opts.keyPair && !b4a.equals(opts.key, opts.keyPair.publicKey)) {\n throw BAD_ARGUMENT('Key must match publicKey when in compat mode', this.discoveryKey)\n }\n }\n\n const keyPair = opts.keyPair || (opts.key ? null : crypto.keyPair())\n\n const defaultManifest =\n !opts.manifest &&\n (!!opts.compat || !opts.key || !!(keyPair && b4a.equals(opts.key, keyPair.publicKey)))\n const manifest = defaultManifest\n ? Verifier.defaultSignerManifest(opts.key || keyPair.publicKey)\n : Verifier.createManifest(opts.manifest)\n\n header = {\n key: opts.key || (compat ? manifest.signers[0].publicKey : Verifier.manifestHash(manifest)),\n manifest,\n keyPair: keyPair\n ? { publicKey: keyPair.publicKey, secretKey: keyPair.secretKey || null }\n : null,\n frozen: false,\n tree: {\n fork: 0,\n length: 0,\n rootHash: null,\n signature: null\n },\n hints: {\n reorgs: [],\n contiguousLength: 0,\n remoteContiguousLength: 0\n }\n }\n\n const discoveryKey = opts.discoveryKey || crypto.discoveryKey(header.key)\n\n storage = await this.db.createCore({\n key: header.key,\n manifest,\n keyPair,\n frozen: false,\n discoveryKey,\n userData: opts.userData || [],\n alias: opts.alias || null\n })\n }\n\n // unslab the long lived buffers to avoid keeping the slab alive\n header.key = unslab(header.key)\n\n if (header.tree) {\n header.tree.rootHash = unslab(header.tree.rootHash)\n header.tree.signature = unslab(header.tree.signature)\n }\n\n if (header.keyPair) {\n header.keyPair.publicKey = unslab(header.keyPair.publicKey)\n header.keyPair.secretKey = unslab(header.keyPair.secretKey)\n }\n\n if (header.keyPair) {\n header.keyPair.publicKey = unslab(header.keyPair.publicKey)\n header.keyPair.secretKey = unslab(header.keyPair.secretKey)\n }\n\n if (opts.manifest) {\n // if we provide a manifest and no key, verify that the stored key is the same\n if (\n !opts.key &&\n !Verifier.isValidManifest(header.key, Verifier.createManifest(opts.manifest))\n ) {\n throw STORAGE_CONFLICT('Manifest does not hash to provided key', this.discoveryKey)\n }\n\n if (!header.manifest) header.manifest = opts.manifest\n }\n\n if (opts.key && !b4a.equals(header.key, opts.key)) {\n throw STORAGE_CONFLICT('Another Hypercore is stored here', this.discoveryKey)\n }\n\n // if we signalled compat, but already now this core isn't disable it\n if (compat && header.manifest && !Verifier.isCompat(header.key, header.manifest)) {\n compat = false\n } else if (!compat && header.manifest && Verifier.isCompat(header.key, header.manifest)) {\n compat = true\n }\n\n const prologue = header.manifest ? header.manifest.prologue : null\n\n const bitfield = await Bitfield.open(storage, header.tree.length)\n\n const treeInfo = {\n fork: header.tree.fork,\n length: header.tree.length,\n signature: header.tree.signature,\n roots: header.tree.length\n ? await MerkleTree.getRootsFromStorage(storage, header.tree.length)\n : [],\n prologue\n }\n\n if (overwrite) {\n const tx = storage.write()\n tx.deleteTreeNodeRange(0, -1)\n tx.deleteBlockRange(0, -1)\n bitfield.clear(tx)\n await tx.flush()\n }\n\n const len = bitfield.findFirst(false, header.hints.contiguousLength)\n if (header.hints.contiguousLength !== len && !storage.readOnly) {\n header.hints.contiguousLength = len\n const tx = storage.write()\n tx.setHints(header.hints)\n await tx.flush()\n }\n\n // to unslab\n if (header.manifest) {\n header.manifest = Verifier.createManifest(header.manifest)\n if (!hadManifest || (header.keyPair && !hadKeyPair)) {\n const tx = storage.write()\n tx.setAuth({\n key: header.key,\n discoveryKey: crypto.discoveryKey(header.key),\n manifest: header.manifest,\n keyPair: header.keyPair\n })\n await tx.flush()\n }\n }\n\n const verifier = header.manifest\n ? new Verifier(header.key, header.manifest, { crypto, legacy })\n : null\n\n this.storage = storage\n this.header = header\n this.compat = compat\n this.bitfield = bitfield\n this.verifier = verifier\n this.state = new SessionState(this, null, storage, treeInfo, null)\n\n if (this.key === null) this.key = this.header.key\n if (this.discoveryKey === null) this.discoveryKey = crypto.discoveryKey(this.key)\n if (this.id === null) this.id = z32.encode(this.key)\n if (this.manifest === null) this.manifest = this.header.manifest\n }\n\n async audit(opts) {\n await this.state.mutex.lock()\n\n try {\n return await audit(this, opts)\n } finally {\n this.state._unlock()\n }\n }\n\n async setManifest(manifest) {\n await this.state.mutex.lock()\n\n try {\n if (manifest && this.header.manifest === null) {\n if (!Verifier.isValidManifest(this.header.key, manifest)) {\n throw INVALID_CHECKSUM('Manifest hash does not match', this.discoveryKey)\n }\n\n const tx = this.state.createWriteBatch()\n this._setManifest(tx, Verifier.createManifest(manifest), null)\n await this.state.flush()\n }\n } finally {\n this.state._unlock()\n }\n }\n\n _setManifest(tx, manifest, keyPair) {\n if (!manifest && b4a.equals(keyPair.publicKey, this.header.key)) {\n manifest = Verifier.defaultSignerManifest(this.header.key)\n }\n if (!manifest) return\n\n const verifier = new Verifier(this.header.key, manifest, { legacy: this._legacy })\n\n if (verifier.prologue) this.state.prologue = Object.assign({}, verifier.prologue)\n\n this.manifest = this.header.manifest = manifest\n\n tx.setAuth({\n key: this.header.key,\n discoveryKey: this.discoveryKey,\n manifest,\n keyPair: this.header.keyPair\n // TODO: encryptionKey?\n })\n\n this.compat = verifier.compat\n this.verifier = verifier\n\n this.replicator.onupgrade()\n this.emitManifest()\n }\n\n async copyPrologue(src) {\n await this.state.mutex.lock()\n\n try {\n await src.mutex.lock()\n } catch (err) {\n this.state.mutex.unlock()\n throw err\n }\n\n try {\n await copyPrologue(src, this)\n } finally {\n src.mutex.unlock()\n this.state.mutex.unlock()\n this.checkIfIdle()\n }\n }\n\n flushed() {\n return this.state.flushed()\n }\n\n async _validateCommit(state, treeLength) {\n if (this.state.length > state.length) {\n return false // TODO: partial commit and truncation possible in the future\n }\n\n if (this.state.length > treeLength) {\n for (const root of this.state.roots) {\n const batchRoot = await MerkleTree.get(state, root.index)\n if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {\n return false\n }\n }\n }\n\n if (this.verifier === null) {\n return false // easier to assert than upsert\n }\n\n return true\n }\n\n _verifyBatchUpgrade(batch, manifest) {\n if (!this.header.manifest) {\n // compat, drop at some point\n if (!manifest) manifest = Verifier.defaultSignerManifest(this.header.key)\n\n if (\n !manifest ||\n !(\n Verifier.isValidManifest(this.header.key, manifest) ||\n Verifier.isCompat(this.header.key, manifest)\n )\n ) {\n throw INVALID_SIGNATURE('Proof contains an invalid manifest', this.discoveryKey) // TODO: proper error type\n }\n }\n\n const verifier =\n this.verifier ||\n new Verifier(this.header.key, Verifier.createManifest(manifest), { legacy: this._legacy })\n\n if (!verifier.verify(batch, batch.signature)) {\n throw INVALID_SIGNATURE('Proof contains an invalid signature', this.discoveryKey)\n }\n\n return manifest\n }\n\n async _verifyExclusive({ batch, bitfield, value, manifest }) {\n manifest = this._verifyBatchUpgrade(batch, manifest)\n\n if (\n !(await this.state._verifyBlock(\n batch,\n bitfield,\n value,\n this.header.manifest ? null : manifest\n ))\n ) {\n return false\n }\n\n if (!batch.upgraded && bitfield) {\n this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop)\n }\n\n return true\n }\n\n async _verifyShared() {\n if (!this._verifies.length) return false\n\n await this.state.mutex.lock()\n\n const tx = this.state.createWriteBatch()\n\n const verifies = this._verifies\n this._verifies = null\n this._verified = null\n\n try {\n for (const { batch, bitfield, value } of verifies) {\n if (!batch.commitable()) continue\n\n if (bitfield) {\n tx.putBlock(bitfield.start, value)\n }\n }\n\n const bits = new BitInterlude()\n\n for (let i = 0; i < verifies.length; i++) {\n const { batch, bitfield, manifest } = verifies[i]\n\n if (!batch.commitable()) {\n verifies[i] = null // signal that we cannot commit this one\n continue\n }\n\n if (bitfield) {\n bits.setRange(bitfield.start, bitfield.start + 1, true)\n }\n\n // if we got a manifest AND its strictly a non compat one, lets store it\n if (manifest && this.header.manifest === null) {\n if (!Verifier.isValidManifest(this.header.key, manifest)) {\n throw INVALID_CHECKSUM('Manifest hash does not match', this.discoveryKey)\n }\n this._setManifest(tx, manifest, null)\n }\n\n if (batch.commitable()) batch.commit(tx)\n }\n\n const ranges = bits.flush(tx, this.bitfield)\n\n await this.state.flush()\n\n for (const { start, end, value } of ranges) {\n this._setBitfieldRanges(start, end, value)\n }\n\n for (let i = 0; i < verifies.length; i++) {\n const bitfield = verifies[i] && verifies[i].bitfield\n if (bitfield) {\n this.updateContiguousLength(bitfield)\n this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop)\n }\n }\n\n if (this.hintsChanged) await this.flushHints()\n } finally {\n this.state._clearActiveBatch()\n this.state.mutex.unlock()\n }\n\n return verifies[0] !== null\n }\n\n async flushHints() {\n if (!this.hintsChanged) return\n this.hintsChanged = false // we unset this immediately as a \"debounce\"\n\n const tx = this.state.storage.write()\n\n tx.setHints({\n contiguousLength: this.header.hints.contiguousLength,\n remoteContiguousLength: this.header.hints.remoteContiguousLength\n })\n\n await tx.flush()\n }\n\n async checkConflict(proof, from) {\n if (this.state.length < proof.upgrade.length || proof.fork !== this.state.fork) {\n // out of date this proof - ignore for now\n return false\n }\n\n // sanity check -> no manifest, no way to verify\n if (!this.header.manifest) {\n return false\n }\n\n const batch = MerkleTree.verifyFullyRemote(this.state, proof)\n\n try {\n this._verifyBatchUpgrade(batch, proof.manifest)\n } catch {\n return true\n }\n\n const roots = await MerkleTree.getRootsFromStorage(this.storage, proof.upgrade.length)\n const remoteTreeHash = crypto.tree(proof.upgrade.nodes)\n const localTreeHash = crypto.tree(roots)\n\n try {\n const rx = this.state.storage.read()\n const treeProofPromise = MerkleTree.proof(this.state, rx, {\n block: null,\n hash: null,\n seek: null,\n upgrade: {\n start: 0,\n length: proof.upgrade.length\n }\n })\n\n rx.tryFlush()\n\n const treeProof = await treeProofPromise\n\n const verifyBatch = MerkleTree.verifyFullyRemote(this.state, await treeProof.settle())\n this._verifyBatchUpgrade(verifyBatch, this.header.manifest)\n } catch {\n return true\n }\n\n // both proofs are valid, now check if they forked\n if (b4a.equals(localTreeHash, remoteTreeHash)) return false\n\n await this.state.mutex.lock()\n\n try {\n const tx = this.state.createWriteBatch()\n\n this.header.frozen = true\n\n tx.setAuth({\n key: this.header.key,\n discoveryKey: this.discoveryKey,\n manifest: this.header.manifest,\n keyPair: this.header.keyPair,\n frozen: true\n })\n\n await this.state.flush()\n } finally {\n this.state.mutex.unlock()\n }\n\n // tmp log so we can see these\n const id = b4a.toString(this.discoveryKey, 'hex')\n console.log(\n '[hypercore] conflict detected in ' +\n id +\n ' (writable=' +\n !!this.header.keyPair +\n ',quorum=' +\n this.header.manifest.quorum +\n ')'\n )\n await this._onconflict(proof)\n return true\n }\n\n async verifyReorg(proof) {\n const batch = new ReorgBatch(this.state)\n await MerkleTree.reorg(this.state, proof, batch)\n const manifest = this._verifyBatchUpgrade(batch, proof.manifest)\n\n if (manifest && !this.header.manifest) {\n await this.state.mutex.lock()\n try {\n if (manifest && this.header.manifest === null) {\n const tx = this.state.createWriteBatch()\n this._setManifest(tx, Verifier.createManifest(manifest), null)\n await this.state.flush()\n }\n } finally {\n this.state._unlock()\n }\n }\n\n return batch\n }\n\n async verify(proof, from) {\n // We cannot apply \"other forks\" atm.\n // We should probably still try and they are likely super similar for non upgrades\n // but this is easy atm (and the above layer will just retry)\n if (proof.fork !== this.state.fork) return false\n\n const batch = await MerkleTree.verify(this.state, proof)\n\n if (batch.upgraded && batch.length <= this.state.length) {\n await batch.downgrade()\n }\n\n if (!batch.commitable()) {\n return false\n }\n\n const value = (proof.block && proof.block.value) || null\n const op = {\n batch,\n bitfield: value && { drop: false, start: proof.block.index, length: 1 },\n value,\n manifest: proof.manifest,\n from\n }\n\n if (batch.upgraded) {\n return this._verifyExclusive(op)\n }\n\n if (this._verifies !== null) {\n const verifies = this._verifies\n const i = verifies.push(op)\n await this._verified\n return verifies[i] !== null\n }\n\n this._verifies = [op]\n this._verified = this._verifyShared()\n\n return this._verified\n }\n\n async reorg(batch) {\n if (!batch.commitable()) return false\n\n this.truncating++\n\n try {\n await this.state.reorg(batch)\n } finally {\n this.truncating--\n }\n\n return true\n }\n\n openSkipBitfield() {\n if (this.skipBitfield !== null) return this.skipBitfield\n this.skipBitfield = new RemoteBitfield()\n const buf = this.bitfield.toBuffer(this.state.length)\n const bitfield = new Uint32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4)\n this.skipBitfield.insert(0, bitfield)\n return this.skipBitfield\n }\n\n _setBitfieldRanges(start, end, value) {\n this.bitfield.setRange(start, end, value)\n if (this.skipBitfield !== null) this.skipBitfield.setRange(start, end, value)\n }\n\n close() {\n if (!this.closing) this.closing = this._close()\n return this.closing\n }\n\n updateContiguousLength(bitfield) {\n const contig = updateContigBatch(this.header.hints.contiguousLength, bitfield, this.bitfield)\n\n if (contig.length !== -1 && contig.length !== this.header.hints.contiguousLength) {\n this.header.hints.contiguousLength = contig.length\n this.hintsChanged = true\n }\n }\n\n updateRemoteContiguousLength(length) {\n this.header.hints.remoteContiguousLength = length\n this.hintsChanged = true\n\n for (let i = this.monitors.length - 1; i >= 0; i--) {\n this.monitors[i].emit('remote-contiguous-length', length)\n }\n }\n\n onappend(tree, bitfield) {\n this.header.tree = tree\n\n if (!bitfield) {\n this.replicator.onupgrade()\n return\n }\n\n this.replicator.cork()\n\n const { start, length, drop } = bitfield\n\n this._setBitfieldRanges(start, start + length, true)\n this.updateContiguousLength({ start, length, drop: false })\n\n this.replicator.onupgrade()\n this.replicator.onhave(start, length, drop)\n this.replicator.uncork()\n }\n\n ontruncate(tree, { start, length }) {\n if (tree) this.header.tree = tree\n\n this.replicator.cork()\n\n this.replicator.ontruncate(start, length)\n this.replicator.onhave(start, length, true)\n this.replicator.onupgrade()\n this.replicator.uncork()\n\n for (const sessionState of this.sessionStates) {\n if (start < sessionState.snapshotCompatLength) sessionState.snapshotCompatLength = start\n }\n\n this._setBitfieldRanges(start, start + length, false)\n this.updateContiguousLength({ start, length, drop: true })\n }\n\n async _onconflict(proof) {\n await this.replicator.onconflict()\n\n for (let i = this.monitors.length - 1; i >= 0; i--) {\n const s = this.monitors[i]\n s.emit('conflict', proof.upgrade.length, proof.fork, proof)\n }\n\n const err = new Error('Two conflicting signatures exist for length ' + proof.upgrade.length)\n await this.closeAllSessions(err)\n }\n\n async closeAllSessions(err) {\n // this.sessions modifies itself when a session closes\n // This way we ensure we indeed iterate over all sessions\n const sessions = this.allSessions()\n\n const all = []\n for (const s of sessions) all.push(s.close({ error: err, force: false })) // force false or else infinite recursion\n await Promise.allSettled(all)\n }\n\n async destroy() {\n if (this.destroyed === true) return\n this.destroyed = true\n\n if (this.hasSession() === true) throw new Error('Cannot destroy while sessions are open')\n\n const weakSessions = this.allSessions()\n\n if (this.replicator) this.replicator.destroy()\n if (this.state) await this.state.close()\n\n // close all pending weak sessions...\n for (const s of weakSessions) s.close().catch(noop)\n }\n\n async _close() {\n if (this.opened === false) await this.opening\n if (this.hasSession() === true) throw new Error('Cannot close while sessions are open')\n\n if (this.replicator) await this.replicator.close()\n\n await this.destroy()\n if (this.autoClose) await this.storage.store.close()\n\n this.closed = true\n }\n}\n\nfunction updateContigBatch(start, upd, bitfield) {\n const end = upd.start + upd.length\n\n let c = start\n\n if (upd.drop) {\n // If we dropped a block in the current contig range, \"downgrade\" it\n if (c > upd.start) {\n c = upd.start\n }\n } else {\n if (c <= end && c >= upd.start) {\n c = end\n while (bitfield.get(c)) c++\n }\n }\n\n if (c === start) {\n return {\n length: -1\n }\n }\n\n if (c > start) {\n return {\n length: c\n }\n }\n\n return {\n length: c\n }\n}\n\nfunction getDefaultTree() {\n return {\n fork: 0,\n length: 0,\n rootHash: null,\n signature: null\n }\n}\n\nfunction parseHeader(info) {\n if (!info) return null\n\n return {\n key: info.key,\n manifest: info.manifest,\n external: null,\n keyPair: info.keyPair,\n tree: info.head || getDefaultTree(),\n hints: {\n reorgs: [],\n contiguousLength: info.hints ? info.hints.contiguousLength : 0,\n remoteContiguousLength: info.hints ? info.hints.remoteContiguousLength : 0\n }\n }\n}\n\nfunction noop() {}\n\nasync function getCoreInfo(storage) {\n const r = storage.read()\n\n const auth = r.getAuth()\n const head = r.getHead()\n const hints = r.getHints()\n\n r.tryFlush()\n\n const [authInfo, headInfo, hintsInfo] = await Promise.all([auth, head, hints])\n return {\n ...authInfo,\n head: headInfo,\n hints: hintsInfo\n }\n}\nconst sodium = require('sodium-universal')\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst { DEFAULT_ENCRYPTION } = require('./caps')\n\nconst nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)\n\nmodule.exports = class DefaultEncryption {\n static PADDING = 8\n\n constructor(encryptionKey, hypercoreKey, opts = {}) {\n this.key = encryptionKey\n this.compat = opts.compat === true\n\n const keys = DefaultEncryption.deriveKeys(encryptionKey, hypercoreKey, opts)\n\n this.blockKey = keys.block\n this.blindingKey = keys.blinding\n }\n\n static deriveKeys(encryptionKey, hypercoreKey, { block = false, compat = false } = {}) {\n const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)\n\n const blockKey = block ? encryptionKey : subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)\n const blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)\n\n if (!block) {\n if (compat) {\n sodium.crypto_generichash_batch(blockKey, [encryptionKey], hypercoreKey)\n } else {\n sodium.crypto_generichash_batch(blockKey, [DEFAULT_ENCRYPTION, hypercoreKey, encryptionKey])\n }\n }\n\n sodium.crypto_generichash(blindingKey, blockKey)\n\n return {\n blinding: blindingKey,\n block: blockKey\n }\n }\n\n static blockEncryptionKey(hypercoreKey, encryptionKey) {\n const blockKey = b4a.alloc(sodium.crypto_stream_KEYBYTES)\n sodium.crypto_generichash_batch(blockKey, [DEFAULT_ENCRYPTION, hypercoreKey, encryptionKey])\n return blockKey\n }\n\n static encrypt(index, block, fork, blockKey, blindingKey) {\n const padding = block.subarray(0, DefaultEncryption.PADDING)\n block = block.subarray(DefaultEncryption.PADDING)\n\n c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)\n c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)\n\n // Zero out any previous padding.\n nonce.fill(0, 8, 8 + padding.byteLength)\n\n // Blind the fork ID, possibly risking reusing the nonce on a reorg of the\n // Hypercore. This is fine as the blinding is best-effort and the latest\n // fork ID shared on replication anyway.\n sodium.crypto_stream_xor(padding, padding, nonce, blindingKey)\n\n nonce.set(padding, 8)\n\n // The combination of a (blinded) fork ID and a block index is unique for a\n // given Hypercore and is therefore a valid nonce for encrypting the block.\n sodium.crypto_stream_xor(block, block, nonce, blockKey)\n }\n\n static decrypt(index, block, blockKey) {\n const padding = block.subarray(0, DefaultEncryption.PADDING)\n block = block.subarray(DefaultEncryption.PADDING)\n\n c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)\n\n nonce.set(padding, 8)\n\n // Decrypt the block using the blinded fork ID.\n sodium.crypto_stream_xor(block, block, nonce, blockKey)\n }\n\n encrypt(index, block, fork, core) {\n if (core.compat !== this.compat) this._reload(core)\n return DefaultEncryption.encrypt(index, block, fork, this.blockKey, this.blindingKey)\n }\n\n decrypt(index, block, core) {\n if (core.compat !== this.compat) this._reload(core)\n return DefaultEncryption.decrypt(index, block, this.blockKey)\n }\n\n padding() {\n return DefaultEncryption.PADDING\n }\n\n _reload(core) {\n const block = b4a.equals(this.key, this.blockKey)\n const keys = DefaultEncryption.deriveKeys(this.key, core.key, { block, compat: core.compat })\n\n this.blockKey = keys.blockKey\n this.blindingKey = keys.blindingKey\n }\n}\nmodule.exports = class Download {\n constructor(session, range) {\n this.session = session\n this.range = range\n this.request = null\n this.opened = false\n this.opening = this._open()\n this.opening.catch(noop)\n }\n\n ready() {\n return this.opening\n }\n\n async _open() {\n if (this.session.opened === false) await this.session.opening\n this._download()\n this.opened = true\n }\n\n async done() {\n await this.ready()\n\n try {\n return await this.request.promise\n } catch (err) {\n if (isSessionMoved(err)) return this._download()\n throw err\n }\n }\n\n _download() {\n const activeRequests = (this.range && this.range.activeRequests) || this.session.activeRequests\n this.request = this.session.core.replicator.addRange(activeRequests, this.range)\n this.request.promise.catch(noop)\n return this.request.promise\n }\n\n /**\n * Deprecated. Use `range.done()`.\n */\n downloaded() {\n return this.done()\n }\n\n destroy() {\n this._destroyBackground().catch(noop)\n }\n\n async _destroyBackground() {\n if (this.opened === false) await this.ready()\n if (this.request.context) this.request.context.detach(this.request)\n }\n}\n\nfunction noop() {}\n\nfunction isSessionMoved(err) {\n return err.code === 'SESSION_MOVED'\n}\nconst TICKS = 16\n\nmodule.exports = class HotswapQueue {\n constructor() {\n this.priorities = [[], [], []]\n }\n\n *pick(peer) {\n for (let i = 0; i < this.priorities.length; i++) {\n // try first one more than second one etc etc\n let ticks = (this.priorities.length - i) * TICKS\n const queue = this.priorities[i]\n\n for (let j = 0; j < queue.length; j++) {\n const r = j + Math.floor(Math.random() * queue.length - j)\n const a = queue[j]\n const b = queue[r]\n\n if (r !== j) {\n queue[(b.hotswap.index = j)] = b\n queue[(a.hotswap.index = r)] = a\n }\n\n if (hasInflight(b, peer)) continue\n\n yield b\n\n if (--ticks <= 0) break\n }\n }\n }\n\n add(block) {\n if (block.hotswap !== null) this.remove(block)\n if (block.inflight.length === 0 || block.inflight.length >= 3) return\n\n // TODO: also use other stuff to determine queue prio\n const queue = this.priorities[block.inflight.length - 1]\n\n const index = queue.push(block) - 1\n block.hotswap = { ref: this, queue, index }\n }\n\n remove(block) {\n const hotswap = block.hotswap\n if (hotswap === null) return\n\n block.hotswap = null\n const head = hotswap.queue.pop()\n if (head === block) return\n hotswap.queue[(head.hotswap.index = hotswap.index)] = head\n }\n}\n\nfunction hasInflight(block, peer) {\n for (let j = 0; j < block.inflight.length; j++) {\n if (block.inflight[j].peer === peer) return true\n }\n return false\n}\nmodule.exports = class Info {\n constructor(opts = {}) {\n this.key = opts.key\n this.discoveryKey = opts.discoveryKey\n this.length = opts.length || 0\n this.contiguousLength = opts.contiguousLength || 0\n this.byteLength = opts.byteLength || 0\n this.fork = opts.fork || 0\n this.padding = opts.padding || 0\n this.storage = opts.storage || null\n }\n\n static async from(session, opts = {}) {\n return new Info({\n key: session.key,\n discoveryKey: session.discoveryKey,\n length: session.length,\n contiguousLength: session.contiguousLength,\n byteLength: session.byteLength,\n fork: session.fork,\n padding: session.padding,\n storage: opts.storage ? await this.storage(session) : null\n })\n }\n\n static async storage(session) {\n const { oplog, tree, blocks, bitfield } = session.core\n try {\n return {\n oplog: await Info.bytesUsed(oplog.storage),\n tree: await Info.bytesUsed(tree.storage),\n blocks: await Info.bytesUsed(blocks.storage),\n bitfield: await Info.bytesUsed(bitfield.storage)\n }\n } catch {\n return null\n }\n }\n\n static bytesUsed(file) {\n return new Promise((resolve, reject) => {\n file.stat((err, st) => {\n if (err) {\n resolve(0) // prob just file not found (TODO, improve)\n } else if (typeof st.blocks !== 'number') {\n reject(new Error('cannot determine bytes used'))\n } else {\n resolve(st.blocks * 512)\n }\n })\n })\n }\n}\nconst b4a = require('b4a')\n\nmodule.exports = function (core, depth, opts) {\n let indent = ''\n if (typeof opts.indentationLvl === 'number') {\n while (indent.length < opts.indentationLvl) indent += ' '\n }\n\n let peers = ''\n const min = Math.min(core.peers.length, 5)\n\n for (let i = 0; i < min; i++) {\n const peer = core.peers[i]\n\n peers += `${indent} Peer(\\n`\n peers += `${indent} remotePublicKey: ${opts.stylize(toHex(peer.remotePublicKey), 'string')}\\n`\n peers += `${indent} remoteLength: ${opts.stylize(peer.remoteLength, 'number')}\\n`\n peers += `${indent} remoteFork: ${opts.stylize(peer.remoteFork, 'number')}\\n`\n peers += `${indent} remoteCanUpgrade: ${opts.stylize(peer.remoteCanUpgrade, 'boolean')}\\n`\n peers += `${indent} )\\n`\n }\n\n if (core.peers.length > 5) {\n peers += `${indent} ... and ${core.peers.length - 5} more\\n`\n }\n\n if (peers) peers = `[\\n${peers}${indent} ]`\n else peers = `[ ${opts.stylize(0, 'number')} ]`\n\n return (\n `${core.constructor.name}(\\n` +\n `${indent} id: ${opts.stylize(core.id, 'string')}\\n` +\n `${indent} key: ${opts.stylize(toHex(core.key), 'string')}\\n` +\n `${indent} discoveryKey: ${opts.stylize(toHex(core.discoveryKey), 'string')}\\n` +\n `${indent} opened: ${opts.stylize(core.opened, 'boolean')}\\n` +\n `${indent} closed: ${opts.stylize(core.closed, 'boolean')}\\n` +\n `${indent} snapshotted: ${opts.stylize(core.snapshotted, 'boolean')}\\n` +\n `${indent} writable: ${opts.stylize(core.writable, 'boolean')}\\n` +\n `${indent} length: ${opts.stylize(core.length, 'number')}\\n` +\n `${indent} fork: ${opts.stylize(core.fork, 'number')}\\n` +\n `${indent} sessions: [ ${opts.stylize(core.sessions.length, 'number')} ]\\n` +\n `${indent} activeRequests: [ ${opts.stylize(core.activeRequests.length, 'number')} ]\\n` +\n `${indent} peers: ${peers}\\n` +\n `${indent})`\n )\n}\n\nfunction toHex(buf) {\n return buf && b4a.toString(buf, 'hex')\n}\nconst flat = require('flat-tree')\nconst crypto = require('hypercore-crypto')\nconst b4a = require('b4a')\nconst unslab = require('unslab')\nconst caps = require('./caps')\nconst {\n INVALID_PROOF,\n INVALID_CHECKSUM,\n INVALID_OPERATION,\n BAD_ARGUMENT,\n ASSERTION\n} = require('hypercore-errors')\n\nclass NodeQueue {\n constructor(nodes, extra = null) {\n this.i = 0\n this.nodes = nodes\n this.extra = extra\n this.length = nodes.length + (this.extra === null ? 0 : 1)\n }\n\n shift(index) {\n if (this.extra !== null && this.extra.index === index) {\n const node = this.extra\n this.extra = null\n this.length--\n return node\n }\n\n if (this.i >= this.nodes.length) {\n throw INVALID_OPERATION('Expected node ' + index + ', got (nil)')\n }\n\n const node = this.nodes[this.i++]\n if (node.index !== index) {\n throw INVALID_OPERATION('Expected node ' + index + ', got node ' + node.index)\n }\n\n this.length--\n return node\n }\n}\n\nclass MerkleTreeBatch {\n constructor(session) {\n this.fork = session.fork\n this.roots = [...session.roots]\n this.length = session.length\n this.signature = session.signature\n this.ancestors = session.length\n this.byteLength = session.byteLength\n this.prologue = session.prologue\n this.hashCached = null\n\n this.committed = false\n this.truncated = false\n this.treeLength = session.length\n this.treeFork = session.fork\n this.storage = session.storage\n this.session = session\n this.nodes = []\n this.upgraded = false\n }\n\n checkout(length, additionalRoots) {\n const roots = []\n let r = 0\n\n const head = 2 * length - 2\n const gaps = new Set()\n const all = new Map()\n\n // additional roots is so the original roots can be passed (we mutate the array in appendRoot)\n if (additionalRoots) {\n for (const node of additionalRoots) all.set(node.index, node)\n }\n\n for (const node of this.nodes) all.set(node.index, node)\n\n for (const index of flat.fullRoots(head + 2)) {\n const left = flat.leftSpan(index)\n if (left !== 0) gaps.add(left - 1)\n\n if (r < this.roots.length && this.roots[r].index === index) {\n roots.push(this.roots[r++])\n continue\n }\n const node = all.get(index)\n if (!node) throw new BAD_ARGUMENT('root missing for given length')\n roots.push(node)\n }\n\n this.roots = roots\n this.length = length\n this.byteLength = totalSize(roots)\n this.hashCached = null\n this.signature = null\n\n for (let i = 0; i < this.nodes.length; i++) {\n const index = this.nodes[i].index\n if (index <= head && !gaps.has(index)) continue\n const last = this.nodes.pop()\n if (i < this.nodes.length) this.nodes[i--] = last\n }\n }\n\n prune(length) {\n if (length === 0) return\n\n const head = 2 * length - 2\n const gaps = new Set()\n\n // TODO: make a function for this in flat-tree\n for (const index of flat.fullRoots(head + 2)) {\n const left = flat.leftSpan(index)\n if (left !== 0) gaps.add(left - 1)\n }\n\n for (let i = 0; i < this.nodes.length; i++) {\n const index = this.nodes[i].index\n if (index > head || gaps.has(index)) continue\n const last = this.nodes.pop()\n if (i < this.nodes.length) this.nodes[i--] = last\n }\n }\n\n clone() {\n const b = new MerkleTreeBatch(this.session)\n\n b.fork = this.fork\n b.roots = [...this.roots]\n b.length = this.length\n b.byteLength = this.byteLength\n b.signature = this.signature\n b.treeLength = this.treeLength\n b.treeFork = this.treeFork\n b.tree = this.tree\n b.nodes = [...this.nodes]\n b.upgraded = this.upgraded\n\n return b\n }\n\n hash() {\n if (this.hashCached === null) this.hashCached = unslab(crypto.tree(this.roots))\n return this.hashCached\n }\n\n signable(manifestHash) {\n return caps.treeSignable(manifestHash, this.hash(), this.length, this.fork)\n }\n\n signableCompat(noHeader) {\n return caps.treeSignableCompat(this.hash(), this.length, this.fork, noHeader)\n }\n\n get(index) {\n if (index >= this.length * 2) {\n return null\n }\n\n for (const n of this.nodes) {\n if (n.index === index) return n\n }\n\n return getTreeNodeFromStorage(this.session.storage, index)\n }\n\n // deprecated, use sssion proof instead\n proof(batch, { block, hash, seek, upgrade }) {\n return generateProof(this.session, batch, block, hash, seek, upgrade)\n }\n\n verifyUpgrade(proof) {\n const unverified = verifyTree(proof, this.nodes)\n\n if (!proof.upgrade) throw INVALID_OPERATION('Expected upgrade proof')\n\n return verifyUpgrade(proof, unverified, this)\n }\n\n addNodesUnsafe(nodes) {\n for (let i = 0; i < nodes.length; i++) {\n this.nodes.push(nodes[i])\n }\n }\n\n append(buf) {\n const head = this.length * 2\n const ite = flat.iterator(head)\n const node = blockNode(head, buf)\n\n this.appendRoot(node, ite)\n }\n\n appendRoot(node, ite) {\n node = unslabNode(node)\n this.hashCached = null\n this.upgraded = true\n this.length += ite.factor / 2\n this.byteLength += node.size\n this.roots.push(node)\n this.nodes.push(node)\n\n while (this.roots.length > 1) {\n const a = this.roots[this.roots.length - 1]\n const b = this.roots[this.roots.length - 2]\n\n // TODO: just have a peek sibling instead? (pretty sure it's always the left sib as well)\n if (ite.sibling() !== b.index) {\n ite.sibling() // unset so it always points to last root\n break\n }\n\n const node = unslabNode(parentNode(ite.parent(), a, b))\n this.nodes.push(node)\n this.roots.pop()\n this.roots.pop()\n this.roots.push(node)\n }\n }\n\n commitable() {\n return (\n this.treeFork === this.session.fork &&\n (this.upgraded\n ? this.treeLength === this.session.length\n : this.treeLength <= this.session.length)\n )\n }\n\n commit(tx) {\n if (tx === undefined) throw INVALID_OPERATION('No database batch was passed')\n if (!this.commitable()) {\n throw INVALID_OPERATION('Tree was modified during batch, refusing to commit')\n }\n\n if (this.upgraded) this._commitUpgrade(tx)\n\n for (let i = 0; i < this.nodes.length; i++) {\n const node = this.nodes[i]\n tx.putTreeNode(node)\n }\n\n this.committed = true\n\n return this\n }\n\n _commitUpgrade(tx) {\n // TODO: If easy to detect, we should refuse an trunc+append here without a fork id\n // change. Will only happen on user error so mostly to prevent that.\n\n if (this.ancestors < this.treeLength) {\n tx.deleteTreeNodeRange(this.ancestors * 2, this.treeLength * 2)\n\n if (this.ancestors > 0) {\n const head = this.ancestors * 2\n const ite = flat.iterator(head - 2)\n\n while (true) {\n if (ite.contains(head) && ite.index < head) {\n tx.deleteTreeNode(ite.index)\n }\n if (ite.offset === 0) break\n ite.parent()\n }\n\n this.truncated = true\n }\n }\n }\n\n seek(bytes, padding) {\n return new ByteSeeker(this, this, bytes, padding)\n }\n\n byteRange(index) {\n const rx = this.storage.read()\n const range = getByteRange(this, index, rx)\n rx.tryFlush()\n\n return range\n }\n\n byteOffset(index) {\n if (index === 2 * this.length) return this.byteLength\n\n const rx = this.storage.read()\n const offset = getByteOffset(this, index, rx)\n rx.tryFlush()\n\n return offset\n }\n\n async downgrade() {\n if (!this.upgraded) return true\n\n const rx = this.storage.read()\n const nodePromises = []\n\n for (const r of this.roots) {\n nodePromises.push(rx.getTreeNode(r.index))\n }\n\n rx.tryFlush()\n\n const nodes = await Promise.all(nodePromises)\n\n for (let i = 0; i < this.roots.length; i++) {\n const root = this.roots[i]\n const node = nodes[i]\n\n if (!node) return false\n if (!b4a.equals(node.hash, root.hash)) return false\n }\n\n this.upgraded = false\n return true\n }\n\n async restore(length) {\n if (length === this.length) return this\n\n const roots = unslabNodes(await MerkleTree.getRootsFromStorage(this.storage, length))\n\n this.roots = roots\n this.length = length\n this.byteLength = totalSize(roots)\n this.ancestors = length\n\n for (const node of roots) this.byteLength += node.size\n\n return this\n }\n}\n\nclass ReorgBatch extends MerkleTreeBatch {\n constructor(session) {\n super(session)\n\n this.roots = []\n this.length = 0\n this.byteLength = 0\n this.diff = null\n this.ancestors = 0\n // We set upgraded because reorgs are signed so hit will\n // hit the same code paths (like the treeLength check in commit)\n this.upgraded = true\n this.want = {\n nodes: 0,\n start: 0,\n end: 0\n }\n }\n\n get finished() {\n return this.want === null\n }\n\n update(proof) {\n if (this.want === null) return true\n\n const nodes = []\n const root = verifyTree(proof, nodes)\n\n if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false\n\n this.nodes.push(...nodes)\n return this._update(nodes)\n }\n\n async _update(nodes) {\n const n = new Map()\n for (const node of nodes) n.set(node.index, node)\n\n let diff = null\n const ite = flat.iterator(this.diff.index)\n const startingDiff = this.diff\n\n while ((ite.index & 1) !== 0) {\n const left = n.get(ite.leftChild())\n if (!left) break\n\n const existing = await getTreeNodeFromStorage(this.session.storage, left.index)\n if (!existing || !b4a.equals(existing.hash, left.hash)) {\n diff = left\n } else {\n diff = n.get(ite.sibling())\n }\n }\n\n if ((this.diff.index & 1) === 0) return true\n if (diff === null) return false\n if (startingDiff !== this.diff) return false\n\n return this._updateDiffRoot(diff)\n }\n\n _updateDiffRoot(diff) {\n if (this.want === null) return true\n\n const spans = flat.spans(diff.index)\n const start = spans[0] / 2\n const end = Math.min(this.treeLength, spans[1] / 2 + 1)\n const len = end - start\n\n this.ancestors = start\n this.diff = diff\n\n if ((diff.index & 1) === 0 || this.want.start >= this.treeLength || len <= 0) {\n this.want = null\n return true\n }\n\n this.want.start = start\n this.want.end = end\n this.want.nodes = log2(spans[1] - spans[0] + 2) - 1\n\n return false\n }\n}\n\nclass ByteSeeker {\n constructor(session, bytes, padding = 0) {\n this.session = session\n this.bytes = bytes\n this.padding = padding\n\n const size = session.byteLength - session.length * padding\n\n this.start = bytes >= size ? session.length : 0\n this.end = bytes < size ? session.length : 0\n }\n\n async _seek(bytes) {\n if (!bytes) return [0, 0]\n\n for (const node of this.session.roots) {\n // all async ticks happen once we find the root so safe\n const size = getUnpaddedSize(node, this.padding, null)\n\n if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]\n if (bytes > size) {\n bytes -= size\n continue\n }\n\n const ite = flat.iterator(node.index)\n\n while ((ite.index & 1) !== 0) {\n const l = await getTreeNodeFromStorage(this.session.storage, ite.leftChild())\n\n if (l) {\n const size = getUnpaddedSize(l, this.padding, ite)\n\n if (size === bytes) return [ite.rightSpan() + 2, 0]\n if (size > bytes) continue\n bytes -= size\n ite.sibling()\n } else {\n ite.parent()\n return [ite.index, bytes]\n }\n }\n\n return [ite.index, bytes]\n }\n\n return null\n }\n\n async update() {\n // TODO: combine _seek and this, much simpler\n const res = await this._seek(this.bytes)\n if (!res) return null\n if ((res[0] & 1) === 0) return [res[0] / 2, res[1]]\n\n const span = flat.spans(res[0])\n this.start = span[0] / 2\n this.end = span[1] / 2 + 1\n\n return null\n }\n}\n\nclass TreeProof {\n constructor(session, block, hash, seek, upgrade) {\n this.fork = session.fork\n this.signature = session.signature\n\n this.block = block\n this.hash = hash\n this.seek = seek\n this.upgrade = upgrade\n\n this.pending = {\n node: null,\n seek: null,\n upgrade: null,\n additionalUpgrade: null\n }\n }\n\n async settle() {\n const result = {\n fork: this.fork,\n block: null,\n hash: null,\n seek: null,\n upgrade: null,\n manifest: null\n }\n\n const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(this.pending)\n\n if (this.block) {\n if (pNode === null) throw INVALID_OPERATION('Invalid block request')\n result.block = {\n index: this.block.index,\n value: null, // populated upstream, alloc it here for simplicity\n nodes: pNode\n }\n } else if (this.hash) {\n if (pNode === null) throw INVALID_OPERATION('Invalid block request')\n result.hash = {\n index: this.hash.index,\n nodes: pNode\n }\n }\n\n if (this.seek && pSeek !== null) {\n result.seek = {\n bytes: this.seek.bytes,\n nodes: pSeek\n }\n }\n\n if (this.upgrade) {\n result.upgrade = {\n start: this.upgrade.start,\n length: this.upgrade.length,\n nodes: pUpgrade,\n additionalNodes: pAdditional || [],\n signature: this.signature\n }\n }\n\n return result\n }\n}\n\nclass MerkleTree {\n static hash(s) {\n return unslab(crypto.tree(s.roots))\n }\n\n static signable(s, namespace) {\n return caps.treeSignable(namespace, MerkleTree.hash(s), s.length, s.fork)\n }\n\n static size(roots) {\n return totalSize(roots)\n }\n\n static span(roots) {\n return totalSpan(roots)\n }\n\n static getRoots(session, length) {\n return MerkleTree.getRootsFromStorage(session.storage, length)\n }\n\n static getRootsFromStorage(storage, length) {\n const indexes = flat.fullRoots(2 * length)\n const roots = new Array(indexes.length)\n const rx = storage.read()\n\n for (let i = 0; i < indexes.length; i++) {\n roots[i] = getTreeNodeOrError(rx, indexes[i])\n }\n\n rx.tryFlush()\n\n return Promise.all(roots)\n }\n\n static async upgradeable(session, length) {\n const indexes = flat.fullRoots(2 * length)\n const roots = new Array(indexes.length)\n const rx = session.storage.read()\n\n for (let i = 0; i < indexes.length; i++) {\n roots[i] = rx.getTreeNode(indexes[i])\n }\n\n rx.tryFlush()\n\n for (const node of await Promise.all(roots)) {\n if (node === null) return false\n }\n\n return true\n }\n\n static seek(session, bytes, padding) {\n return new ByteSeeker(session, bytes, padding)\n }\n\n static get(session, index) {\n return getTreeNodeFromStorage(session.storage, index)\n }\n\n static async truncate(session, length, batch, fork = batch.fork) {\n const head = length * 2\n const fullRoots = flat.fullRoots(head)\n\n for (let i = 0; i < fullRoots.length; i++) {\n const root = fullRoots[i]\n if (i < batch.roots.length && batch.roots[i].index === root) continue\n\n while (batch.roots.length > i) batch.roots.pop()\n batch.roots.push(unslabNode(await getTreeNodeFromStorageOrError(session.storage, root)))\n }\n\n while (batch.roots.length > fullRoots.length) {\n batch.roots.pop()\n }\n\n batch.fork = fork\n batch.length = length\n batch.ancestors = length\n batch.byteLength = totalSize(batch.roots)\n batch.upgraded = true\n\n return batch\n }\n\n static async reorg(session, proof, batch) {\n let unverified = null\n\n if (proof.block || proof.hash || proof.seek) {\n unverified = verifyTree(proof, batch.nodes)\n }\n\n if (!verifyUpgrade(proof, unverified, batch)) {\n throw INVALID_PROOF('Fork proof not verifiable')\n }\n\n for (const root of batch.roots) {\n const existing = await getTreeNodeFromStorage(session.storage, root.index)\n if (existing && b4a.equals(existing.hash, root.hash)) continue\n batch._updateDiffRoot(root)\n break\n }\n\n if (batch.diff !== null) {\n await batch._update(batch.nodes)\n } else {\n batch.want = null\n batch.ancestors = batch.length\n }\n\n return batch\n }\n\n static verifyFullyRemote(session, proof) {\n // TODO: impl this less hackishly\n const batch = new MerkleTreeBatch(session)\n\n batch.fork = proof.fork\n batch.roots = []\n batch.length = 0\n batch.ancestors = 0\n batch.byteLength = 0\n\n let unverified = verifyTree(proof, batch.nodes)\n\n if (proof.upgrade) {\n if (verifyUpgrade(proof, unverified, batch)) {\n unverified = null\n }\n }\n\n return batch\n }\n\n static async verify(session, proof) {\n const batch = new MerkleTreeBatch(session)\n\n let unverified = verifyTree(proof, batch.nodes)\n\n if (proof.upgrade) {\n if (verifyUpgrade(proof, unverified, batch)) {\n unverified = null\n }\n }\n\n if (unverified) {\n const verified = await getTreeNodeFromStorageOrError(session.storage, unverified.index)\n if (!b4a.equals(verified.hash, unverified.hash)) {\n throw INVALID_CHECKSUM('Invalid checksum at node ' + unverified.index)\n }\n }\n\n return batch\n }\n\n static proof(session, rx, { block, hash, seek, upgrade }) {\n return generateProof(session, rx, block, hash, seek, upgrade)\n }\n\n static maxMissingNodes(index, length) {\n const head = 2 * length\n const ite = flat.iterator(index)\n\n // See iterator.rightSpan()\n const iteRightSpan = ite.index + ite.factor / 2 - 1\n // If the index is not in the current tree, we do not know how many missing nodes there are...\n if (iteRightSpan >= head) return 0\n\n for (const r of flat.fullRoots(head)) {\n if (r === index) return 0\n }\n\n // need missing nodes not inclusive of index\n ite.parent()\n\n let cnt = 0\n while (!ite.contains(head)) {\n cnt++\n ite.parent()\n }\n\n return cnt\n }\n\n static async missingNodes(session, index, length) {\n const head = 2 * length\n const ite = flat.iterator(index)\n\n // See iterator.rightSpan()\n const iteRightSpan = ite.index + ite.factor / 2 - 1\n // If the index is not in the current tree, we do not know how many missing nodes there are...\n if (iteRightSpan >= head) return 0\n\n let cnt = 0\n // TODO: we could prop use a read batch here and do this in blocks of X for perf\n while (!ite.contains(head) && !(await hasTreeNode(session.storage, ite.index))) {\n cnt++\n if (cnt >= 1024) {\n throw ASSERTION('Bad arguments to missingNodes index=' + index + ' at length=' + length)\n }\n ite.parent()\n }\n\n return cnt\n }\n\n static byteOffset(session, index) {\n return getByteOffsetSession(session, index, null)\n }\n\n static byteRange(session, index) {\n const rx = session.storage.read()\n const offset = getByteOffsetSession(session, index, rx)\n const size = getNodeSize(index, rx)\n rx.tryFlush()\n return Promise.all([offset, size])\n }\n}\n\nmodule.exports = {\n MerkleTreeBatch,\n ReorgBatch,\n MerkleTree\n}\n\nasync function getNodeSize(index, rx) {\n return (await getTreeNodeOrError(rx, index)).size\n}\n\nasync function getByteOffsetSession(session, index, rx) {\n if (index === 2 * session.length) return session.byteLength\n\n const treeNodes =\n rx === null\n ? await getByteOffsetBatchFlush(session.roots, index, session.storage.read())\n : await getByteOffsetBatch(session.roots, index, rx)\n\n let offset = 0\n for (const node of treeNodes) offset += node.size\n\n return offset\n}\n\nasync function getByteOffset(tree, index, rx) {\n if (index === 2 * tree.length) return tree.byteLength\n\n const treeNodes = await getByteOffsetBatch(tree.roots, index, rx)\n\n let offset = 0\n for (const node of treeNodes) offset += node.size\n\n return offset\n}\n\nfunction getByteOffsetBatchFlush(roots, index, rx) {\n const treeNodes = getByteOffsetBatch(roots, index, rx)\n rx.tryFlush()\n return treeNodes\n}\n\nfunction getByteOffsetBatch(roots, index, rx) {\n if ((index & 1) === 1) index = flat.leftSpan(index)\n\n let head = 0\n let cnt = 0\n\n const promises = []\n\n for (const node of roots) {\n // all async ticks happen once we find the root so safe\n head += 2 * (node.index - head + 1)\n\n if (index >= head) {\n promises.push(node.size)\n continue\n }\n\n const ite = flat.iterator(node.index)\n\n while (ite.index !== index) {\n if (++cnt >= 1024) throw ASSERTION('Bad getByteOffsetSession index=' + index)\n\n if (index < ite.index) {\n ite.leftChild()\n } else {\n promises.push(getTreeNodeOrError(rx, ite.leftChild()))\n ite.sibling()\n }\n }\n\n return Promise.all(promises)\n }\n\n throw ASSERTION('Failed to find offset')\n}\n\nfunction getByteRange(tree, index, rx) {\n const head = 2 * tree.length\n if (((index & 1) === 0 ? index : flat.rightSpan(index)) >= head) {\n throw BAD_ARGUMENT('Index is out of bounds')\n }\n\n const offset = getByteOffset(tree, index, rx)\n const size = getNodeSize(index, rx)\n\n return Promise.all([offset, size])\n}\n\n// All the methods needed for proof verification\n\nfunction verifyTree({ block, hash, seek }, nodes) {\n const untrustedNode = block\n ? { index: 2 * block.index, value: block.value, nodes: block.nodes }\n : hash\n ? { index: hash.index, value: null, nodes: hash.nodes }\n : null\n\n if (untrustedNode === null && (!seek || !seek.nodes.length)) return null\n\n let root = null\n\n if (seek && seek.nodes.length) {\n const ite = flat.iterator(seek.nodes[0].index)\n const q = new NodeQueue(seek.nodes)\n\n root = q.shift(ite.index)\n nodes.push(root)\n\n while (q.length > 0) {\n const node = q.shift(ite.sibling())\n\n root = parentNode(ite.parent(), root, node)\n nodes.push(node)\n nodes.push(root)\n }\n }\n\n if (untrustedNode === null) return root\n\n const ite = flat.iterator(untrustedNode.index)\n const blockHash = untrustedNode.value && blockNode(ite.index, untrustedNode.value)\n\n const q = new NodeQueue(untrustedNode.nodes, root)\n\n root = blockHash || q.shift(ite.index)\n nodes.push(root)\n\n while (q.length > 0) {\n const node = q.shift(ite.sibling())\n\n root = parentNode(ite.parent(), root, node)\n nodes.push(node)\n nodes.push(root)\n }\n\n return root\n}\n\nfunction verifyUpgrade({ fork, upgrade }, blockRoot, batch) {\n const prologue = batch.prologue\n\n if (prologue) {\n const { start, length } = upgrade\n if (start < prologue.length && (start !== 0 || length < prologue.length)) {\n throw INVALID_PROOF('Upgrade does not satisfy prologue')\n }\n }\n\n const q = new NodeQueue(upgrade.nodes, blockRoot)\n\n let grow = batch.roots.length > 0\n let i = 0\n\n const to = 2 * (upgrade.start + upgrade.length)\n const ite = flat.iterator(0)\n\n for (; ite.fullRoot(to); ite.nextTree()) {\n if (i < batch.roots.length && batch.roots[i].index === ite.index) {\n i++\n continue\n }\n\n if (grow) {\n grow = false\n const root = ite.index\n if (i < batch.roots.length) {\n ite.seek(batch.roots[batch.roots.length - 1].index)\n while (ite.index !== root) {\n batch.appendRoot(q.shift(ite.sibling()), ite)\n }\n continue\n }\n }\n\n batch.appendRoot(q.shift(ite.index), ite)\n }\n\n if (prologue && batch.length === prologue.length) {\n if (!b4a.equals(prologue.hash, batch.hash())) {\n throw INVALID_PROOF('Invalid hash')\n }\n }\n\n const extra = upgrade.additionalNodes\n\n ite.seek(batch.roots[batch.roots.length - 1].index)\n i = 0\n\n while (i < extra.length && extra[i].index === ite.sibling()) {\n batch.appendRoot(extra[i++], ite)\n }\n\n while (i < extra.length) {\n const node = extra[i++]\n\n while (node.index !== ite.index) {\n if (ite.factor === 2) throw INVALID_OPERATION('Unexpected node: ' + node.index)\n ite.leftChild()\n }\n\n batch.appendRoot(node, ite)\n ite.sibling()\n }\n\n batch.signature = unslab(upgrade.signature)\n batch.fork = fork\n\n return q.extra === null\n}\n\nasync function seekFromHead(session, head, bytes, padding) {\n const roots = flat.fullRoots(head)\n\n for (let i = 0; i < roots.length; i++) {\n const root = roots[i]\n const node = await getTreeNodeFromStorage(session.storage, root)\n const size = getUnpaddedSize(node, padding, null)\n\n if (bytes === size) return root\n if (bytes > size) {\n bytes -= size\n continue\n }\n\n return seekTrustedTree(session, root, bytes, padding)\n }\n\n return head\n}\n\n// trust that bytes are within the root tree and find the block at bytes\n\nasync function seekTrustedTree(session, root, bytes, padding) {\n if (!bytes) return root\n\n const ite = flat.iterator(root)\n let cnt = 0\n\n while ((ite.index & 1) !== 0) {\n if (++cnt >= 1024) throw ASSERTION('Bad seekTrusted bytes=' + bytes + ', paddding=' + padding)\n const l = await getTreeNodeFromStorage(session.storage, ite.leftChild())\n\n if (l) {\n const size = getUnpaddedSize(l, padding, ite)\n if (size === bytes) return ite.index\n if (size > bytes) continue\n bytes -= size\n ite.sibling()\n } else {\n ite.parent()\n return ite.index\n }\n }\n\n return ite.index\n}\n\n// try to find the block at bytes without trusting that is *is* within the root passed\n\nasync function seekUntrustedTree(session, root, bytes, padding) {\n const offset =\n (await getByteOffsetSession(session, root, null)) -\n (padding ? (padding * flat.leftSpan(root)) / 2 : 0)\n\n if (offset > bytes) throw INVALID_OPERATION('Invalid seek')\n if (offset === bytes) return root\n\n bytes -= offset\n\n const node = await getTreeNodeFromStorageOrError(session.storage, root)\n\n if (getUnpaddedSize(node, padding, null) <= bytes) throw INVALID_OPERATION('Invalid seek')\n\n return seekTrustedTree(session, root, bytes, padding)\n}\n\n// Below is proof production, ie, construct proofs to verify a request\n// Note, that all these methods are sync as we can statically infer which nodes\n// are needed for the remote to verify given they arguments they passed us\n\nfunction seekProof(session, rx, seekRoot, root, p) {\n const ite = flat.iterator(seekRoot)\n let cnt = 0\n\n p.seek = []\n p.seek.push(getTreeNodeOrError(rx, ite.index))\n\n while (ite.index !== root) {\n if (++cnt >= 1024) throw ASSERTION('Bad seekProof seekRoot=' + seekRoot + ', root=' + root)\n ite.sibling()\n p.seek.push(getTreeNodeOrError(rx, ite.index))\n ite.parent()\n }\n}\n\nfunction blockAndSeekProof(session, rx, node, seek, seekRoot, root, p) {\n if (!node) return seekProof(session, rx, seekRoot, root, p)\n\n const ite = flat.iterator(node.index)\n let cnt = 0\n\n p.node = []\n if (!node.value) p.node.push(getTreeNodeOrError(rx, ite.index))\n\n while (ite.index !== root) {\n if (++cnt >= 1024) {\n throw ASSERTION('Bad blockAndSeekProof seekRoot=' + seekRoot + ', root=' + root)\n }\n ite.sibling()\n\n if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {\n seekProof(session, rx, seekRoot, ite.index, p)\n } else {\n p.node.push(getTreeNodeOrError(rx, ite.index))\n }\n\n ite.parent()\n }\n}\n\nfunction upgradeProof(session, rx, node, seek, from, to, subTree, p) {\n if (from === 0) p.upgrade = []\n\n for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {\n // check if they already have the node\n if (ite.index + ite.factor / 2 < from) continue\n\n // connect existing tree\n if (p.upgrade === null && ite.contains(from - 2)) {\n p.upgrade = []\n\n const root = ite.index\n const target = from - 2\n let cnt = 0\n\n ite.seek(target)\n\n while (ite.index !== root) {\n if (++cnt >= 1024) throw ASSERTION('Bad upgradeProof target=' + target + ', root=' + root)\n\n ite.sibling()\n if (ite.index > target) {\n if (p.node === null && p.seek === null && ite.contains(subTree)) {\n blockAndSeekProof(session, rx, node, seek, subTree, ite.index, p)\n } else {\n p.upgrade.push(getTreeNodeOrError(rx, ite.index))\n }\n }\n ite.parent()\n }\n\n continue\n }\n\n if (p.upgrade === null) {\n p.upgrade = []\n }\n\n // if the subtree included is a child of this tree, include that one\n // instead of a dup node\n if (p.node === null && p.seek === null && ite.contains(subTree)) {\n blockAndSeekProof(session, rx, node, seek, subTree, ite.index, p)\n continue\n }\n\n // add root (can be optimised since the root might be in tree.roots)\n p.upgrade.push(getTreeNodeOrError(rx, ite.index))\n }\n}\n\nfunction additionalUpgradeProof(session, rx, from, to, p) {\n if (from === 0) p.additionalUpgrade = []\n\n for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {\n // check if they already have the node\n if (ite.index + ite.factor / 2 < from) continue\n\n // connect existing tree\n if (p.additionalUpgrade === null && ite.contains(from - 2)) {\n p.additionalUpgrade = []\n\n const root = ite.index\n const target = from - 2\n let cnt = 0\n\n ite.seek(target)\n\n while (ite.index !== root) {\n if (++cnt >= 1024) {\n throw ASSERTION(\n 'Bad arguments to additionalUpgradeProof root=' + root + ' target=' + target\n )\n }\n ite.sibling()\n if (ite.index > target) {\n p.additionalUpgrade.push(getTreeNodeOrError(rx, ite.index))\n }\n ite.parent()\n }\n\n continue\n }\n\n if (p.additionalUpgrade === null) {\n p.additionalUpgrade = []\n }\n\n // add root (can be optimised since the root is in tree.roots)\n p.additionalUpgrade.push(getTreeNodeOrError(rx, ite.index))\n }\n}\n\nfunction nodesToRoot(index, nodes, head) {\n const ite = flat.iterator(index)\n\n for (let i = 0; i < nodes; i++) {\n ite.parent()\n if (ite.contains(head)) throw INVALID_OPERATION('Nodes is out of bounds')\n }\n\n return ite.index\n}\n\nfunction totalSize(nodes) {\n let s = 0\n for (const node of nodes) s += node.size\n return s\n}\n\nfunction totalSpan(nodes) {\n let s = 0\n for (const node of nodes) s += 2 * (node.index - s + 1)\n return s\n}\n\nfunction blockNode(index, value) {\n return { index, size: value.byteLength, hash: crypto.data(value) }\n}\n\nfunction parentNode(index, a, b) {\n return { index, size: a.size + b.size, hash: crypto.parent(a, b) }\n}\n\nfunction log2(n) {\n let res = 1\n\n while (n > 2) {\n n /= 2\n res++\n }\n\n return res\n}\n\nfunction normalizeIndexed(block, hash) {\n if (block) {\n return {\n value: true,\n index: block.index * 2,\n nodes: block.nodes,\n lastIndex: block.index\n }\n }\n if (hash) {\n return {\n value: false,\n index: hash.index,\n nodes: hash.nodes,\n lastIndex: flat.rightSpan(hash.index) / 2\n }\n }\n return null\n}\n\nasync function getTreeNodeOrError(rx, index) {\n const node = await rx.getTreeNode(index)\n if (node === null) {\n throw INVALID_OPERATION('Expected tree node ' + index + ' from storage, got (nil)')\n }\n return node\n}\n\nfunction getTreeNodeFromStorageOrError(storage, index) {\n const rx = storage.read()\n const p = getTreeNodeOrError(rx, index)\n rx.tryFlush()\n return p\n}\n\nfunction getTreeNodeFromStorage(storage, index) {\n const rx = storage.read()\n const node = rx.getTreeNode(index)\n rx.tryFlush()\n return node\n}\n\nfunction hasTreeNode(storage, index) {\n const rx = storage.read()\n const has = rx.hasTreeNode(index)\n rx.tryFlush()\n return has\n}\n\nasync function settleProof(p) {\n const result = [\n p.node && Promise.all(p.node),\n p.seek && Promise.all(p.seek),\n p.upgrade && Promise.all(p.upgrade),\n p.additionalUpgrade && Promise.all(p.additionalUpgrade)\n ]\n\n try {\n return await Promise.all(result)\n } catch (err) {\n if (p.node) await Promise.allSettled(p.node)\n if (p.seek) await Promise.allSettled(p.seek)\n if (p.upgrade) await Promise.allSettled(p.upgrade)\n if (p.additionalUpgrade) await Promise.allSettled(p.additionalUpgrade)\n throw err\n }\n}\n\n// tree can be either the merkle tree or a merkle tree batch\nasync function generateProof(session, rx, block, hash, seek, upgrade) {\n // Important that this does not throw inbetween making the promise arrays\n // and finalise being called, otherwise there will be lingering promises in the background\n\n if (session.prologue && upgrade) {\n upgrade.start = upgrade.start < session.prologue.length ? 0 : upgrade.start\n upgrade.length =\n upgrade.start < session.prologue.length ? session.prologue.length : upgrade.length\n }\n\n const head = 2 * session.length\n const from = upgrade ? upgrade.start * 2 : 0\n const to = upgrade ? from + upgrade.length * 2 : head\n const node = normalizeIndexed(block, hash)\n\n // can't do anything as we have no data...\n if (head === 0) return new TreeProof(session, null, null, null, null)\n\n if (from >= to || to > head) {\n throw INVALID_OPERATION('Invalid upgrade')\n }\n if (seek && upgrade && node !== null && node.index >= from) {\n throw INVALID_OPERATION('Cannot both do a seek and block/hash request when upgrading')\n }\n\n let subTree = head\n\n const p = new TreeProof(session, block, hash, seek, upgrade)\n\n if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {\n subTree = nodesToRoot(node.index, node.nodes, to)\n const seekRoot = seek\n ? await seekUntrustedTree(session, subTree, seek.bytes, seek.padding)\n : head\n blockAndSeekProof(session, rx, node, seek, seekRoot, subTree, p.pending)\n } else if ((node || seek) && upgrade) {\n subTree = seek ? await seekFromHead(session, to, seek.bytes, seek.padding) : node.index\n }\n\n if (upgrade) {\n upgradeProof(session, rx, node, seek, from, to, subTree, p.pending)\n if (head > to) additionalUpgradeProof(session, rx, to, head, p.pending)\n }\n\n return p\n}\n\nfunction getUnpaddedSize(node, padding, ite) {\n return padding === 0\n ? node.size\n : node.size - padding * (ite ? ite.countLeaves() : flat.countLeaves(node.index))\n}\n\nfunction unslabNodes(nodes) {\n for (const node of nodes) unslabNode(node)\n return nodes\n}\n\nfunction unslabNode(node) {\n if (node === null) return node\n node.hash = unslab(node.hash)\n return node\n}\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst { DEFAULT_NAMESPACE } = require('./caps')\nconst { INVALID_OPLOG_VERSION } = require('hypercore-errors')\nconst unslab = require('unslab')\n\nconst EMPTY = b4a.alloc(0)\n\nconst MANIFEST_PATCH = 0b00000001\nconst MANIFEST_PROLOGUE = 0b00000010\nconst MANIFEST_LINKED = 0b00000100\nconst MANIFEST_USER_DATA = 0b00001000\n\nconst hashes = {\n preencode(state, m) {\n state.end++ // small uint\n },\n encode(state, m) {\n if (m === 'blake2b') {\n c.uint.encode(state, 0)\n return\n }\n\n throw new Error('Unknown hash: ' + m)\n },\n decode(state) {\n const n = c.uint.decode(state)\n if (n === 0) return 'blake2b'\n throw new Error('Unknown hash id: ' + n)\n }\n}\n\nconst signatures = {\n preencode(state, m) {\n state.end++ // small uint\n },\n encode(state, m) {\n if (m === 'ed25519') {\n c.uint.encode(state, 0)\n return\n }\n\n throw new Error('Unknown signature: ' + m)\n },\n decode(state) {\n const n = c.uint.decode(state)\n if (n === 0) return 'ed25519'\n throw new Error('Unknown signature id: ' + n)\n }\n}\n\nconst signer = {\n preencode(state, m) {\n signatures.preencode(state, m.signature)\n c.fixed32.preencode(state, m.namespace)\n c.fixed32.preencode(state, m.publicKey)\n },\n encode(state, m) {\n signatures.encode(state, m.signature)\n c.fixed32.encode(state, m.namespace)\n c.fixed32.encode(state, m.publicKey)\n },\n decode(state) {\n return {\n signature: signatures.decode(state),\n namespace: c.fixed32.decode(state),\n publicKey: c.fixed32.decode(state)\n }\n }\n}\n\nconst signerArray = c.array(signer)\n\nconst prologue = {\n preencode(state, p) {\n c.fixed32.preencode(state, p.hash)\n c.uint.preencode(state, p.length)\n },\n encode(state, p) {\n c.fixed32.encode(state, p.hash)\n c.uint.encode(state, p.length)\n },\n decode(state) {\n return {\n hash: c.fixed32.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst manifestv0 = {\n preencode(state, m) {\n hashes.preencode(state, m.hash)\n state.end++ // type\n\n if (m.prologue && m.signers.length === 0) {\n c.fixed32.preencode(state, m.prologue.hash)\n return\n }\n\n if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) {\n signer.preencode(state, m.signers[0])\n } else {\n state.end++ // flags\n c.uint.preencode(state, m.quorum)\n signerArray.preencode(state, m.signers)\n }\n },\n encode(state, m) {\n hashes.encode(state, m.hash)\n\n if (m.prologue && m.signers.length === 0) {\n c.uint.encode(state, 0)\n c.fixed32.encode(state, m.prologue.hash)\n return\n }\n\n if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) {\n c.uint.encode(state, 1)\n signer.encode(state, m.signers[0])\n } else {\n c.uint.encode(state, 2)\n c.uint.encode(state, m.allowPatch ? 1 : 0)\n c.uint.encode(state, m.quorum)\n signerArray.encode(state, m.signers)\n }\n },\n decode(state) {\n const hash = hashes.decode(state)\n const type = c.uint.decode(state)\n\n if (type > 2) throw new Error('Unknown type: ' + type)\n\n if (type === 0) {\n return {\n version: 0,\n hash,\n allowPatch: false,\n quorum: 0,\n signers: [],\n prologue: {\n hash: c.fixed32.decode(state),\n length: 0\n },\n linked: null,\n userData: null\n }\n }\n\n if (type === 1) {\n return {\n version: 0,\n hash,\n allowPatch: false,\n quorum: 1,\n signers: [signer.decode(state)],\n prologue: null,\n linked: null,\n userData: null\n }\n }\n\n const flags = c.uint.decode(state)\n\n return {\n version: 0,\n hash,\n allowPatch: (flags & 1) !== 0,\n quorum: c.uint.decode(state),\n signers: signerArray.decode(state),\n prologue: null,\n linked: null,\n userData: null\n }\n }\n}\n\nconst fixed32Array = c.array(c.fixed32)\n\nconst manifest = (exports.manifest = {\n preencode(state, m) {\n state.end++ // version\n\n if (m.version === 0) return manifestv0.preencode(state, m)\n\n state.end++ // flags\n hashes.preencode(state, m.hash)\n\n c.uint.preencode(state, m.quorum)\n signerArray.preencode(state, m.signers)\n\n if (m.prologue) prologue.preencode(state, m.prologue)\n if (m.linked) fixed32Array.preencode(state, m.linked)\n if (m.userData) c.buffer.preencode(state, m.userData)\n },\n encode(state, m) {\n c.uint.encode(state, m.version)\n\n if (m.version === 0) return manifestv0.encode(state, m)\n\n let flags = 0\n if (m.allowPatch) flags |= MANIFEST_PATCH\n if (m.prologue) flags |= MANIFEST_PROLOGUE\n if (m.linked) flags |= MANIFEST_LINKED\n if (m.userData) flags |= MANIFEST_USER_DATA\n\n c.uint.encode(state, flags)\n hashes.encode(state, m.hash)\n\n c.uint.encode(state, m.quorum)\n signerArray.encode(state, m.signers)\n\n if (m.prologue) prologue.encode(state, m.prologue)\n if (m.linked) fixed32Array.encode(state, m.linked)\n if (m.userData) c.buffer.encode(state, m.userData)\n },\n decode(state) {\n const version = c.uint.decode(state)\n\n if (version === 0) return manifestv0.decode(state)\n if (version > 2) throw new Error('Unknown version: ' + version)\n\n const flags = c.uint.decode(state)\n const hash = hashes.decode(state)\n const quorum = c.uint.decode(state)\n const signers = signerArray.decode(state)\n\n const hasPatch = (flags & MANIFEST_PATCH) !== 0\n const hasPrologue = (flags & MANIFEST_PROLOGUE) !== 0\n const hasLinked = (flags & MANIFEST_LINKED) !== 0\n const hasUserData = (flags & MANIFEST_USER_DATA) !== 0\n\n return {\n version,\n hash,\n allowPatch: hasPatch,\n quorum,\n signers,\n prologue: hasPrologue ? prologue.decode(state) : null,\n linked: hasLinked ? fixed32Array.decode(state) : null,\n userData: hasUserData ? c.buffer.decode(state) : null\n }\n }\n})\n\nconst node = {\n preencode(state, n) {\n c.uint.preencode(state, n.index)\n c.uint.preencode(state, n.size)\n c.fixed32.preencode(state, n.hash)\n },\n encode(state, n) {\n c.uint.encode(state, n.index)\n c.uint.encode(state, n.size)\n c.fixed32.encode(state, n.hash)\n },\n decode(state) {\n return {\n index: c.uint.decode(state),\n size: c.uint.decode(state),\n hash: c.fixed32.decode(state)\n }\n }\n}\n\nconst nodeArray = c.array(node)\n\nconst wire = (exports.wire = {})\n\nwire.handshake = {\n preencode(state, m) {\n c.uint.preencode(state, 1)\n c.fixed32.preencode(state, m.capability)\n },\n encode(state, m) {\n c.uint.encode(state, m.seeks ? 1 : 0)\n c.fixed32.encode(state, m.capability)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n return {\n seeks: (flags & 1) !== 0,\n capability: unslab(c.fixed32.decode(state))\n }\n }\n}\n\nconst requestBlock = {\n preencode(state, b) {\n c.uint.preencode(state, b.index)\n c.uint.preencode(state, b.nodes)\n },\n encode(state, b) {\n c.uint.encode(state, b.index)\n c.uint.encode(state, b.nodes)\n },\n decode(state) {\n return {\n index: c.uint.decode(state),\n nodes: c.uint.decode(state)\n }\n }\n}\n\nconst requestSeek = {\n preencode(state, s) {\n c.uint.preencode(state, s.bytes)\n c.uint.preencode(state, s.padding)\n },\n encode(state, s) {\n c.uint.encode(state, s.bytes)\n c.uint.encode(state, s.padding)\n },\n decode(state) {\n return {\n bytes: c.uint.decode(state),\n padding: c.uint.decode(state)\n }\n }\n}\n\nconst requestUpgrade = {\n preencode(state, u) {\n c.uint.preencode(state, u.start)\n c.uint.preencode(state, u.length)\n },\n encode(state, u) {\n c.uint.encode(state, u.start)\n c.uint.encode(state, u.length)\n },\n decode(state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nwire.request = {\n preencode(state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.id)\n c.uint.preencode(state, m.fork)\n\n if (m.block) requestBlock.preencode(state, m.block)\n if (m.hash) requestBlock.preencode(state, m.hash)\n if (m.seek) requestSeek.preencode(state, m.seek)\n if (m.upgrade) requestUpgrade.preencode(state, m.upgrade)\n if (m.priority) c.uint.preencode(state, m.priority)\n },\n encode(state, m) {\n const flags =\n (m.block ? 1 : 0) |\n (m.hash ? 2 : 0) |\n (m.seek ? 4 : 0) |\n (m.upgrade ? 8 : 0) |\n (m.manifest ? 16 : 0) |\n (m.priority ? 32 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.id)\n c.uint.encode(state, m.fork)\n\n if (m.block) requestBlock.encode(state, m.block)\n if (m.hash) requestBlock.encode(state, m.hash)\n if (m.seek) requestSeek.encode(state, m.seek)\n if (m.upgrade) requestUpgrade.encode(state, m.upgrade)\n if (m.priority) c.uint.encode(state, m.priority)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n\n return {\n id: c.uint.decode(state),\n fork: c.uint.decode(state),\n block: flags & 1 ? requestBlock.decode(state) : null,\n hash: flags & 2 ? requestBlock.decode(state) : null,\n seek: flags & 4 ? requestSeek.decode(state) : null,\n upgrade: flags & 8 ? requestUpgrade.decode(state) : null,\n manifest: (flags & 16) !== 0,\n priority: flags & 32 ? c.uint.decode(state) : 0\n }\n }\n}\n\nwire.cancel = {\n preencode(state, m) {\n c.uint.preencode(state, m.request)\n },\n encode(state, m) {\n c.uint.encode(state, m.request)\n },\n decode(state, m) {\n return {\n request: c.uint.decode(state)\n }\n }\n}\n\nconst dataUpgrade = {\n preencode(state, u) {\n c.uint.preencode(state, u.start)\n c.uint.preencode(state, u.length)\n nodeArray.preencode(state, u.nodes)\n nodeArray.preencode(state, u.additionalNodes)\n c.buffer.preencode(state, u.signature)\n },\n encode(state, u) {\n c.uint.encode(state, u.start)\n c.uint.encode(state, u.length)\n nodeArray.encode(state, u.nodes)\n nodeArray.encode(state, u.additionalNodes)\n c.buffer.encode(state, u.signature)\n },\n decode(state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state),\n nodes: nodeArray.decode(state),\n additionalNodes: nodeArray.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst dataSeek = {\n preencode(state, s) {\n c.uint.preencode(state, s.bytes)\n nodeArray.preencode(state, s.nodes)\n },\n encode(state, s) {\n c.uint.encode(state, s.bytes)\n nodeArray.encode(state, s.nodes)\n },\n decode(state) {\n return {\n bytes: c.uint.decode(state),\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nconst dataBlock = {\n preencode(state, b) {\n c.uint.preencode(state, b.index)\n c.buffer.preencode(state, b.value)\n nodeArray.preencode(state, b.nodes)\n },\n encode(state, b) {\n c.uint.encode(state, b.index)\n c.buffer.encode(state, b.value)\n nodeArray.encode(state, b.nodes)\n },\n decode(state) {\n return {\n index: c.uint.decode(state),\n value: c.buffer.decode(state) || EMPTY,\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nconst dataHash = {\n preencode(state, b) {\n c.uint.preencode(state, b.index)\n nodeArray.preencode(state, b.nodes)\n },\n encode(state, b) {\n c.uint.encode(state, b.index)\n nodeArray.encode(state, b.nodes)\n },\n decode(state) {\n return {\n index: c.uint.decode(state),\n nodes: nodeArray.decode(state)\n }\n }\n}\n\nwire.data = {\n preencode(state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.request)\n c.uint.preencode(state, m.fork)\n\n if (m.block) dataBlock.preencode(state, m.block)\n if (m.hash) dataHash.preencode(state, m.hash)\n if (m.seek) dataSeek.preencode(state, m.seek)\n if (m.upgrade) dataUpgrade.preencode(state, m.upgrade)\n if (m.manifest) manifest.preencode(state, m.manifest)\n },\n encode(state, m) {\n const flags =\n (m.block ? 1 : 0) |\n (m.hash ? 2 : 0) |\n (m.seek ? 4 : 0) |\n (m.upgrade ? 8 : 0) |\n (m.manifest ? 16 : 0)\n\n c.uint.encode(state, flags)\n c.uint.encode(state, m.request)\n c.uint.encode(state, m.fork)\n\n if (m.block) dataBlock.encode(state, m.block)\n if (m.hash) dataHash.encode(state, m.hash)\n if (m.seek) dataSeek.encode(state, m.seek)\n if (m.upgrade) dataUpgrade.encode(state, m.upgrade)\n if (m.manifest) manifest.encode(state, m.manifest)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n\n return {\n request: c.uint.decode(state),\n fork: c.uint.decode(state),\n block: flags & 1 ? dataBlock.decode(state) : null,\n hash: flags & 2 ? dataHash.decode(state) : null,\n seek: flags & 4 ? dataSeek.decode(state) : null,\n upgrade: flags & 8 ? dataUpgrade.decode(state) : null,\n manifest: flags & 16 ? manifest.decode(state) : null\n }\n }\n}\n\nwire.noData = {\n preencode(state, m) {\n c.uint.preencode(state, m.request)\n c.uint.preencode(state, m.reason ? 1 : 0)\n if (m.reason) c.uint.preencode(state, m.reason)\n },\n encode(state, m) {\n c.uint.encode(state, m.request)\n c.uint.encode(state, m.reason ? 1 : 0)\n if (m.reason) c.uint.encode(state, m.reason)\n },\n decode(state, m) {\n const request = c.uint.decode(state)\n const flags = state.start < state.end ? c.uint.decode(state) : 0\n return {\n request,\n reason: flags & 1 ? c.uint.decode(state) : 0\n }\n }\n}\n\n// TODO: we should move to having these have a handle like in data, easier since they\n// allocate state in practice\nwire.want = {\n preencode(state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n\n const flags = m.any ? 1 : 0\n if (flags !== 0) c.uint.preencode(state, flags)\n },\n encode(state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n\n const flags = m.any ? 1 : 0\n if (flags !== 0) c.uint.encode(state, flags)\n },\n decode(state) {\n const start = c.uint.decode(state)\n const length = c.uint.decode(state)\n const flags = state.start < state.end ? c.uint.decode(state) : 0\n\n return {\n start,\n length,\n any: (flags & 1) !== 0\n }\n }\n}\n\nwire.unwant = {\n preencode(state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n\n const flags = m.any ? 1 : 0\n if (flags !== 0) c.uint.preencode(state, flags)\n },\n encode(state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n\n const flags = m.any ? 1 : 0\n if (flags !== 0) c.uint.encode(state, flags)\n },\n decode(state, m) {\n const start = c.uint.decode(state)\n const length = c.uint.decode(state)\n const flags = state.start < state.end ? c.uint.decode(state) : 0\n\n return {\n start,\n length,\n any: (flags & 1) !== 0\n }\n }\n}\n\nwire.range = {\n preencode(state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.start)\n if (m.length !== 1) c.uint.preencode(state, m.length)\n },\n encode(state, m) {\n c.uint.encode(state, (m.drop ? 1 : 0) | (m.length === 1 ? 2 : 0))\n c.uint.encode(state, m.start)\n if (m.length !== 1) c.uint.encode(state, m.length)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n\n return {\n drop: (flags & 1) !== 0,\n start: c.uint.decode(state),\n length: (flags & 2) !== 0 ? 1 : c.uint.decode(state)\n }\n }\n}\n\nwire.bitfield = {\n preencode(state, m) {\n c.uint.preencode(state, m.start)\n c.uint32array.preencode(state, m.bitfield)\n },\n encode(state, m) {\n c.uint.encode(state, m.start)\n c.uint32array.encode(state, m.bitfield)\n },\n decode(state, m) {\n return {\n start: c.uint.decode(state),\n bitfield: c.uint32array.decode(state)\n }\n }\n}\n\nwire.sync = {\n preencode(state, m) {\n state.end++ // flags\n c.uint.preencode(state, m.fork)\n c.uint.preencode(state, m.length)\n c.uint.preencode(state, m.remoteLength)\n },\n encode(state, m) {\n c.uint.encode(\n state,\n (m.canUpgrade ? 1 : 0) |\n (m.uploading ? 2 : 0) |\n (m.downloading ? 4 : 0) |\n (m.hasManifest ? 8 : 0) |\n (m.allowPush ? 16 : 0)\n )\n c.uint.encode(state, m.fork)\n c.uint.encode(state, m.length)\n c.uint.encode(state, m.remoteLength)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n\n return {\n fork: c.uint.decode(state),\n length: c.uint.decode(state),\n remoteLength: c.uint.decode(state),\n canUpgrade: (flags & 1) !== 0,\n uploading: (flags & 2) !== 0,\n downloading: (flags & 4) !== 0,\n hasManifest: (flags & 8) !== 0,\n allowPush: (flags & 16) !== 0\n }\n }\n}\n\nwire.reorgHint = {\n preencode(state, m) {\n c.uint.preencode(state, m.from)\n c.uint.preencode(state, m.to)\n c.uint.preencode(state, m.ancestors)\n },\n encode(state, m) {\n c.uint.encode(state, m.from)\n c.uint.encode(state, m.to)\n c.uint.encode(state, m.ancestors)\n },\n decode(state) {\n return {\n from: c.uint.encode(state),\n to: c.uint.encode(state),\n ancestors: c.uint.encode(state)\n }\n }\n}\n\nwire.extension = {\n preencode(state, m) {\n c.string.preencode(state, m.name)\n c.raw.preencode(state, m.message)\n },\n encode(state, m) {\n c.string.encode(state, m.name)\n c.raw.encode(state, m.message)\n },\n decode(state) {\n return {\n name: c.string.decode(state),\n message: c.raw.decode(state)\n }\n }\n}\n\nconst keyValue = {\n preencode(state, p) {\n c.string.preencode(state, p.key)\n c.buffer.preencode(state, p.value)\n },\n encode(state, p) {\n c.string.encode(state, p.key)\n c.buffer.encode(state, p.value)\n },\n decode(state) {\n return {\n key: c.string.decode(state),\n value: c.buffer.decode(state)\n }\n }\n}\n\nconst treeUpgrade = {\n preencode(state, u) {\n c.uint.preencode(state, u.fork)\n c.uint.preencode(state, u.ancestors)\n c.uint.preencode(state, u.length)\n c.buffer.preencode(state, u.signature)\n },\n encode(state, u) {\n c.uint.encode(state, u.fork)\n c.uint.encode(state, u.ancestors)\n c.uint.encode(state, u.length)\n c.buffer.encode(state, u.signature)\n },\n decode(state) {\n return {\n fork: c.uint.decode(state),\n ancestors: c.uint.decode(state),\n length: c.uint.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst bitfieldUpdate = {\n // TODO: can maybe be folded into a HAVE later on with the most recent spec\n preencode(state, b) {\n state.end++ // flags\n c.uint.preencode(state, b.start)\n c.uint.preencode(state, b.length)\n },\n encode(state, b) {\n state.buffer[state.start++] = b.drop ? 1 : 0\n c.uint.encode(state, b.start)\n c.uint.encode(state, b.length)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n return {\n drop: (flags & 1) !== 0,\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst oplog = (exports.oplog = {})\n\noplog.entry = {\n preencode(state, m) {\n state.end++ // flags\n if (m.userData) keyValue.preencode(state, m.userData)\n if (m.treeNodes) nodeArray.preencode(state, m.treeNodes)\n if (m.treeUpgrade) treeUpgrade.preencode(state, m.treeUpgrade)\n if (m.bitfield) bitfieldUpdate.preencode(state, m.bitfield)\n },\n encode(state, m) {\n const s = state.start++\n let flags = 0\n\n if (m.userData) {\n flags |= 1\n keyValue.encode(state, m.userData)\n }\n if (m.treeNodes) {\n flags |= 2\n nodeArray.encode(state, m.treeNodes)\n }\n if (m.treeUpgrade) {\n flags |= 4\n treeUpgrade.encode(state, m.treeUpgrade)\n }\n if (m.bitfield) {\n flags |= 8\n bitfieldUpdate.encode(state, m.bitfield)\n }\n\n state.buffer[s] = flags\n },\n decode(state) {\n const flags = c.uint.decode(state)\n return {\n userData: (flags & 1) !== 0 ? keyValue.decode(state) : null,\n treeNodes: (flags & 2) !== 0 ? nodeArray.decode(state) : null,\n treeUpgrade: (flags & 4) !== 0 ? treeUpgrade.decode(state) : null,\n bitfield: (flags & 8) !== 0 ? bitfieldUpdate.decode(state) : null\n }\n }\n}\n\nconst keyPair = {\n preencode(state, kp) {\n c.buffer.preencode(state, kp.publicKey)\n c.buffer.preencode(state, kp.secretKey)\n },\n encode(state, kp) {\n c.buffer.encode(state, kp.publicKey)\n c.buffer.encode(state, kp.secretKey)\n },\n decode(state) {\n return {\n publicKey: c.buffer.decode(state),\n secretKey: c.buffer.decode(state)\n }\n }\n}\n\nconst reorgHint = {\n preencode(state, r) {\n c.uint.preencode(state, r.from)\n c.uint.preencode(state, r.to)\n c.uint.preencode(state, r.ancestors)\n },\n encode(state, r) {\n c.uint.encode(state, r.from)\n c.uint.encode(state, r.to)\n c.uint.encode(state, r.ancestors)\n },\n decode(state) {\n return {\n from: c.uint.decode(state),\n to: c.uint.decode(state),\n ancestors: c.uint.decode(state)\n }\n }\n}\n\nconst reorgHintArray = c.array(reorgHint)\n\nconst hints = {\n preencode(state, h) {\n reorgHintArray.preencode(state, h.reorgs)\n c.uint.preencode(state, h.contiguousLength)\n },\n encode(state, h) {\n reorgHintArray.encode(state, h.reorgs)\n c.uint.encode(state, h.contiguousLength)\n },\n decode(state) {\n return {\n reorgs: reorgHintArray.decode(state),\n contiguousLength: state.start < state.end ? c.uint.decode(state) : 0\n }\n }\n}\n\nconst treeHeader = {\n preencode(state, t) {\n c.uint.preencode(state, t.fork)\n c.uint.preencode(state, t.length)\n c.buffer.preencode(state, t.rootHash)\n c.buffer.preencode(state, t.signature)\n },\n encode(state, t) {\n c.uint.encode(state, t.fork)\n c.uint.encode(state, t.length)\n c.buffer.encode(state, t.rootHash)\n c.buffer.encode(state, t.signature)\n },\n decode(state) {\n return {\n fork: c.uint.decode(state),\n length: c.uint.decode(state),\n rootHash: c.buffer.decode(state),\n signature: c.buffer.decode(state)\n }\n }\n}\n\nconst types = {\n preencode(state, t) {\n c.string.preencode(state, t.tree)\n c.string.preencode(state, t.bitfield)\n c.string.preencode(state, t.signer)\n },\n encode(state, t) {\n c.string.encode(state, t.tree)\n c.string.encode(state, t.bitfield)\n c.string.encode(state, t.signer)\n },\n decode(state) {\n return {\n tree: c.string.decode(state),\n bitfield: c.string.decode(state),\n signer: c.string.decode(state)\n }\n }\n}\n\nconst externalHeader = {\n preencode(state, m) {\n c.uint.preencode(state, m.start)\n c.uint.preencode(state, m.length)\n },\n encode(state, m) {\n c.uint.encode(state, m.start)\n c.uint.encode(state, m.length)\n },\n decode(state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state)\n }\n }\n}\n\nconst keyValueArray = c.array(keyValue)\n\noplog.header = {\n preencode(state, h) {\n state.end += 2 // version + flags\n if (h.external) {\n externalHeader.preencode(state, h.external)\n return\n }\n c.fixed32.preencode(state, h.key)\n if (h.manifest) manifest.preencode(state, h.manifest)\n if (h.keyPair) keyPair.preencode(state, h.keyPair)\n keyValueArray.preencode(state, h.userData)\n treeHeader.preencode(state, h.tree)\n hints.preencode(state, h.hints)\n },\n encode(state, h) {\n c.uint.encode(state, 1)\n if (h.external) {\n c.uint.encode(state, 1) // ONLY set the first big for clarity\n externalHeader.encode(state, h.external)\n return\n }\n c.uint.encode(state, (h.manifest ? 2 : 0) | (h.keyPair ? 4 : 0))\n c.fixed32.encode(state, h.key)\n if (h.manifest) manifest.encode(state, h.manifest)\n if (h.keyPair) keyPair.encode(state, h.keyPair)\n keyValueArray.encode(state, h.userData)\n treeHeader.encode(state, h.tree)\n hints.encode(state, h.hints)\n },\n decode(state) {\n const version = c.uint.decode(state)\n\n if (version > 1) {\n throw INVALID_OPLOG_VERSION('Invalid header version. Expected <= 1, got ' + version)\n }\n\n if (version === 0) {\n const old = {\n types: types.decode(state),\n userData: keyValueArray.decode(state),\n tree: treeHeader.decode(state),\n signer: keyPair.decode(state),\n hints: hints.decode(state)\n }\n\n return {\n external: null,\n key: old.signer.publicKey,\n manifest: {\n version: 0,\n hash: old.types.tree,\n allowPatch: false,\n quorum: 1,\n signers: [\n {\n signature: old.types.signer,\n namespace: DEFAULT_NAMESPACE,\n publicKey: old.signer.publicKey\n }\n ],\n prologue: null,\n linked: null,\n userData: null\n },\n keyPair: old.signer.secretKey ? old.signer : null,\n userData: old.userData,\n tree: old.tree,\n hints: old.hints\n }\n }\n\n const flags = c.uint.decode(state)\n\n if (flags & 1) {\n return {\n external: externalHeader.decode(state),\n key: null,\n manifest: null,\n keyPair: null,\n userData: null,\n tree: null,\n hints: null\n }\n }\n\n return {\n external: null,\n key: c.fixed32.decode(state),\n manifest: (flags & 2) !== 0 ? manifest.decode(state) : null,\n keyPair: (flags & 4) !== 0 ? keyPair.decode(state) : null,\n userData: keyValueArray.decode(state),\n tree: treeHeader.decode(state),\n hints: hints.decode(state)\n }\n }\n}\n\nconst uintArray = c.array(c.uint)\n\nconst multisigInput = {\n preencode(state, inp) {\n c.uint.preencode(state, inp.signer)\n c.fixed64.preencode(state, inp.signature)\n c.uint.preencode(state, inp.patch)\n },\n encode(state, inp) {\n c.uint.encode(state, inp.signer)\n c.fixed64.encode(state, inp.signature)\n c.uint.encode(state, inp.patch)\n },\n decode(state) {\n return {\n signer: c.uint.decode(state),\n signature: c.fixed64.decode(state),\n patch: c.uint.decode(state)\n }\n }\n}\n\nconst patchEncodingv0 = {\n preencode(state, n) {\n c.uint.preencode(state, n.start)\n c.uint.preencode(state, n.length)\n uintArray.preencode(state, n.nodes)\n },\n encode(state, n) {\n c.uint.encode(state, n.start)\n c.uint.encode(state, n.length)\n uintArray.encode(state, n.nodes)\n },\n decode(state) {\n return {\n start: c.uint.decode(state),\n length: c.uint.decode(state),\n nodes: uintArray.decode(state)\n }\n }\n}\n\nconst multisigInputv0 = {\n preencode(state, n) {\n state.end++\n c.uint.preencode(state, n.signer)\n c.fixed64.preencode(state, n.signature)\n if (n.patch) patchEncodingv0.preencode(state, n.patch)\n },\n encode(state, n) {\n c.uint.encode(state, n.patch ? 1 : 0)\n c.uint.encode(state, n.signer)\n c.fixed64.encode(state, n.signature)\n if (n.patch) patchEncodingv0.encode(state, n.patch)\n },\n decode(state) {\n const flags = c.uint.decode(state)\n return {\n signer: c.uint.decode(state),\n signature: c.fixed64.decode(state),\n patch: flags & 1 ? patchEncodingv0.decode(state) : null\n }\n }\n}\n\nconst multisigInputArrayv0 = c.array(multisigInputv0)\nconst multisigInputArray = c.array(multisigInput)\n\nconst compactNode = {\n preencode(state, n) {\n c.uint.preencode(state, n.index)\n c.uint.preencode(state, n.size)\n c.fixed32.preencode(state, n.hash)\n },\n encode(state, n) {\n c.uint.encode(state, n.index)\n c.uint.encode(state, n.size)\n c.fixed32.encode(state, n.hash)\n },\n decode(state) {\n return {\n index: c.uint.decode(state),\n size: c.uint.decode(state),\n hash: c.fixed32.decode(state)\n }\n }\n}\n\nconst compactNodeArray = c.array(compactNode)\n\nexports.multiSignaturev0 = {\n preencode(state, s) {\n multisigInputArrayv0.preencode(state, s.proofs)\n compactNodeArray.preencode(state, s.patch)\n },\n encode(state, s) {\n multisigInputArrayv0.encode(state, s.proofs)\n compactNodeArray.encode(state, s.patch)\n },\n decode(state) {\n return {\n proofs: multisigInputArrayv0.decode(state),\n patch: compactNodeArray.decode(state)\n }\n }\n}\n\nexports.multiSignature = {\n preencode(state, s) {\n multisigInputArray.preencode(state, s.proofs)\n compactNodeArray.preencode(state, s.patch)\n },\n encode(state, s) {\n multisigInputArray.encode(state, s.proofs)\n compactNodeArray.encode(state, s.patch)\n },\n decode(state) {\n return {\n proofs: multisigInputArray.decode(state),\n patch: compactNodeArray.decode(state)\n }\n }\n}\nconst c = require('compact-encoding')\nconst b4a = require('b4a')\nconst flat = require('flat-tree')\nconst { MerkleTree } = require('./merkle-tree')\nconst { multiSignature, multiSignaturev0 } = require('./messages')\n\nmodule.exports = {\n assemblev0,\n assemble,\n inflatev0,\n inflate,\n partialSignature,\n signableLength\n}\n\nfunction inflatev0(data) {\n return c.decode(multiSignaturev0, data)\n}\n\nfunction inflate(data) {\n return c.decode(multiSignature, data)\n}\n\nasync function partialSignature(\n core,\n signer,\n from,\n to = core.state.length,\n signature = core.state.signature\n) {\n if (from > core.state.length) return null\n const nodes = to <= from ? null : await upgradeNodes(core, from, to)\n\n if (signature.byteLength !== 64) {\n signature = c.decode(multiSignature, signature).proofs[0].signature\n }\n\n return {\n signer,\n signature,\n patch: nodes ? to - from : 0,\n nodes\n }\n}\n\nasync function upgradeNodes(core, from, to) {\n const rx = core.state.storage.read()\n let p = null\n try {\n p = await MerkleTree.proof(core.state, rx, { upgrade: { start: from, length: to - from } })\n } catch (err) {\n rx.destroy()\n throw err\n }\n rx.tryFlush()\n return (await p.settle()).upgrade.nodes\n}\n\nfunction signableLength(lengths, quorum) {\n if (quorum <= 0) quorum = 1\n if (quorum > lengths.length) return 0\n\n return lengths.sort(cmp)[quorum - 1]\n}\n\nfunction cmp(a, b) {\n return b - a\n}\n\nfunction assemblev0(inputs) {\n const proofs = []\n const patch = []\n\n for (const u of inputs) {\n proofs.push(compressProof(u, patch))\n }\n\n return c.encode(multiSignaturev0, { proofs, patch })\n}\n\nfunction assemble(inputs) {\n const proofs = []\n const patch = []\n const seen = new Set()\n\n for (const u of inputs) {\n if (u.nodes) {\n for (const node of u.nodes) {\n if (seen.has(node.index)) continue\n seen.add(node.index)\n patch.push(node)\n }\n }\n\n proofs.push({\n signer: u.signer,\n signature: u.signature,\n patch: u.patch\n })\n }\n\n return c.encode(multiSignature, { proofs, patch })\n}\n\nfunction compareNode(a, b) {\n if (a.index !== b.index) return false\n if (a.size !== b.size) return false\n return b4a.equals(a.hash, b.hash)\n}\n\nfunction compressProof(proof, nodes) {\n return {\n signer: proof.signer,\n signature: proof.signature,\n patch: proof.patch ? compressUpgrade(proof, nodes) : null\n }\n}\n\nfunction compressUpgrade(p, nodes) {\n const u = {\n start: flat.rightSpan(p.nodes[p.nodes.length - 1].index) / 2 + 1,\n length: p.patch,\n nodes: []\n }\n\n for (const node of p.nodes) {\n let present = false\n for (let i = 0; i < nodes.length; i++) {\n if (!compareNode(nodes[i], node)) continue\n\n u.nodes.push(i)\n present = true\n break\n }\n\n if (present) continue\n u.nodes.push(nodes.push(node) - 1)\n }\n\n return u\n}\nmodule.exports = class Mutex {\n constructor() {\n this.locked = false\n this.destroyed = false\n\n this._destroying = null\n this._destroyError = null\n this._queue = []\n this._enqueue = (resolve, reject) => this._queue.push([resolve, reject])\n }\n\n idle() {\n return this._queue.length === 0 && this.locked === false\n }\n\n lock() {\n if (this.destroyed) {\n return Promise.reject(this._destroyError || new Error('Mutex has been destroyed'))\n }\n if (this.locked) return new Promise(this._enqueue)\n this.locked = true\n return Promise.resolve()\n }\n\n unlock() {\n if (!this._queue.length) {\n this.locked = false\n return\n }\n this._queue.shift()[0]()\n }\n\n destroy(err) {\n if (!this._destroying) {\n this._destroying = this.locked ? this.lock().catch(() => {}) : Promise.resolve()\n }\n\n this.destroyed = true\n if (err) this._destroyError = err\n\n if (err) {\n while (this._queue.length) this._queue.shift()[1](err)\n }\n\n return this._destroying\n }\n}\nconst FIFO = require('fast-fifo')\n\nmodule.exports = class ReceiverQueue {\n constructor() {\n this.queue = new FIFO()\n this.priority = []\n this.requests = new Map()\n this.length = 0\n }\n\n push(req) {\n // TODO: use a heap at some point if we wanna support multiple prios\n if (req.priority > 0) this.priority.push(req)\n else this.queue.push(req)\n\n this.requests.set(req.id, req)\n this.length++\n }\n\n shift() {\n while (this.priority.length > 0) {\n const msg = this.priority.pop()\n const req = this._processRequest(msg)\n if (req !== null) return req\n }\n\n while (this.queue.length > 0) {\n const msg = this.queue.shift()\n const req = this._processRequest(msg)\n if (req !== null) return req\n }\n\n return null\n }\n\n _processRequest(req) {\n if (req.block || req.hash || req.seek || req.upgrade || req.manifest) {\n this.requests.delete(req.id)\n this.length--\n return req\n }\n\n return null\n }\n\n clear() {\n this.queue.clear()\n this.priority = []\n this.length = 0\n this.requests.clear()\n }\n\n delete(id) {\n const req = this.requests.get(id)\n if (!req) return\n\n req.block = null\n req.hash = null\n req.seek = null\n req.upgrade = null\n req.manifest = false\n\n this.requests.delete(id)\n this.length--\n\n if (this.length === 0) {\n this.queue.clear()\n this.priority = []\n }\n }\n}\nconst BigSparseArray = require('big-sparse-array')\nconst quickbit = require('./compat').quickbit\n\nconst BITS_PER_PAGE = 32768\nconst BYTES_PER_PAGE = BITS_PER_PAGE / 8\nconst WORDS_PER_PAGE = BYTES_PER_PAGE / 4\nconst BITS_PER_SEGMENT = 2097152\nconst BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8\nconst PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE\n\nclass RemoteBitfieldPage {\n constructor(index, bitfield, segment) {\n this.index = index\n this.offset = index * BYTES_PER_PAGE - segment.offset\n this.bitfield = bitfield\n this.segment = segment\n\n segment.add(this)\n }\n\n get tree() {\n return this.segment.tree\n }\n\n get(index) {\n return quickbit.get(this.bitfield, index)\n }\n\n set(index, val) {\n if (quickbit.set(this.bitfield, index, val)) {\n this.tree.update(this.offset * 8 + index)\n }\n }\n\n setRange(start, end, val) {\n quickbit.fill(this.bitfield, val, start, end)\n\n let i = Math.floor(start / 128)\n const n = i + Math.ceil((end - start) / 128)\n\n while (i <= n) this.tree.update(this.offset * 8 + i++ * 128)\n }\n\n findFirst(val, position) {\n return quickbit.findFirst(this.bitfield, val, position)\n }\n\n findLast(val, position) {\n return quickbit.findLast(this.bitfield, val, position)\n }\n\n insert(start, bitfield) {\n this.bitfield.set(bitfield, start / 32)\n this.segment.refresh()\n }\n\n clear(start, bitfield) {\n quickbit.clear(this.bitfield, { field: bitfield, offset: start })\n }\n}\n\nclass RemoteBitfieldSegment {\n constructor(index) {\n this.index = index\n this.offset = index * BYTES_PER_SEGMENT\n this.tree = quickbit.Index.from([], BYTES_PER_SEGMENT)\n this.pages = new Array(PAGES_PER_SEGMENT)\n this.pagesLength = 0\n }\n\n get chunks() {\n return this.tree.chunks\n }\n\n refresh() {\n this.tree = quickbit.Index.from(this.tree.chunks, BYTES_PER_SEGMENT)\n }\n\n add(page) {\n const pageIndex = page.index - this.index * PAGES_PER_SEGMENT\n if (pageIndex >= this.pagesLength) this.pagesLength = pageIndex + 1\n\n this.pages[pageIndex] = page\n\n const chunk = { field: page.bitfield, offset: page.offset }\n\n this.chunks.push(chunk)\n\n for (let i = this.chunks.length - 2; i >= 0; i--) {\n const prev = this.chunks[i]\n if (prev.offset <= chunk.offset) break\n this.chunks[i] = chunk\n this.chunks[i + 1] = prev\n }\n }\n\n findFirst(val, position) {\n position = this.tree.skipFirst(!val, position)\n\n let j = position & (BITS_PER_PAGE - 1)\n let i = (position - j) / BITS_PER_PAGE\n\n if (i >= PAGES_PER_SEGMENT) return -1\n\n while (i < this.pagesLength) {\n const p = this.pages[i]\n\n let index = -1\n\n if (p) index = p.findFirst(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_PAGE + index\n\n j = 0\n i++\n }\n\n return val || this.pagesLength === PAGES_PER_SEGMENT ? -1 : this.pagesLength * BITS_PER_PAGE\n }\n\n findLast(val, position) {\n position = this.tree.skipLast(!val, position)\n\n let j = position & (BITS_PER_PAGE - 1)\n let i = (position - j) / BITS_PER_PAGE\n\n if (i >= PAGES_PER_SEGMENT) return -1\n\n while (i >= 0) {\n const p = this.pages[i]\n\n let index = -1\n\n if (p) index = p.findLast(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_PAGE + index\n\n j = BITS_PER_PAGE - 1\n i--\n }\n\n return -1\n }\n}\n\nmodule.exports = class RemoteBitfield {\n static BITS_PER_PAGE = BITS_PER_PAGE\n static BITS_PER_SEGMENT = BITS_PER_SEGMENT\n\n constructor() {\n this._pages = new BigSparseArray()\n this._segments = new BigSparseArray()\n this._maxSegments = 0\n }\n\n getBitfield(index) {\n const j = index & (BITS_PER_PAGE - 1)\n const i = (index - j) / BITS_PER_PAGE\n\n const p = this._pages.get(i)\n return p || null\n }\n\n get(index) {\n const j = index & (BITS_PER_PAGE - 1)\n const i = (index - j) / BITS_PER_PAGE\n\n const p = this._pages.get(i)\n\n return p ? p.get(j) : false\n }\n\n set(index, val) {\n const j = index & (BITS_PER_PAGE - 1)\n const i = (index - j) / BITS_PER_PAGE\n\n let p = this._pages.get(i)\n\n if (!p && val) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))\n if (this._maxSegments <= k) this._maxSegments = k + 1\n\n p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))\n }\n\n if (p) p.set(j, val)\n }\n\n setRange(start, end, val) {\n let j = start & (BITS_PER_PAGE - 1)\n let i = (start - j) / BITS_PER_PAGE\n\n while (start < end) {\n let p = this._pages.get(i)\n\n if (!p && val) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))\n if (this._maxSegments <= k) this._maxSegments = k + 1\n\n p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))\n }\n\n const offset = i * BITS_PER_PAGE\n const last = Math.min(end - offset, BITS_PER_PAGE)\n const range = last - j\n\n if (p) p.setRange(j, last, val)\n\n j = 0\n i++\n start += range\n }\n }\n\n findFirst(val, position) {\n let j = position & (BITS_PER_SEGMENT - 1)\n let i = (position - j) / BITS_PER_SEGMENT\n\n while (i < this._maxSegments) {\n const s = this._segments.get(i)\n\n let index = -1\n\n if (s) index = s.findFirst(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_SEGMENT + index\n\n j = 0\n i++\n }\n\n // For the val === false case, we always return at least\n // the 'position', also if nothing was found\n return val ? -1 : Math.max(position, this._maxSegments * BITS_PER_SEGMENT)\n }\n\n firstSet(position) {\n return this.findFirst(true, position)\n }\n\n firstUnset(position) {\n return this.findFirst(false, position)\n }\n\n findLast(val, position) {\n let j = position & (BITS_PER_SEGMENT - 1)\n let i = (position - j) / BITS_PER_SEGMENT\n\n while (i >= 0) {\n const s = this._segments.get(i)\n\n let index = -1\n\n if (s) index = s.findLast(val, j)\n else if (!val) index = j\n\n if (index !== -1) return i * BITS_PER_SEGMENT + index\n\n j = BITS_PER_SEGMENT - 1\n i--\n }\n\n return -1\n }\n\n lastSet(position) {\n return this.findLast(true, position)\n }\n\n lastUnset(position) {\n return this.findLast(false, position)\n }\n\n insert(start, bitfield) {\n if (start % 32 !== 0) return false\n\n let length = bitfield.byteLength * 8\n\n let j = start & (BITS_PER_PAGE - 1)\n let i = (start - j) / BITS_PER_PAGE\n\n while (length > 0) {\n let p = this._pages.get(i)\n\n if (!p) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))\n if (this._maxSegments <= k) this._maxSegments = k + 1\n\n p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))\n }\n\n const end = Math.min(j + length, BITS_PER_PAGE)\n const range = end - j\n\n p.insert(j, bitfield.subarray(0, range / 32))\n\n bitfield = bitfield.subarray(range / 32)\n\n j = 0\n i++\n length -= range\n }\n\n return true\n }\n\n clear(start, bitfield) {\n if (start % 32 !== 0) return false\n\n let length = bitfield.byteLength * 8\n\n let j = start & (BITS_PER_PAGE - 1)\n let i = (start - j) / BITS_PER_PAGE\n\n while (length > 0) {\n let p = this._pages.get(i)\n\n if (!p) {\n const k = Math.floor(i / PAGES_PER_SEGMENT)\n const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))\n if (this._maxSegments <= k) this._maxSegments = k + 1\n\n p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))\n }\n\n const end = Math.min(j + length, BITS_PER_PAGE)\n const range = end - j\n\n p.clear(j, bitfield.subarray(0, range / 32))\n\n bitfield = bitfield.subarray(range / 32)\n\n j = 0\n i++\n length -= range\n }\n\n return true\n }\n}\n/* DEV DOCS\n Every hypercore has one Replicator object managing its connections to other peers.\n There is one Peer object per peer connected to the Hypercore.\n Hypercores do not know about other hypercores, so when a peer is connected to multiple cores, there exists one Peer object per core.\n\n Hypercore indicates block should be downloaded through methods like Replicator.addRange or Replicator.addBlock\n Hypercore calls Replicator.updateActivity every time a hypercore session opens/closes\n Replicator.updateActivity ensures the Hypercore is downloading blocks as expected\n Replicator keeps track of:\n - Which blocks need to be downloaded (Replicator._blocks)\n - Which blocks currently have inflight requests (Replicator._inflight)\n\n Blocks are requested from remote peers by Peer objects. The flow is:\n - The replicator's updatePeer method gets called\n - The replicator detects whether the Peer can accept more requests (for example by checking if it's maxed out on inflight blocks)\n - The replicator then tells the Peer what to request (e.g. Peer_requestRange or Peer._requestBlock methods)\n\n The Peer object is responsible for tracking\n - Which blocks does the Peer have available (tracked in remoteBitfield)\n - Which blocks are you actively looking for from this peer (tracked in missingBlocks)\n - How many blocks are currently inflight (tracked in inflight)\n The Peer uses this information to decide which blocks to request from the peer in response to _requestRange requests and the like.\n*/\n\nconst b4a = require('b4a')\nconst safetyCatch = require('safety-catch')\nconst RandomIterator = require('random-array-iterator')\nconst flatTree = require('flat-tree')\nconst Mutex = require('./mutex')\nconst ReceiverQueue = require('./receiver-queue')\nconst HotswapQueue = require('./hotswap-queue')\nconst RemoteBitfield = require('./remote-bitfield')\nconst { MerkleTree } = require('./merkle-tree')\nconst { LocalWants, RemoteWants, WANT_BATCH } = require('./wants')\nconst {\n REQUEST_CANCELLED,\n REQUEST_TIMEOUT,\n INVALID_CAPABILITY,\n SNAPSHOT_NOT_AVAILABLE,\n ASSERTION\n} = require('hypercore-errors')\nconst m = require('./messages')\nconst caps = require('./caps')\n\nconst DEFAULT_MAX_INFLIGHT = [16, 512]\nconst SCALE_LATENCY = 50\nconst NOT_DOWNLOADING_SLACK = (20000 + Math.random() * 20000) | 0\nconst MAX_PEERS_UPGRADE = 3\nconst LAST_BLOCKS = 256\n\nconst MAX_RANGES = 64\n\nconst NOT_AVAILABLE = 1\nconst INVALID_REQUEST = 2\nconst MAX_INVALID_REQUESTS = 64\nconst MAX_BACKOFFS = MAX_INVALID_REQUESTS / 2\n\nconst PRIORITY = {\n NORMAL: 0,\n HIGH: 1,\n VERY_HIGH: 2,\n CANCELLED: 255 // reserved to mark cancellation\n}\n\nclass Attachable {\n constructor(replicator) {\n this.replicator = replicator\n this.resolved = false\n this.processing = false\n this.refs = []\n this.wants = null\n }\n\n addWant(w) {\n if (this.wants === null) this.wants = new Set()\n this.wants.add(w)\n }\n\n removeWant(w) {\n if (this.wants === null) return\n this.wants.delete(w)\n }\n\n attach(session) {\n const r = {\n context: this,\n session,\n sindex: 0,\n rindex: 0,\n snapshot: true,\n resolve: null,\n reject: null,\n promise: null,\n timeout: null\n }\n\n r.sindex = session.push(r) - 1\n r.rindex = this.refs.push(r) - 1\n r.promise = new Promise((resolve, reject) => {\n r.resolve = resolve\n r.reject = reject\n })\n\n return r\n }\n\n detach(r, err = null) {\n if (r.context !== this) return false\n\n this._detach(r)\n this._cancel(r, err)\n this.gc()\n\n return true\n }\n\n _detach(r) {\n const rh = this.refs.pop()\n const sh = r.session.pop()\n\n if (r.rindex < this.refs.length) this.refs[(rh.rindex = r.rindex)] = rh\n if (r.sindex < r.session.length) r.session[(sh.sindex = r.sindex)] = sh\n\n destroyRequestTimeout(r)\n r.context = null\n\n return r\n }\n\n gc() {\n if (this.refs.length > 0 || this.processing) return\n this._unref()\n\n if (this.wants === null) return\n\n const wants = this.wants\n this.wants = null\n\n let update = null\n\n for (const w of wants) {\n if (w.any) {\n const removed = w.wants.removeAnyRange(w.start, w.length, this)\n if (!removed) continue\n if (update === null) update = new Set()\n update.add(w.wants.peer)\n // TODO: should also use a free list for simplicity\n w.wants.peer.wireUnwant.send(removed)\n continue\n }\n\n if (w.wants.removeBatch(w.start, this)) {\n if (update === null) update = new Set()\n update.add(w.wants.peer)\n }\n }\n\n if (update === null) return\n for (const peer of update) {\n this.replicator.updatePeer(peer)\n }\n }\n\n processed() {\n this.processing = false\n this.gc()\n }\n\n _cancel(r, err) {\n r.reject(err || REQUEST_CANCELLED())\n }\n\n _unref() {\n // overwrite me\n }\n\n resolve(val) {\n this.resolved = true\n while (this.refs.length > 0) {\n this._detach(this.refs[this.refs.length - 1]).resolve(val)\n }\n this.gc()\n }\n\n reject(err) {\n this.resolved = true\n while (this.refs.length > 0) {\n this._detach(this.refs[this.refs.length - 1]).reject(err)\n }\n this.gc()\n }\n\n setTimeout(r, ms) {\n destroyRequestTimeout(r)\n r.timeout = setTimeout(onrequesttimeout, ms, r)\n }\n}\n\nclass BlockRequest extends Attachable {\n constructor(replicator, tracker, index, priority, force) {\n super(replicator)\n\n this.index = index\n this.priority = priority\n this.inflight = []\n this.queued = false\n this.hotswap = null\n this.tracker = tracker\n this.force = force\n }\n\n _unref() {\n if (this.resolved) return\n this.queued = false\n\n for (const req of this.inflight) {\n req.peer._cancelRequest(req)\n }\n\n this.tracker.remove(this.index)\n removeHotswap(this)\n }\n}\n\nclass RangeBatchRequest extends Attachable {\n constructor(replicator, batch) {\n super(replicator)\n\n this.batch = batch\n this.index = 0\n }\n}\n\nclass RangeRequest extends Attachable {\n constructor(replicator, ranges, start, end, linear, ifAvailable, blocks) {\n super(replicator)\n\n this.start = start\n this.end = end\n this.linear = linear\n this.ifAvailable = ifAvailable\n this.blocks = blocks\n this.ranges = ranges\n this.batches = []\n\n ranges.push(this)\n\n if (this.end === -1) {\n this.replicator._alwaysLatestBlock++\n }\n\n // As passed by the user, immut\n this.userStart = start\n this.userEnd = end\n }\n\n addBatch(batch) {\n for (let i = 0; i < this.batches.length; i++) {\n const b = this.batches[i]\n if (b.batch === batch) return false\n }\n const b = new RangeBatchRequest(this.replicator, batch)\n b.index = this.batches.length\n this.batches.push(b)\n return true\n }\n\n removeBatch(batch) {\n const head = this.batches.pop()\n\n if (head !== batch) {\n head.index = batch.index\n this.batches[head.index] = head\n }\n\n batch.gc()\n }\n\n _unref() {\n const rangeIndex = this.ranges.indexOf(this)\n if (rangeIndex === -1) return\n\n const h = this.ranges.pop()\n if (h !== this) {\n this.ranges[rangeIndex] = h\n }\n\n if (this.end === -1) {\n this.replicator._alwaysLatestBlock--\n }\n\n while (this.batches.length) {\n this.batches.pop().gc()\n }\n }\n\n _cancel(r) {\n r.resolve(false)\n }\n}\n\nclass UpgradeRequest extends Attachable {\n constructor(replicator, fork, length) {\n super(replicator)\n\n this.fork = fork\n this.length = length\n this.inflight = []\n }\n\n _unref() {\n if (this.resolved) return\n if (this.replicator.eagerUpgrade === true || this.inflight.length > 0) return\n this.replicator._upgrade = null\n }\n\n _cancel(r) {\n r.resolve(false)\n }\n}\n\nclass SeekRequest extends Attachable {\n constructor(replicator, seeks, seeker) {\n super(replicator)\n\n this.seeker = seeker\n this.inflight = []\n this.seeks = seeks\n }\n\n _unref() {\n if (this.resolved) return\n if (this.inflight.length > 0) return\n const i = this.seeks.indexOf(this)\n if (i === -1) return\n const h = this.seeks.pop()\n if (i < this.seeks.length) this.seeks[i] = h\n }\n}\n\nclass ForkRequest extends Attachable {\n constructor(replicator, fork) {\n super(replicator)\n this.fork = fork\n this.inflight = []\n this.batch = null\n }\n}\n\nclass InflightTracker {\n constructor() {\n this._requests = []\n this._free = []\n this._active = 0\n }\n\n get idle() {\n return this._active === 0\n }\n\n *[Symbol.iterator]() {\n for (const req of this._requests) {\n if (req !== null) yield req\n }\n }\n\n add(req) {\n const id = this._free.length ? this._free.pop() : this._requests.push(null)\n req.id = id\n this._requests[id - 1] = req\n this._active++\n return req\n }\n\n get(id) {\n return id <= this._requests.length ? this._requests[id - 1] : null\n }\n\n remove(id, roundtrip) {\n if (id > this._requests.length) return\n if (this._requests[id - 1]) this._active--\n this._requests[id - 1] = null\n if (roundtrip === true) this._free.push(id)\n }\n\n reusable(id) {\n this._free.push(id)\n }\n}\n\nclass BlockTracker {\n constructor(replicator) {\n this._replicator = replicator\n this._map = new Map()\n }\n\n [Symbol.iterator]() {\n return this._map.values()\n }\n\n isEmpty() {\n return this._map.size === 0\n }\n\n has(index) {\n return this._map.has(index)\n }\n\n get(index) {\n return this._map.get(index) || null\n }\n\n add(index, priority, force) {\n let b = this._map.get(index)\n if (b) return b\n\n b = new BlockRequest(this._replicator, this, index, priority, force)\n this._map.set(index, b)\n\n return b\n }\n\n remove(index) {\n const b = this.get(index)\n this._map.delete(index)\n return b\n }\n}\n\nclass RoundtripQueue {\n constructor() {\n this.queue = []\n this.tick = 0\n }\n\n clear() {\n const ids = new Array(this.queue.length)\n for (let i = 0; i < ids.length; i++) {\n ids[i] = this.queue[i][1]\n }\n\n this.queue = []\n\n return ids\n }\n\n add(id) {\n this.queue.push([++this.tick, id])\n }\n\n flush(tick) {\n let flushed = null\n\n for (let i = 0; i < this.queue.length; i++) {\n if (this.queue[i][0] > tick) break\n if (flushed === null) flushed = []\n flushed.push(this.queue[i][1])\n }\n\n if (flushed !== null) this.queue.splice(0, flushed.length)\n return flushed\n }\n}\n\nclass ProofRequest {\n constructor(msg, proof, block, manifest) {\n this.msg = msg\n this.proof = proof\n this.block = block\n this.manifest = manifest\n }\n\n async fulfill() {\n if (this.proof === null) return null\n\n const [proof, block] = await Promise.all([this.proof.settle(), this.block])\n\n if (this.manifest) proof.manifest = this.manifest\n if (!block && proof.block) return null\n\n if (block) proof.block.value = block\n return proof\n }\n}\n\nclass Peer {\n constructor(replicator, protomux, channel, inflightRange) {\n this.core = replicator.core\n this.replicator = replicator\n this.stream = protomux.stream\n this.protomux = protomux\n this.remotePublicKey = this.stream.remotePublicKey\n this.remoteSupportsSeeks = false\n this.inflightRange = inflightRange\n this.fullyDownloadedSignaled = 0\n\n this.paused = false\n this.removed = false\n\n this.channel = channel\n this.channel.userData = this\n\n this.wireSync = this.channel.messages[0]\n this.wireRequest = this.channel.messages[1]\n this.wireCancel = this.channel.messages[2]\n this.wireData = this.channel.messages[3]\n this.wireNoData = this.channel.messages[4]\n this.wireWant = this.channel.messages[5]\n this.wireUnwant = this.channel.messages[6]\n this.wireBitfield = this.channel.messages[7]\n this.wireRange = this.channel.messages[8]\n this.wireExtension = this.channel.messages[9]\n\n // Same stats as replicator, but for this specific peer\n this.stats = {\n wireSync: { tx: 0, rx: 0 },\n wireRequest: { tx: 0, rx: 0 },\n wireCancel: { tx: 0, rx: 0 },\n wireData: { tx: 0, rx: 0 },\n wireWant: { tx: 0, rx: 0 },\n wireUnwant: { tx: 0, rx: 0 },\n wireBitfield: { tx: 0, rx: 0 },\n wireRange: { tx: 0, rx: 0 },\n wireExtension: { tx: 0, rx: 0 },\n hotswaps: 0,\n invalidData: 0,\n invalidRequests: 0,\n backoffs: 0\n }\n\n this.receiverQueue = new ReceiverQueue()\n this.receiverBusy = false\n\n // most often not used, so made on demand\n this.roundtripQueue = null\n\n this.inflight = 0\n this.dataProcessing = 0\n\n this.canUpgrade = true\n\n this.needsSync = false\n this.syncsProcessing = 0\n this.lastUpgradableLength = 0\n this.lastUpgradableFork = 0\n\n this._remoteContiguousLength = 0\n\n // TODO: tweak pipelining so that data sent BEFORE remoteOpened is not cap verified!\n // we might wanna tweak that with some crypto, ie use the cap to encrypt it...\n // or just be aware of that, to only push non leaky data\n\n this.remoteOpened = false\n this.remoteBitfield = new RemoteBitfield()\n this.missingBlocks = new RemoteBitfield()\n\n this.pushedLength = 0\n\n this.remoteFork = 0\n this.remoteLength = 0\n this.remoteCanUpgrade = false\n this.remoteUploading = true\n this.remoteDownloading = true\n this.remoteSynced = false\n this.remoteHasManifest = false\n this.remoteAllowPush = false\n this.remoteRequests = new Map()\n\n this.segmentsWanted = new Set()\n this.broadcastedNonSparse = false\n\n this.wants = new LocalWants(this)\n this.remoteWants = new RemoteWants(this)\n\n this.lengthAcked = 0\n\n this.extensions = new Map()\n this.lastExtensionSent = ''\n this.lastExtensionRecv = ''\n\n replicator._ifAvailable++\n replicator._active++\n }\n\n get remoteContiguousLength() {\n return this.remoteBitfield.findFirst(false, this._remoteContiguousLength)\n }\n\n getMaxInflight() {\n const stream = this.stream.rawStream\n if (!stream.udx) return Math.min(this.inflightRange[1], this.inflightRange[0] * 3)\n\n const scale =\n stream.rtt <= SCALE_LATENCY\n ? 1\n : (stream.rtt / SCALE_LATENCY) * Math.min(1, 2 / this.replicator.peers.length)\n return Math.max(\n this.inflightRange[0],\n Math.round(Math.min(this.inflightRange[1], this.inflightRange[0] * scale))\n )\n }\n\n getMaxHotswapInflight() {\n const inf = this.getMaxInflight()\n return Math.max(16, inf / 2)\n }\n\n signalUpgrade() {\n if (this._shouldUpdateCanUpgrade() === true) this._updateCanUpgradeAndSync()\n else this.sendSync()\n }\n\n _markInflight(index) {\n this.missingBlocks.set(index, false)\n }\n\n broadcastRange(start, length, drop) {\n if (!this.isActive()) return\n\n if (drop) this._unclearLocalRange(start, length)\n else this._clearLocalRange(start, length)\n\n const fullyContig = this.core.header.hints.contiguousLength === this.core.state.length\n\n if (start + LAST_BLOCKS < this.core.state.length && !drop && !fullyContig) {\n if (!this.remoteWants.hasRange(start, length)) return\n }\n\n let force = false\n if (fullyContig && !drop) {\n start = 0\n length = this.core.state.length\n\n // Always send the broadcast when we switch from sparse to fully contiguous, so remote knows too\n if (this.fullyDownloadedSignaled < length) {\n this.fullyDownloadedSignaled = length\n force = true\n }\n }\n\n // TODO: consider also adding early-returns on the drop===true case\n if (!force && !drop) {\n // No need to broadcast if the remote already has this range\n\n if (this._remoteContiguousLength >= start + length) return\n\n if (length === 1) {\n if (this.remoteBitfield.get(start)) return\n } else {\n if (this.remoteBitfield.firstUnset(start) >= start + length) return\n }\n }\n\n this.wireRange.send({\n drop,\n start,\n length\n })\n incrementTx(this.stats.wireRange, this.replicator.stats.wireRange)\n }\n\n extension(name, message) {\n this.wireExtension.send({ name: name === this.lastExtensionSent ? '' : name, message })\n incrementTx(this.stats.wireExtension, this.replicator.stats.wireExtension)\n this.lastExtensionSent = name\n }\n\n onextension(message) {\n const name = message.name || this.lastExtensionRecv\n this.lastExtensionRecv = name\n const ext = this.extensions.get(name)\n if (ext) {\n ext._onmessage({ start: 0, end: message.message.byteLength, buffer: message.message }, this)\n }\n }\n\n sendSync() {\n if (this.syncsProcessing !== 0) {\n this.needsSync = true\n return\n }\n\n if (this.core.state.fork !== this.remoteFork) {\n this.canUpgrade = false\n }\n\n this.needsSync = false\n\n this.wireSync.send({\n fork: this.core.state.fork,\n length: this.core.state.length,\n remoteLength: this.core.state.fork === this.remoteFork ? this.remoteLength : 0,\n canUpgrade: this.canUpgrade,\n uploading: true,\n downloading: this.replicator.isDownloading(),\n hasManifest: !!this.core.header.manifest && this.core.compat === false,\n allowPush: this.replicator.isAllowingPush()\n })\n incrementTx(this.stats.wireSync, this.replicator.stats.wireSync)\n }\n\n onopen({ seeks, capability }) {\n const expected = caps.replicate(\n this.stream.isInitiator === false,\n this.core.key,\n this.stream.handshakeHash\n )\n\n if (b4a.equals(capability, expected) !== true) {\n // TODO: change this to a rejection instead, less leakage\n throw INVALID_CAPABILITY('Remote sent an invalid replication capability')\n }\n\n if (this.remoteOpened === true) return\n this.remoteOpened = true\n this.remoteSupportsSeeks = seeks\n\n this.protomux.cork()\n\n this.sendSync()\n\n const contig = Math.min(this.core.state.length, this.core.header.hints.contiguousLength)\n if (contig > 0) {\n this.broadcastRange(0, contig, false)\n\n if (contig === this.core.state.length) {\n this.broadcastedNonSparse = true\n }\n }\n\n this.replicator._ifAvailable--\n this.replicator._addPeer(this)\n\n this.protomux.uncork()\n\n this.core.checkIfIdle()\n }\n\n onclose(isRemote) {\n // we might have signalled to the remote that we are done (ie not downloading) and the remote might agree on that\n // if that happens, the channel might be closed by the remote. if so just renegotiate it.\n // TODO: add a CLOSE_REASON to mux to we can make this cleaner...\n const reopen =\n isRemote === true &&\n this.remoteOpened === true &&\n this.remoteDownloading === false &&\n this.remoteUploading === true &&\n this.replicator.downloading === true\n\n if (this.remoteOpened === false) {\n this.replicator._ifAvailable--\n this.replicator.updateAll()\n return\n }\n\n this.remoteOpened = false\n this.removed = true\n this.remoteRequests.clear() // cancel all\n this.receiverQueue.clear()\n\n if (this.roundtripQueue !== null) {\n for (const id of this.roundtripQueue.clear()) this.replicator._inflight.reusable(id)\n }\n\n this.replicator._removePeer(this)\n\n if (reopen) {\n this.replicator._makePeer(this.protomux)\n }\n }\n\n closeIfIdle() {\n if (this.remoteDownloading === false && this.replicator.isDownloading() === false) {\n // idling, shut it down...\n this.channel.close()\n return true\n }\n\n return false\n }\n\n async onsync(msg) {\n const {\n fork,\n length,\n remoteLength,\n canUpgrade,\n uploading,\n downloading,\n hasManifest,\n allowPush\n } = msg\n\n const lengthChanged = length !== this.remoteLength\n const sameFork = fork === this.core.state.fork\n\n this.remoteSynced = true\n this.remoteFork = fork\n this.remoteLength = length\n this.remoteCanUpgrade = canUpgrade\n this.remoteUploading = uploading\n this.remoteDownloading = downloading\n this.remoteHasManifest = hasManifest\n this.remoteAllowPush = allowPush\n\n if (this.closeIfIdle()) return\n\n this.lengthAcked = sameFork ? remoteLength : 0\n this.syncsProcessing++\n\n this.replicator._updateFork(this)\n\n if (this.remoteLength > this.core.state.length && this.lengthAcked === this.core.state.length) {\n if (this.replicator._addUpgradeMaybe() !== null) this._update()\n }\n\n const upgrade =\n lengthChanged === false || sameFork === false\n ? this.canUpgrade && sameFork\n : await this._canUpgrade(length, fork)\n\n if (length === this.remoteLength && fork === this.core.state.fork) {\n this.canUpgrade = upgrade\n if (upgrade) {\n this.lastUpgradableFork = fork\n this.lastUpgradableLength = length\n }\n }\n\n if (--this.syncsProcessing !== 0) return // ie not latest\n\n if (\n this.needsSync === true ||\n (this.core.state.fork === this.remoteFork && this.core.state.length > this.remoteLength)\n ) {\n this.signalUpgrade()\n }\n\n this._update()\n }\n\n _shouldUpdateCanUpgrade() {\n return (\n this.core.state.fork === this.remoteFork &&\n this.core.state.length > this.remoteLength &&\n this.canUpgrade === false &&\n this.syncsProcessing === 0\n )\n }\n\n async _updateCanUpgradeAndSync() {\n const { length, fork } = this.core.state\n\n const remoteLength = this.remoteLength\n const remoteFork = this.remoteFork\n const canUpgrade = await this._canUpgrade(this.remoteLength, this.remoteFork)\n\n if (\n this.syncsProcessing > 0 ||\n length !== this.core.state.length ||\n fork !== this.core.state.fork\n ) {\n return\n }\n if (remoteLength !== this.remoteLength || remoteFork !== this.remoteFork) {\n return\n }\n if (canUpgrade === this.canUpgrade) {\n return\n }\n\n this.canUpgrade = canUpgrade\n if (canUpgrade) {\n this.lastUpgradableLength = this.remoteLength\n this.lastUpgradableFork = this.remoteFork\n }\n\n this.sendSync()\n }\n\n // Safe to call in the background - never fails\n async _canUpgrade(remoteLength, remoteFork) {\n if (remoteFork !== this.core.state.fork) return false\n\n if (remoteLength === 0) return true\n if (remoteLength >= this.core.state.length) return false\n\n try {\n // Rely on caching to make sure this is cheap...\n const canUpgrade = await MerkleTree.upgradeable(this.core.state, remoteLength)\n\n if (remoteFork !== this.core.state.fork) return false\n\n return canUpgrade\n } catch {\n return false\n }\n }\n\n async _getProof(batch, msg, pushing) {\n let block = null\n\n if (msg.block) {\n const index = msg.block.index\n\n if (!pushing && (msg.fork !== this.core.state.fork || !this.core.bitfield.get(index))) {\n return new ProofRequest(msg, null, null, null)\n }\n\n block = batch.getBlock(index)\n block.catch(noop)\n }\n\n const manifest = msg.manifest && !this.core.compat ? this.core.header.manifest : null\n\n try {\n const proof = await MerkleTree.proof(this.core.state, batch, msg)\n return new ProofRequest(msg, proof, block, manifest)\n } catch (err) {\n batch.destroy()\n\n this.replicator._oninvalidrequest(err, msg, this)\n\n if (this.stats.invalidRequests >= MAX_INVALID_REQUESTS) throw err\n\n await backoff(this.stats.invalidRequests)\n return null\n }\n }\n\n async onrequest(msg) {\n const size = this.remoteRequests.size\n this.remoteRequests.set(msg.id, msg)\n\n // if size didnt change -> id overwrite -> old one is deleted, cancel current and re-add\n if (size === this.remoteRequests.size) {\n this._cancel(msg.id)\n this.remoteRequests.set(msg.id, msg)\n }\n\n if (!this.protomux.drained || this.receiverQueue.length) {\n this.receiverQueue.push(msg)\n return\n }\n\n if (this.replicator.destroyed) return\n\n await this._handleRequest(msg)\n }\n\n oncancel(msg) {\n this._cancel(msg.request)\n }\n\n _cancel(id) {\n this.remoteRequests.delete(id)\n this.receiverQueue.delete(id)\n }\n\n ondrain() {\n return this._handleRequests()\n }\n\n async _handleRequests() {\n if (this.receiverBusy || this.replicator.destroyed) return\n this.receiverBusy = true\n this.protomux.cork()\n\n while (\n this.remoteOpened &&\n this.protomux.drained &&\n this.receiverQueue.length > 0 &&\n !this.removed\n ) {\n const msg = this.receiverQueue.shift()\n await this._handleRequest(msg)\n }\n\n this.protomux.uncork()\n this.receiverBusy = false\n }\n\n async push(index) {\n if (!this.remoteAllowPush) return\n if (!this.remoteDownloading) return\n if (this.core.state.fork !== this.remoteFork) return\n if (this.remoteBitfield.get(index)) return\n\n const msg = {\n id: 0,\n fork: this.core.state.fork,\n block: null,\n hash: null,\n seek: null,\n upgrade: null,\n manifest: this.remoteLength === 0,\n priority: 0\n }\n\n const remoteLength = Math.max(this.remoteLength, this.pushedLength)\n\n msg.block = {\n index,\n nodes: MerkleTree.maxMissingNodes(2 * index, remoteLength)\n }\n\n if (index >= remoteLength) {\n msg.upgrade = {\n start: remoteLength,\n length: this.core.state.length - remoteLength\n }\n }\n\n let req = null\n const batch = this.core.storage.read()\n try {\n req = await this._getProof(batch, msg, true)\n } catch (err) {\n this.replicator._oninvalidrequest(err, msg, this)\n return\n }\n\n if (req === null) return\n batch.tryFlush()\n\n await this._fulfillRequest(req, true)\n }\n\n async _handleRequest(msg) {\n const batch = this.core.storage.read()\n\n // TODO: could still be answerable if (index, fork) is an ancestor of the current fork\n const req =\n msg.fork === this.core.state.fork\n ? await this._getProof(batch, msg, false)\n : new ProofRequest(msg, null, null, null)\n\n if (req === null) {\n this.wireNoData.send({ request: msg.id, reason: INVALID_REQUEST })\n return\n }\n\n batch.tryFlush()\n\n await this._fulfillRequest(req, false)\n }\n\n async _fulfillRequest(req, pushing) {\n const proof = await req.fulfill()\n\n if (!pushing) {\n // if cancelled do not reply\n if (this.remoteRequests.get(req.msg.id) !== req.msg) {\n return\n }\n\n // sync from now on, so safe to delete from the map\n this.remoteRequests.delete(req.msg.id)\n } else if (!this.remoteAllowPush) {\n // if pushing but remote disabled it, just drop it\n return\n }\n\n if (!this.isActive() && proof.block !== null) {\n return\n }\n\n if (proof === null) {\n if (req.msg.manifest && this.core.header.manifest) {\n const manifest = this.core.header.manifest\n this.wireData.send({\n request: req.msg.id,\n fork: this.core.state.fork,\n block: null,\n hash: null,\n seek: null,\n upgrade: null,\n manifest\n })\n incrementTx(this.stats.wireData, this.replicator.stats.wireData)\n return\n }\n\n this.wireNoData.send({ request: req.msg.id, reason: NOT_AVAILABLE })\n return\n }\n\n if (proof.block !== null) {\n this.replicator._onupload(proof.block.index, proof.block.value.byteLength, this)\n }\n\n if (proof.upgrade) {\n const remoteLength = proof.upgrade.start + proof.upgrade.length\n if (remoteLength > this.pushedLength) this.pushedLength = remoteLength\n }\n\n this.wireData.send({\n request: req.msg.id,\n fork: req.msg.fork,\n block: proof.block,\n hash: proof.hash,\n seek: proof.seek,\n upgrade: proof.upgrade,\n manifest: proof.manifest\n })\n incrementTx(this.stats.wireData, this.replicator.stats.wireData)\n }\n\n _cancelRequest(req) {\n if (req.priority === PRIORITY.CANCELLED) return\n // mark as cancelled also and avoid re-entry\n req.priority = PRIORITY.CANCELLED\n\n this.inflight--\n this.replicator._requestDone(req.id, false)\n\n // clear inflight state\n if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)\n if (isUpgradeRequest(req)) this.replicator._clearInflightUpgrade(req)\n\n if (this.roundtripQueue === null) this.roundtripQueue = new RoundtripQueue()\n this.roundtripQueue.add(req.id)\n this.wireCancel.send({ request: req.id })\n incrementTx(this.stats.wireCancel, this.replicator.stats.wireCancel)\n }\n\n _checkIfConflict() {\n this.paused = true\n\n const length = Math.min(this.core.state.length, this.remoteLength)\n if (length === 0) return // pause and ignore\n\n this.wireRequest.send({\n id: 0, // TODO: use an more explicit id for this eventually...\n fork: this.remoteFork,\n block: null,\n hash: null,\n seek: null,\n upgrade: {\n start: 0,\n length\n }\n })\n\n incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest)\n }\n\n _unmarkInflightBlockRequest(req, data) {\n if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index)\n else if (data.block && !req) this.replicator._unmarkInflight(data.block.index)\n }\n\n async ondata(data) {\n if (data.request !== 0) return this._handleData(data)\n\n await this.replicator._pushLock.lock()\n try {\n await this._handleData(data)\n } finally {\n this.replicator._pushLock.unlock()\n }\n }\n\n async _handleData(data) {\n // always allow a fork conflict proof to be sent\n if (data.request === 0 && data.upgrade && data.upgrade.start === 0) {\n if (await this.core.checkConflict(data, this)) return\n this.paused = false\n }\n\n const req = data.request > 0 ? this.replicator._inflight.get(data.request) : null\n const reorg = data.fork > this.core.state.fork\n\n // no push atm, TODO: check if this satisfies another pending request\n // allow reorg pushes tho as those are not written to storage so we'll take all the help we can get\n if (req === null && reorg === false && !this.replicator.allowPush) {\n return\n }\n\n if (req === null && reorg === false && data.block) {\n // mark this as inflight to avoid parallel requests\n this.replicator._markInflight(data.block.index)\n }\n\n if (req !== null) {\n if (req.peer !== this) return\n this._onrequestroundtrip(req)\n }\n\n try {\n if (reorg === true) return await this.replicator._onreorgdata(this, req, data)\n } catch (err) {\n safetyCatch(err)\n this._unmarkInflightBlockRequest(req, data)\n\n this.paused = true\n this.replicator._oninvaliddata(err, req, data, this)\n return\n }\n\n this.dataProcessing++\n if (isBlockRequest(req)) this.replicator._markProcessing(req.block.index)\n\n try {\n if (!this.replicator._matchingRequest(req, data) || !(await this.core.verify(data, this))) {\n this.replicator._onnodata(this, req, 0)\n return\n }\n } catch (err) {\n safetyCatch(err)\n this._unmarkInflightBlockRequest(req, data)\n\n if (err.code === 'WRITE_FAILED') {\n // For example, we don't want to keep pulling data when storage is full\n // TODO: notify the user somehow\n this.paused = true\n return\n }\n\n if (this.core.closed && !isCriticalError(err)) return\n\n if (err.code !== 'INVALID_OPERATION') {\n // might be a fork, verify\n this._checkIfConflict()\n }\n\n // if push mode, we MIGHT get an occasional bad message\n // no need to mega bail for that. TODO: rate limit instead as a general thing\n if (!this.replicator.allowPush) {\n this.paused = true\n }\n\n this.replicator._onnodata(this, req, 0)\n this.replicator._oninvaliddata(err, req, data, this)\n return\n } finally {\n if (isBlockRequest(req)) this.replicator._markProcessed(req.block.index)\n this.dataProcessing--\n }\n\n this.replicator._ondata(this, req, data)\n\n if (this._shouldUpdateCanUpgrade() === true) {\n this._updateCanUpgradeAndSync()\n }\n }\n\n onnodata({ request, reason }) {\n const req = request > 0 ? this.replicator._inflight.get(request) : null\n\n if (req === null || req.peer !== this) {\n this.replicator.updateAll()\n return\n }\n\n this._onrequestroundtrip(req)\n this.replicator._onnodata(this, req, reason)\n }\n\n _onrequestroundtrip(req) {\n if (req.priority === PRIORITY.CANCELLED) return\n // to avoid re-entry we also just mark it as cancelled\n req.priority = PRIORITY.CANCELLED\n\n this.inflight--\n this.replicator._requestDone(req.id, true)\n if (this.roundtripQueue === null) return\n const flushed = this.roundtripQueue.flush(req.rt)\n if (flushed === null) return\n for (const id of flushed) this.replicator._inflight.reusable(id)\n }\n\n onwant(range) {\n if (!this.remoteWants.add(range)) return\n this.replicator._onwant(this, range.start, range.length, range.any)\n }\n\n onunwant(range) {\n this.remoteWants.remove(range)\n }\n\n onbitfield({ start, bitfield }) {\n if (start < this._remoteContiguousLength) this._remoteContiguousLength = start // bitfield is always the truth\n this.remoteBitfield.insert(start, bitfield)\n this.missingBlocks.insert(start, bitfield)\n this._clearLocalRange(start, bitfield.byteLength * 8)\n this._update()\n }\n\n _clearLocalRange(start, length) {\n const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield\n\n if (length === 1) {\n this.missingBlocks.set(start, this._remoteHasBlock(start) && !bitfield.get(start))\n return\n }\n\n const contig = Math.min(this.core.state.length, this.core.header.hints.contiguousLength)\n\n if (start + length < contig) {\n this.missingBlocks.setRange(start, contig, false)\n return\n }\n\n const rem = start & 32767\n if (rem > 0) {\n start -= rem\n length += rem\n }\n\n const end = start + Math.min(length, this.core.state.length)\n while (start < end) {\n const local = bitfield.getBitfield(start)\n\n if (local && local.bitfield) {\n this.missingBlocks.clear(start, local.bitfield)\n }\n\n start += 32768\n }\n }\n\n _resetMissingBlock(index) {\n const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield\n this.missingBlocks.set(index, this._remoteHasBlock(index) && !bitfield.get(index))\n }\n\n _unclearLocalRange(start, length) {\n if (length === 1) {\n this._resetMissingBlock(start)\n return\n }\n\n const rem = start & (RemoteBitfield.BITS_PER_SEGMENT - 1)\n if (rem > 0) {\n start -= rem\n length += rem\n }\n\n const fixedStart = start\n\n const end = start + Math.min(length, this.remoteLength)\n while (start < end) {\n const remote = this.remoteBitfield.getBitfield(start)\n if (remote && remote.bitfield) {\n this.missingBlocks.insert(start, remote.bitfield)\n }\n\n start += RemoteBitfield.BITS_PER_SEGMENT\n }\n\n this._clearLocalRange(fixedStart, length)\n }\n\n async onrange({ drop, start, length }) {\n const has = drop === false\n\n if (drop === true && start < this._remoteContiguousLength) {\n this._remoteContiguousLength = start\n }\n\n if (start === 0 && drop === false) {\n if (length > this._remoteContiguousLength) {\n this._remoteContiguousLength = length\n if (\n this.remoteFork === this.core.state.fork &&\n length > this.core.header.hints.remoteContiguousLength\n ) {\n this.core.updateRemoteContiguousLength(length)\n }\n }\n } else if (length === 1) {\n const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield\n this.remoteBitfield.set(start, has)\n this.missingBlocks.set(start, has && !bitfield.get(start))\n } else {\n const rangeStart = this.remoteBitfield.findFirst(!has, start)\n const rangeEnd = length + start\n\n if (rangeStart !== -1 && rangeStart < rangeEnd) {\n this.remoteBitfield.setRange(rangeStart, rangeEnd, has)\n this.missingBlocks.setRange(rangeStart, rangeEnd, has)\n if (has) this._clearLocalRange(rangeStart, rangeEnd - rangeStart)\n }\n }\n\n if (this.core.hintsChanged) await this.core.flushHints()\n if (drop === false) this._update()\n }\n\n onreorghint() {\n // TODO\n }\n\n _update() {\n // TODO: if this is in a batch or similar it would be better to defer it\n // we could do that with nextTick/microtick mb? (combined with a property on the session to signal read buffer mb)\n this.replicator.updatePeer(this)\n }\n\n async _onconflict() {\n this.protomux.cork()\n if (this.remoteLength > 0 && this.core.state.fork === this.remoteFork) {\n await this.onrequest({\n id: 0,\n fork: this.core.state.fork,\n block: null,\n hash: null,\n seek: null,\n upgrade: {\n start: 0,\n length: Math.min(this.core.state.length, this.remoteLength)\n }\n })\n }\n this.channel.close()\n this.protomux.uncork()\n }\n\n _makeRequest(needsUpgrade, priority, minLength) {\n if (needsUpgrade === true && this.replicator._shouldUpgrade(this) === false) {\n return null\n }\n\n // ensure that the remote has signalled they have the length we request\n if (this.remoteLength < minLength) {\n return null\n }\n\n if (needsUpgrade === false && this.replicator._autoUpgrade(this) === true) {\n needsUpgrade = true\n }\n\n return {\n peer: this,\n rt: this.roundtripQueue === null ? 0 : this.roundtripQueue.tick,\n id: 0,\n fork: this.remoteFork,\n block: null,\n hash: null,\n seek: null,\n upgrade:\n needsUpgrade === false\n ? null\n : { start: this.core.state.length, length: this.remoteLength - this.core.state.length },\n // remote manifest check can be removed eventually...\n manifest: this.core.header.manifest === null && this.remoteHasManifest === true,\n priority,\n timestamp: Date.now(),\n elapsed: 0\n }\n }\n\n _requestManifest() {\n const req = this._makeRequest(false, 0, 0)\n if (req === null) return\n\n this._send(req)\n }\n\n _includeLastBlock() {\n if (this.replicator._alwaysLatestBlock === 0) return null\n\n const index = this.remoteLength - 1\n\n // only valid if its an OOB get\n if (index < this.core.state.length) return null\n\n // do the normal checks\n if (!this._remoteHasBlock(index)) return null\n if (!this._canRequest(index, false)) return null\n\n // atm we only ever do one upgrade request in parallel, if that changes\n // then we should add some check here for inflights to avoid over requesting\n const b = this.replicator._blocks.add(index, PRIORITY.NORMAL, false)\n return b\n }\n\n _requestUpgrade(u) {\n if (this.replicator.pushOnly) return false\n\n const req = this._makeRequest(true, 0, 0)\n if (req === null) return false\n\n const b = this._includeLastBlock()\n\n if (b) this._sendBlockRequest(req, b)\n else this._send(req)\n\n return true\n }\n\n _requestSeek(s) {\n // if replicator is updating the seeks etc, bail and wait for it to drain\n if (this.replicator._updatesPending > 0) return false\n if (this.replicator.pushOnly) return false\n\n const { length, fork } = this.core.state\n\n if (fork !== this.remoteFork) return false\n\n if (s.seeker.start >= length) {\n const req = this._makeRequest(true, 0, 0)\n\n // We need an upgrade for the seek, if non can be provided, skip\n if (req === null) return false\n\n req.seek = this.remoteSupportsSeeks\n ? { bytes: s.seeker.bytes, padding: s.seeker.padding }\n : null\n\n s.inflight.push(req)\n this._send(req)\n\n return true\n }\n\n const len = s.seeker.end - s.seeker.start\n const off = s.seeker.start + Math.floor(Math.random() * len)\n\n for (let i = 0; i < len; i++) {\n let index = off + i\n if (index > s.seeker.end) index -= len\n\n if (this._remoteHasBlock(index) === false) continue\n if (this.core.bitfield.get(index) === true) continue\n if (!this._canRequest(index, false)) continue\n\n // Check if this block is currently inflight - if so pick another\n const b = this.replicator._blocks.get(index)\n if (b !== null && b.inflight.length > 0) continue\n\n // Block is not inflight, but we only want the hash, check if that is inflight\n const h = this.replicator._hashes.add(index, PRIORITY.NORMAL, false)\n if (h.inflight.length > 0) continue\n\n const req = this._makeRequest(false, h.priority, index + 1)\n if (req === null) continue\n\n const nodes = flatTree.depth(s.seeker.start + s.seeker.end - 1)\n\n req.hash = { index: 2 * index, nodes }\n req.seek = this.remoteSupportsSeeks\n ? { bytes: s.seeker.bytes, padding: s.seeker.padding }\n : null\n\n s.inflight.push(req)\n h.inflight.push(req)\n this._send(req)\n\n return true\n }\n\n this._maybeWantAnyRange(s.seeker.start, len, s)\n return false\n }\n\n _canRequest(index, force) {\n if (!(index >= 0)) throw ASSERTION('bad index to _canRequest: ' + index)\n\n if (!force && this.replicator.pushOnly) {\n return false\n }\n\n if (this.remoteLength >= this.core.state.length) {\n return true\n }\n\n if (index < this.lastUpgradableLength && this.lastUpgradableFork === this.core.state.fork) {\n return true\n }\n\n if (this.canUpgrade && this.syncsProcessing === 0) {\n return true\n }\n\n return false\n }\n\n _remoteHasBlock(index) {\n return index < this._remoteContiguousLength || this.remoteBitfield.get(index) === true\n }\n\n _sendBlockRequest(req, b) {\n req.block = { index: b.index, nodes: 0 }\n this.replicator._markInflight(b.index)\n\n b.inflight.push(req)\n this.replicator.hotswaps.add(b)\n this._send(req)\n }\n\n _requestBlock(b) {\n const { length, fork } = this.core.state\n\n if (this._remoteHasBlock(b.index) === false || fork !== this.remoteFork) {\n if (!this.core.bitfield.get(b.index)) this._maybeWant(b.index, b)\n return false\n }\n\n if (!this._canRequest(b.index, b.force)) return false\n\n const req = this._makeRequest(b.index >= length, b.priority, b.index + 1)\n if (req === null) return false\n\n this._sendBlockRequest(req, b)\n\n return true\n }\n\n _maybeWant(index, handle) {\n if (index < this.remoteContiguousLength) return\n\n const res = this.wants.add(index, handle)\n if (!res) return\n\n const batch = res.want && res.unwant\n if (batch) this.protomux.cork()\n\n if (res.unwant) {\n this.wireUnwant.send(res.unwant)\n incrementTx(this.stats.wireUnwant, this.replicator.stats.wireUnwant)\n }\n if (res.want) {\n this.wireWant.send(res.want)\n incrementTx(this.stats.wireWant, this.replicator.stats.wireWant)\n }\n\n if (batch) this.protomux.uncork()\n }\n\n _maybeWantAnyRange(start, length, handle) {\n if (handle.wants) {\n for (const w of handle.wants) {\n if ((w.start !== start || w.length !== length) && w.any) {\n const req = this.wants.removeAnyRange(w.start, w.length, handle)\n if (req) this.wireUnwant.send(req)\n }\n }\n }\n\n if (start + length <= this.remoteContiguousLength) return\n\n const req = this.wants.addAnyRange(start, length, handle)\n if (req) this.wireWant.send(req)\n }\n\n _requestRangeBlock(index, length) {\n if (this.core.bitfield.get(index) === true || !this._canRequest(index, false)) return false\n\n const b = this.replicator._blocks.add(index, PRIORITY.NORMAL, false)\n if (b.inflight.length > 0) {\n this.missingBlocks.set(index, false) // in case we missed some states just set them ondemand, nbd\n return false\n }\n\n const req = this._makeRequest(index >= length, b.priority, index + 1)\n\n // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it\n if (req === null) {\n b.gc()\n return false\n }\n\n this._sendBlockRequest(req, b)\n\n // Don't think this will ever happen, as the pending queue is drained before the range queue\n // but doesn't hurt to check this explicitly here also.\n if (b.queued) b.queued = false\n return true\n }\n\n _findNext(i) {\n if (i < this._remoteContiguousLength) {\n if (this.core.skipBitfield === null) this.replicator._openSkipBitfield()\n i = this.core.skipBitfield.findFirst(false, i)\n if (i < this._remoteContiguousLength && i > -1) return i\n i = this._remoteContiguousLength\n }\n\n return this.missingBlocks.findFirst(true, i)\n }\n\n _addRangeBatch(r, offset, end) {\n while (offset < end) {\n const next = this.core.bitfield.findFirst(false, offset)\n if (next === -1 || next >= end) return false\n\n const b = (next - (next & (WANT_BATCH - 1))) / WANT_BATCH\n\n if (r.addBatch(b)) return true\n\n offset = (b + 1) * WANT_BATCH\n }\n\n return false\n }\n\n _populateRangeBatches(r) {\n for (let i = r.batches.length - 1; i >= 0; i--) {\n const b = r.batches[i]\n const minStart = b.batch * WANT_BATCH\n const maxEnd = minStart + WANT_BATCH\n\n const start = Math.max(r.start, minStart)\n const end = Math.min(r.end === -1 ? maxEnd : r.end, maxEnd)\n\n if (start < end && this.core.bitfield.hasUnset(start, end - start)) continue\n\n r.removeBatch(b)\n }\n\n while (r.batches.length < 3) {\n const end = r.end === -1 ? this.core.state.length : r.end\n if (end <= r.start) return\n\n const len = end - r.start\n const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))\n\n if (!this._addRangeBatch(r, off, end) && !this._addRangeBatch(r, r.start, end)) break\n }\n }\n\n _requestRangeBatch(start, end, length) {\n let tries = this.inflight\n\n do {\n start = this._findNext(start)\n if (start === -1 || start >= end) break\n\n if (this._requestRangeBlock(start, length)) return true\n start++\n } while (tries-- > 0)\n\n return false\n }\n\n _getValidEnd(maxEnd) {\n // if we can upgrade the remote, or the remote is ahead, then all the remotes blocks are valid\n // otherwise truncate to the last length the remote has acked for us\n const maxLocalLength =\n this.canUpgrade || this.remoteLength >= this.core.state.length\n ? this.core.state.length\n : this.core.state.fork === this.lastUpgradableFork\n ? Math.min(this.lastUpgradableLength, this.core.state.length)\n : 0\n\n return Math.min(\n maxLocalLength,\n Math.min(maxEnd === -1 ? this.remoteLength : maxEnd, this.remoteLength)\n )\n }\n\n _requestRange(r) {\n if (this.syncsProcessing > 0) return false\n\n const { length, fork } = this.core.state\n\n if (r.blocks) {\n for (let i = r.start; i < r.end; i++) {\n const index = r.blocks[i]\n const has = index < this._remoteContiguousLength || this.missingBlocks.get(index) === true\n if (has === true && this._requestRangeBlock(index, length)) return true\n this._maybeWant(index, r)\n }\n\n return false\n }\n\n this._populateRangeBatches(r)\n\n const end = this._getValidEnd(r.end)\n\n if (end <= r.start || fork !== this.remoteFork) return false\n\n for (let i = 0; i < r.batches.length; i++) {\n const b = r.batches[i]\n\n const batchStart = Math.max(b.batch * WANT_BATCH, r.start)\n const batchEnd = Math.min((b.batch + 1) * WANT_BATCH, end)\n const len = batchEnd - batchStart\n const off = batchStart + (r.linear ? 0 : Math.floor(Math.random() * len))\n\n if (\n this._requestRangeBatch(off, end, length) ||\n this._requestRangeBatch(batchStart, off, length)\n ) {\n return true\n }\n\n this._maybeWant(batchStart, b)\n }\n\n // Here we should consider making an OOB request (ie if the remote says they have a newer block)\n // but the upgrade code already does that, so no big deal for now\n\n return false\n }\n\n _requestForkProof(f) {\n if (!this.remoteLength) return\n if (this.replicator.pushOnly) return\n\n const req = this._makeRequest(false, 0, 0)\n if (req === null) return\n\n req.upgrade = { start: 0, length: this.remoteLength }\n req.manifest = !this.core.header.manifest\n\n f.inflight.push(req)\n this._send(req)\n }\n\n _requestForkRange(f) {\n if (f.fork !== this.remoteFork || f.batch.want === null) return false\n if (this.replicator.pushOnly) return false\n\n const end = Math.min(f.batch.want.end, this.remoteLength)\n if (end < f.batch.want.start) return false\n\n const len = end - f.batch.want.start\n const off = f.batch.want.start + Math.floor(Math.random() * len)\n\n for (let i = 0; i < len; i++) {\n let index = off + i\n if (index >= end) index -= len\n\n if (this._remoteHasBlock(index) === false) continue\n\n const req = this._makeRequest(false, 0, 0)\n if (req === null) return false\n\n req.hash = { index: 2 * index, nodes: f.batch.want.nodes }\n\n f.inflight.push(req)\n this._send(req)\n\n return true\n }\n\n this._maybeWantAnyRange(f.batch.want.start, len, f)\n return false\n }\n\n isActive() {\n if (this.paused || this.removed || this.core.header.frozen) return false\n return true\n }\n\n async _send(req) {\n const fork = this.core.state.fork\n\n this.inflight++\n this.replicator._inflight.add(req)\n\n if (req.upgrade !== null && req.fork === fork) {\n const u = this.replicator._addUpgrade()\n u.inflight.push(req)\n }\n\n try {\n if (req.block !== null && req.fork === fork) {\n req.block.nodes = await MerkleTree.missingNodes(\n this.core.state,\n 2 * req.block.index,\n this.core.state.length\n )\n if (req.priority === PRIORITY.CANCELLED) return\n }\n if (req.hash !== null && req.fork === fork && req.hash.nodes === 0) {\n req.hash.nodes = await MerkleTree.missingNodes(\n this.core.state,\n req.hash.index,\n this.core.state.length\n )\n if (req.priority === PRIORITY.CANCELLED) return\n\n // nodes === 0, we already have it, bail\n if (req.hash.nodes === 0 && (req.hash.index & 1) === 0) {\n this.inflight--\n this.replicator._resolveHashLocally(this, req)\n return\n }\n }\n } catch (err) {\n this.stream.destroy(err)\n return\n }\n\n this.wireRequest.send(req)\n incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest)\n }\n}\n\nmodule.exports = class Replicator {\n static Peer = Peer // hack to be able to access Peer from outside this module\n\n constructor(\n core,\n {\n notDownloadingLinger = NOT_DOWNLOADING_SLACK,\n eagerUpgrade = true,\n allowFork = true,\n allowPush = false,\n alwaysLatestBlock = false,\n inflightRange = null\n } = {}\n ) {\n this.core = core\n this.eagerUpgrade = eagerUpgrade\n this.allowFork = allowFork\n this.allowPush = allowPush\n this.ondownloading = null // optional external hook for monitoring downloading status\n this.peers = []\n this.findingPeers = 0 // updatable from the outside\n this.destroyed = false\n this.downloading = false\n this.pushOnly = false\n this.activeSessions = 0\n\n this.hotswaps = new HotswapQueue()\n this.inflightRange = inflightRange || DEFAULT_MAX_INFLIGHT\n\n // Note: nodata and unwant not currently tracked\n // tx = transmitted, rx = received\n this.stats = {\n wireSync: { tx: 0, rx: 0 },\n wireRequest: { tx: 0, rx: 0 },\n wireCancel: { tx: 0, rx: 0 },\n wireData: { tx: 0, rx: 0 },\n wireWant: { tx: 0, rx: 0 },\n wireUnwant: { tx: 0, rx: 0 },\n wireBitfield: { tx: 0, rx: 0 },\n wireRange: { tx: 0, rx: 0 },\n wireExtension: { tx: 0, rx: 0 },\n hotswaps: 0,\n invalidData: 0,\n invalidRequests: 0,\n backoffs: 0\n }\n\n this._attached = new Set()\n this._inflight = new InflightTracker()\n this._blocks = new BlockTracker(this)\n this._hashes = new BlockTracker(this)\n\n this._alwaysLatestBlock = alwaysLatestBlock ? 1 : 0\n this._queued = []\n\n this._seeks = []\n this._upgrade = null\n this._reorgs = []\n this._ranges = []\n\n this._pushLock = new Mutex()\n this._hadPeers = false\n this._active = 0\n this._ifAvailable = 0\n this._updatesPending = 0\n this._applyingReorg = null\n this._manifestPeer = null\n this._notDownloadingLinger = notDownloadingLinger\n this._notDownloadingTimer = null\n\n const self = this\n this._onstreamclose = onstreamclose\n\n function onstreamclose() {\n self.detachFrom(this.userData)\n }\n }\n\n setInflightRange(min, max) {\n this.inflightRange = [min, max]\n }\n\n updateActivity(inc, session) {\n this.activeSessions += inc\n this.setDownloading(this.activeSessions !== 0, session)\n }\n\n isDownloading() {\n return this.downloading || !this._inflight.idle\n }\n\n async push(index) {\n const all = []\n for (const peer of this.peers) {\n all.push(peer.push(index))\n }\n await Promise.all(all)\n }\n\n setAllowPush(allowPush) {\n if (allowPush === this.allowPush) return\n this.allowPush = allowPush\n for (const peer of this.peers) peer.sendSync()\n }\n\n isAllowingPush() {\n if (!this.allowPush) return false\n if (this._notDownloadingTimer) return false\n return this.downloading\n }\n\n setPushOnly(pushOnly) {\n if (pushOnly === this.pushOnly) return\n this.pushOnly = pushOnly\n }\n\n setDownloading(downloading) {\n const allowPush = this.isAllowingPush()\n const linger = this._notDownloadingLinger\n\n if (this._notDownloadingTimer) {\n clearTimeout(this._notDownloadingTimer)\n this._notDownloadingTimer = null\n }\n\n if (this.destroyed) return\n\n if (downloading || linger === 0) {\n if (!this._setDownloadingNow(downloading)) this.signalAllowPush(allowPush)\n return\n }\n\n this._notDownloadingTimer = setTimeout(setNotDownloadingLater, linger, this)\n if (this._notDownloadingTimer.unref) this._notDownloadingTimer.unref()\n this.signalAllowPush(allowPush)\n }\n\n signalAllowPush(allowPush) {\n if (allowPush === this.isAllowingPush()) return\n for (const peer of this.peers) peer.signalUpgrade()\n }\n\n _setDownloadingNow(downloading) {\n this._notDownloadingTimer = null\n\n if (this.downloading === downloading) return false\n this.downloading = downloading\n\n if (!downloading && this.isDownloading()) return false\n\n for (const peer of this.peers) peer.signalUpgrade()\n\n if (downloading) {\n // restart channel if needed...\n for (const protomux of this._attached) {\n if (!protomux.stream.handshakeHash) continue\n if (protomux.opened({ protocol: 'hypercore/alpha', id: this.core.discoveryKey })) continue\n this._makePeer(protomux, true)\n }\n } else {\n for (const peer of this.peers) peer.closeIfIdle()\n }\n\n if (this.ondownloading !== null && downloading) this.ondownloading()\n return true\n }\n\n cork() {\n for (const peer of this.peers) peer.protomux.cork()\n }\n\n uncork() {\n for (const peer of this.peers) peer.protomux.uncork()\n }\n\n // Called externally when a range of new blocks has been processed/removed\n onhave(start, length, drop = false) {\n for (const peer of this.peers) peer.broadcastRange(start, length, drop)\n }\n\n // Called externally when a truncation upgrade has been processed\n ontruncate(newLength, truncated) {\n const notify = []\n\n for (const blk of this._blocks) {\n if (blk.index < newLength) continue\n notify.push(blk)\n }\n\n for (const blk of notify) {\n for (const r of blk.refs) {\n if (r.snapshot === false) continue\n blk.detach(r, SNAPSHOT_NOT_AVAILABLE())\n }\n }\n\n for (const peer of this.peers) {\n peer.fullyDownloadedSignaled = Math.min(newLength, peer.fullyDownloadedSignaled)\n peer._unclearLocalRange(newLength, truncated)\n }\n }\n\n // Called externally when a upgrade has been processed\n onupgrade() {\n for (const peer of this.peers) peer.signalUpgrade()\n if (this._blocks.isEmpty() === false) this._resolveBlocksLocally()\n if (this._upgrade !== null) this._resolveUpgradeRequest(null)\n if (!this._blocks.isEmpty() || this._ranges.length !== 0 || this._seeks.length !== 0) {\n this._updateNonPrimary(true)\n }\n }\n\n // Called externally when a conflict has been detected and verified\n async onconflict() {\n const all = []\n for (const peer of this.peers) {\n all.push(peer._onconflict())\n }\n await Promise.allSettled(all)\n }\n\n async applyPendingReorg() {\n if (this._applyingReorg !== null) {\n await this._applyingReorg\n return true\n }\n\n for (let i = this._reorgs.length - 1; i >= 0; i--) {\n const f = this._reorgs[i]\n if (f.batch !== null && f.batch.finished) {\n await this._applyReorg(f)\n return true\n }\n }\n\n return false\n }\n\n addUpgrade(session) {\n if (this._upgrade !== null) {\n const ref = this._upgrade.attach(session)\n this._checkUpgradeIfAvailable()\n return ref\n }\n\n const ref = this._addUpgrade().attach(session)\n\n this.updateAll()\n\n return ref\n }\n\n addBlock(session, index, force) {\n const b = this._blocks.add(index, PRIORITY.HIGH, force)\n const ref = b.attach(session)\n\n this._queueBlock(b)\n this.updateAll()\n\n return ref\n }\n\n addSeek(session, seeker) {\n const s = new SeekRequest(this, this._seeks, seeker)\n const ref = s.attach(session)\n\n this._seeks.push(s)\n this.updateAll()\n\n return ref\n }\n\n addRange(\n session,\n {\n start = 0,\n end = -1,\n length = toLength(start, end),\n blocks = null,\n linear = false,\n ifAvailable = false\n } = {}\n ) {\n if (blocks !== null) {\n // if using blocks, start, end just acts as frames around the blocks array\n start = 0\n end = length = blocks.length\n }\n\n const r = new RangeRequest(\n this,\n this._ranges,\n start,\n length === -1 ? -1 : start + length,\n linear,\n ifAvailable,\n blocks\n )\n\n const ref = r.attach(session)\n\n // Trigger this to see if this is already resolved...\n // Also auto compresses the range based on local bitfield\n clampRange(this.core, r)\n\n if (r.end !== -1 && r.start >= r.end) {\n this._resolveRangeRequest(r)\n return ref\n }\n\n this.updateAll()\n\n return ref\n }\n\n cancel(ref) {\n ref.context.detach(ref, null)\n }\n\n static clearRequests(session, err) {\n if (session.length === 0) return\n\n const updated = new Set()\n\n while (session.length > 0) {\n const ref = session[session.length - 1]\n updated.add(ref.context.replicator)\n ref.context.detach(ref, err)\n }\n\n for (const replicator of updated) {\n replicator.updateAll()\n }\n }\n\n clearRequests(session, err = null) {\n let cleared = false\n while (session.length > 0) {\n const ref = session[session.length - 1]\n ref.context.detach(ref, err)\n cleared = true\n }\n\n if (cleared) this.updateAll()\n }\n\n _matchingRequest(req, data) {\n if (this.allowPush) {\n return true\n }\n if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) {\n return false\n }\n if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) {\n return false\n }\n if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) {\n return false\n }\n if (data.upgrade !== null && req.upgrade === null) {\n return false\n }\n return req.fork === data.fork\n }\n\n _addUpgradeMaybe() {\n return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade\n }\n\n // TODO: this function is OVER called atm, at each updatePeer/updateAll\n // instead its more efficient to only call it when the conditions in here change - ie on sync/add/remove peer\n // Do this when we have more tests.\n _checkUpgradeIfAvailable() {\n if (this._ifAvailable > 0 && this.peers.length < MAX_PEERS_UPGRADE) return\n if (this._upgrade === null || this._upgrade.refs.length === 0) return\n if (this._hadPeers === false && this.findingPeers > 0) return\n\n const maxPeers = Math.min(this.peers.length, MAX_PEERS_UPGRADE)\n\n // check if a peer can upgrade us\n\n for (let i = 0; i < maxPeers; i++) {\n const peer = this.peers[i]\n\n if (peer.remoteSynced === false) return\n\n if (this.core.state.length === 0 && peer.remoteLength > 0) return\n\n if (peer.remoteLength <= this._upgrade.length || peer.remoteFork !== this._upgrade.fork) {\n continue\n }\n\n if (peer.syncsProcessing > 0) return\n\n if (peer.lengthAcked !== this.core.state.length && peer.remoteFork === this.core.state.fork) {\n return\n }\n\n if (peer.remoteCanUpgrade === true) return\n }\n\n // check if reorgs in progress...\n\n if (this._applyingReorg !== null) return\n\n // TODO: we prob should NOT wait for inflight reorgs here, seems better to just resolve the upgrade\n // and then apply the reorg on the next call in case it's slow - needs some testing in practice\n\n for (let i = 0; i < this._reorgs.length; i++) {\n const r = this._reorgs[i]\n if (r.inflight.length > 0) return\n }\n\n // if something is inflight, wait for that first\n if (this._upgrade.inflight.length > 0) return\n\n // nothing to do, indicate no update avail\n\n const u = this._upgrade\n this._upgrade = null\n u.resolve(false)\n }\n\n _addUpgrade() {\n if (this._upgrade !== null) return this._upgrade\n\n // TODO: needs a reorg: true/false flag to indicate if the user requested a reorg\n this._upgrade = new UpgradeRequest(this, this.core.state.fork, this.core.state.length)\n\n return this._upgrade\n }\n\n _addReorg(fork, peer) {\n if (this.allowFork === false) return null\n\n // TODO: eager gc old reorgs from the same peer\n // not super important because they'll get gc'ed when the request finishes\n // but just spam the remote can do ...\n\n for (const f of this._reorgs) {\n if (f.fork > fork && f.batch !== null) return null\n if (f.fork === fork) return f\n }\n\n const f = new ForkRequest(this, fork)\n\n this._reorgs.push(f)\n\n // maintain sorted by fork\n let i = this._reorgs.length - 1\n while (i > 0 && this._reorgs[i - 1].fork > fork) {\n this._reorgs[i] = this._reorgs[i - 1]\n this._reorgs[--i] = f\n }\n\n return f\n }\n\n _shouldUpgrade(peer) {\n if (this._upgrade !== null && this._upgrade.inflight.length > 0) return false\n return (\n peer.remoteCanUpgrade === true &&\n peer.remoteLength > this.core.state.length &&\n peer.lengthAcked === this.core.state.length\n )\n }\n\n _autoUpgrade(peer) {\n return (\n this._upgrade !== null &&\n peer.remoteFork === this.core.state.fork &&\n this._shouldUpgrade(peer)\n )\n }\n\n _addPeer(peer) {\n this._hadPeers = true\n this.peers.push(peer)\n this.updatePeer(peer)\n this._onpeerupdate(true, peer)\n }\n\n _requestDone(id, roundtrip) {\n this._inflight.remove(id, roundtrip)\n if (this.isDownloading() === true) return\n for (const peer of this.peers) peer.signalUpgrade()\n }\n\n _removePeer(peer) {\n this.peers.splice(this.peers.indexOf(peer), 1)\n peer.wants.destroy()\n\n if (this._manifestPeer === peer) this._manifestPeer = null\n\n for (const req of this._inflight) {\n if (req.peer !== peer) continue\n this._inflight.remove(req.id, true)\n this._clearRequest(peer, req)\n }\n\n this._onpeerupdate(false, peer)\n this.updateAll()\n }\n\n _queueBlock(b) {\n if (b.inflight.length > 0 || b.queued === true) return\n b.queued = true\n this._queued.push(b)\n }\n\n _resolveHashLocally(peer, req) {\n this._requestDone(req.id, false)\n this._resolveBlockRequest(this._hashes, req.hash.index / 2, null, req)\n this.updatePeer(peer)\n }\n\n // Runs in the background - not allowed to throw\n async _resolveBlocksLocally() {\n // TODO: check if fork compat etc. Requires that we pass down truncation info\n\n const clear = []\n const blocks = []\n\n const reader = this.core.storage.read()\n for (const b of this._blocks) {\n if (this.core.bitfield.get(b.index) === false) continue\n blocks.push(this._resolveLocalBlock(b, reader, clear))\n }\n reader.tryFlush()\n\n await Promise.all(blocks)\n\n if (!clear.length) return\n\n // Currently the block tracker does not support deletes during iteration, so we make\n // sure to clear them afterwards.\n for (const b of clear) {\n this._blocks.remove(b.index)\n removeHotswap(b)\n }\n }\n\n async _resolveLocalBlock(b, reader, resolved) {\n try {\n b.resolve(await reader.getBlock(b.index))\n } catch (err) {\n b.reject(err)\n return\n }\n\n resolved.push(b)\n }\n\n _resolveBlockRequest(tracker, index, value, req) {\n const b = tracker.remove(index)\n if (b === null) return false\n\n removeInflight(b.inflight, req)\n removeHotswap(b)\n b.queued = false\n\n b.resolve(value)\n\n if (b.inflight.length > 0) {\n // if anything is still inflight, cancel it\n for (let i = b.inflight.length - 1; i >= 0; i--) {\n const req = b.inflight[i]\n req.peer._cancelRequest(req)\n }\n }\n\n return true\n }\n\n _resolveUpgradeRequest(req) {\n if (req !== null) removeInflight(this._upgrade.inflight, req)\n\n if (\n this.core.state.length === this._upgrade.length &&\n this.core.state.fork === this._upgrade.fork\n ) {\n return false\n }\n\n const u = this._upgrade\n this._upgrade = null\n u.resolve(true)\n\n return true\n }\n\n _resolveRangeRequest(req) {\n req.resolve(true)\n req.gc()\n }\n\n _clearInflightBlock(tracker, req) {\n const isBlock = tracker === this._blocks\n const index = isBlock === true ? req.block.index : req.hash.index / 2\n const b = tracker.get(index)\n\n if (b === null || removeInflight(b.inflight, req) === false) return\n\n if (removeHotswap(b) === true && b.inflight.length > 0) {\n this.hotswaps.add(b)\n }\n\n if (b.refs.length > 0 && isBlock === true) {\n this._queueBlock(b)\n return\n }\n\n b.gc()\n }\n\n _clearInflightUpgrade(req) {\n if (this._upgrade === null || removeInflight(this._upgrade.inflight, req) === false) return\n this._upgrade.gc()\n }\n\n _clearInflightSeeks(req) {\n for (const s of this._seeks) {\n if (removeInflight(s.inflight, req) === false) continue\n s.gc()\n }\n }\n\n _clearInflightReorgs(req) {\n for (const r of this._reorgs) {\n removeInflight(r.inflight, req)\n }\n }\n\n _clearOldReorgs(fork) {\n for (let i = 0; i < this._reorgs.length; i++) {\n const f = this._reorgs[i]\n if (f.fork >= fork) continue\n if (i === this._reorgs.length - 1) this._reorgs.pop()\n else this._reorgs[i] = this._reorgs.pop()\n i--\n }\n }\n\n // \"slow\" updates here - async but not allowed to ever throw\n async _updateNonPrimary(updateAll) {\n // Check if running, if so skip it and the running one will issue another update for us (debounce)\n while (++this._updatesPending === 1) {\n let len = Math.min(MAX_RANGES, this._ranges.length)\n\n for (let i = 0; i < len; i++) {\n const r = this._ranges[i]\n\n clampRange(this.core, r)\n\n if (r.end !== -1 && r.start >= r.end) {\n this._resolveRangeRequest(r)\n i--\n if (len > this._ranges.length) len--\n if (this._ranges.length === MAX_RANGES) updateAll = true\n }\n }\n\n for (let i = 0; i < this._seeks.length; i++) {\n const s = this._seeks[i]\n\n let err = null\n let res = null\n\n try {\n res = await s.seeker.update()\n } catch (error) {\n err = error\n }\n\n if (!res && !err) continue\n\n if (i < this._seeks.length - 1) this._seeks[i] = this._seeks.pop()\n else this._seeks.pop()\n\n i--\n\n if (err) s.reject(err)\n else s.resolve(res)\n }\n\n // No additional updates scheduled - break\n if (--this._updatesPending === 0) break\n // Debounce the additional updates - continue\n this._updatesPending = 0\n }\n\n if (this._inflight.idle || updateAll) this.updateAll()\n }\n\n _maybeResolveIfAvailableRanges() {\n if (this._ifAvailable > 0 || !this._inflight.idle || !this._ranges.length) return\n\n for (let i = 0; i < this.peers.length; i++) {\n if (this.peers[i].dataProcessing > 0) return\n }\n\n for (let i = 0; i < this._ranges.length; i++) {\n const r = this._ranges[i]\n\n if (r.ifAvailable) {\n this._resolveRangeRequest(r)\n i--\n }\n }\n }\n\n _clearRequest(peer, req) {\n if (req.block !== null) {\n this._clearInflightBlock(this._blocks, req)\n this._unmarkInflight(req.block.index)\n }\n\n if (req.hash !== null) {\n this._clearInflightBlock(this._hashes, req)\n }\n\n if (req.upgrade !== null && this._upgrade !== null) {\n this._clearInflightUpgrade(req)\n }\n\n if (this._seeks.length > 0) {\n this._clearInflightSeeks(req)\n }\n\n if (this._reorgs.length > 0) {\n this._clearInflightReorgs(req)\n }\n }\n\n _onnodata(peer, req, reason) {\n if (reason === INVALID_REQUEST) {\n peer.stats.backoffs++\n this.stats.backoffs++\n\n if (peer.stats.backoffs >= MAX_BACKOFFS) {\n peer.paused = true\n }\n }\n\n if (req) {\n this._clearRequest(peer, req)\n }\n\n this.updateAll()\n }\n\n _openSkipBitfield() {\n // technically the skip bitfield gets bits cleared if .clear() is called\n // also which might be in inflight also, but that just results in that section being overcalled shortly\n // worst case, so ok for now\n\n const bitfield = this.core.openSkipBitfield()\n\n for (const req of this._inflight) {\n if (req.block) bitfield.set(req.block.index, true) // skip\n }\n }\n\n _markProcessing(index) {\n const b = this._blocks.get(index)\n if (b) {\n b.processing = true\n return\n }\n\n const h = this._hashes.get(index)\n if (h) h.processing = true\n }\n\n _markProcessed(index) {\n const b = this._blocks.get(index)\n if (b) return b.processed()\n\n const h = this._hashes.get(index)\n if (h) h.processed()\n }\n\n _markInflight(index) {\n if (this.core.skipBitfield !== null) this.core.skipBitfield.set(index, true)\n for (const peer of this.peers) peer._markInflight(index)\n }\n\n _unmarkInflight(index) {\n if (this.core.skipBitfield !== null) {\n this.core.skipBitfield.set(index, this.core.bitfield.get(index))\n }\n for (const peer of this.peers) peer._resetMissingBlock(index)\n }\n\n _ondata(peer, req, data) {\n if (req) {\n req.elapsed = Date.now() - req.timestamp\n }\n\n if (data.block !== null) {\n this._resolveBlockRequest(this._blocks, data.block.index, data.block.value, req)\n this._ondownload(data.block.index, data.block.value.byteLength, peer, req)\n }\n\n if (data.hash !== null && (data.hash.index & 1) === 0) {\n this._resolveBlockRequest(this._hashes, data.hash.index / 2, null, req)\n }\n\n if (this._upgrade !== null) {\n this._resolveUpgradeRequest(req)\n }\n\n if (this._seeks.length > 0) {\n this._clearInflightSeeks(req)\n }\n\n if (this._reorgs.length > 0) {\n this._clearInflightReorgs(req)\n }\n\n if (this._manifestPeer === peer && this.core.header.manifest !== null) {\n this._manifestPeer = null\n }\n\n if (this._seeks.length > 0 || this._ranges.length > 0) {\n this._updateNonPrimary(this._seeks.length > 0)\n }\n\n this.updatePeer(peer)\n }\n\n _onwant(peer, start, length, any) {\n if (!peer.isActive()) return\n\n const contig = Math.min(this.core.state.length, this.core.header.hints.contiguousLength)\n\n if (start + length < contig || this.core.state.length === contig || (any && start < contig)) {\n peer.wireRange.send({\n drop: false,\n start: 0,\n length: contig\n })\n incrementTx(peer.stats.wireRange, this.stats.wireRange)\n return\n }\n\n length = Math.min(length, this.core.state.length - start)\n\n if (any) {\n const bit = this.core.bitfield.firstSet(start)\n if (bit > -1 && bit < start + length) {\n peer.wireRange.send({\n drop: false,\n start: bit,\n length: 1\n })\n }\n return\n }\n\n for (const msg of this.core.bitfield.want(start, length)) {\n peer.wireBitfield.send(msg)\n incrementTx(peer.stats.wireBitfield, this.stats.wireBitfield)\n }\n }\n\n async _onreorgdata(peer, req, data) {\n const newBatch = data.upgrade && (await this.core.verifyReorg(data))\n const f = this._addReorg(data.fork, peer)\n\n if (f === null) {\n this.updateAll()\n return\n }\n\n removeInflight(f.inflight, req)\n\n if (f.batch) {\n await f.batch.update(data)\n } else if (data.upgrade) {\n f.batch = newBatch\n\n // Remove \"older\" reorgs in progress as we just verified this one.\n this._clearOldReorgs(f.fork)\n }\n\n if (f.batch && f.batch.finished) {\n if (this._addUpgradeMaybe() !== null) {\n await this._applyReorg(f)\n }\n }\n\n this.updateAll()\n }\n\n // Never throws, allowed to run in the background\n async _applyReorg(f) {\n // TODO: more optimal here to check if potentially a better reorg\n // is available, ie higher fork, and request that one first.\n // This will request that one after this finishes, which is fine, but we\n // should investigate the complexity in going the other way\n\n const u = this._upgrade\n const old = this._reorgs\n\n this._reorgs = [] // clear all as the nodes are against the old tree - easier\n this._applyingReorg = this.core.reorg(f.batch, null) // TODO: null should be the first/last peer?\n\n try {\n await this._applyingReorg\n } catch (err) {\n this._upgrade = null\n u.reject(err)\n }\n\n this._applyingReorg = null\n\n if (this._upgrade !== null) {\n this._resolveUpgradeRequest(null)\n }\n\n for (const peer of this.peers) this._updateFork(peer)\n\n // TODO: all the remaining is a tmp workaround until we have a flag/way for ANY_FORK\n for (const r of this._ranges) {\n r.start = r.userStart\n r.end = r.userEnd\n }\n\n for (const f of old) f.resolve()\n f.resolve()\n\n this.updateAll()\n }\n\n _maybeUpdate() {\n return this._upgrade !== null && this._upgrade.inflight.length === 0\n }\n\n _maybeRequestManifest() {\n return this.core.header.manifest === null && this._manifestPeer === null\n }\n\n _updateFork(peer) {\n if (\n this._applyingReorg !== null ||\n this.allowFork === false ||\n peer.remoteFork <= this.core.state.fork\n ) {\n return false\n }\n\n const f = this._addReorg(peer.remoteFork, peer)\n\n // TODO: one per peer is better\n if (f !== null && f.batch === null && f.inflight.length === 0) {\n return peer._requestForkProof(f)\n }\n\n return false\n }\n\n _updateHotswap(peer) {\n const maxHotswaps = peer.getMaxHotswapInflight()\n if (!peer.isActive() || peer.inflight >= maxHotswaps) return\n\n for (const b of this.hotswaps.pick(peer)) {\n if (peer._requestBlock(b) === false) continue\n peer.stats.hotswaps++\n peer.replicator.stats.hotswaps++\n if (peer.inflight >= maxHotswaps) break\n }\n }\n\n _updatePeer(peer) {\n if (!peer.isActive() || peer.inflight >= peer.getMaxInflight() || !peer.remoteUploading) {\n return false\n }\n\n // Eagerly request the manifest even if the remote length is 0. If not 0 we'll get as part of the upgrade request...\n if (\n this._maybeRequestManifest() === true &&\n peer.remoteLength === 0 &&\n peer.remoteHasManifest === true\n ) {\n this._manifestPeer = peer\n peer._requestManifest()\n }\n\n for (const s of this._seeks) {\n if (s.inflight.length > 0) continue // TODO: one per peer is better\n if (peer._requestSeek(s) === true) {\n return true\n }\n }\n\n // Implied that any block in the queue should be requested, no matter how many inflights\n const blks = new RandomIterator(this._queued)\n\n for (const b of blks) {\n if (b.queued === false || peer._requestBlock(b) === true) {\n b.queued = false\n blks.dequeue()\n return true\n }\n }\n\n return false\n }\n\n _updatePeerNonPrimary(peer) {\n if (!peer.isActive() || peer.inflight >= peer.getMaxInflight() || !peer.remoteUploading) {\n return false\n }\n\n const ranges = new RandomIterator(this._ranges)\n let tried = 0\n\n for (const r of ranges) {\n if (peer._requestRange(r) === true) {\n return true\n }\n if (++tried >= MAX_RANGES) break\n }\n\n // Iterate from newest fork to oldest fork...\n for (let i = this._reorgs.length - 1; i >= 0; i--) {\n const f = this._reorgs[i]\n if (f.batch !== null && f.inflight.length === 0 && peer._requestForkRange(f) === true) {\n return true\n }\n }\n\n if (this._maybeUpdate() === true && peer._requestUpgrade(this._upgrade) === true) {\n return true\n }\n\n return false\n }\n\n updatePeer(peer) {\n // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests\n if (this._applyingReorg !== null) return\n\n while (this._updatePeer(peer) === true);\n while (this._updatePeerNonPrimary(peer) === true);\n\n if (this.peers.length > 1 && this._blocks.isEmpty() === false) {\n this._updateHotswap(peer)\n }\n\n this._checkUpgradeIfAvailable()\n this._maybeResolveIfAvailableRanges()\n }\n\n updateAll() {\n // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests\n if (this._applyingReorg !== null) return\n\n const peers = new RandomIterator(this.peers)\n\n for (const peer of peers) {\n if (this._updatePeer(peer) === true) {\n peers.requeue()\n }\n }\n\n // Check if we can skip the non primary check fully\n if (this._maybeUpdate() === false && this._ranges.length === 0 && this._reorgs.length === 0) {\n this._checkUpgradeIfAvailable()\n return\n }\n\n for (const peer of peers.restart()) {\n if (this._updatePeerNonPrimary(peer) === true) {\n peers.requeue()\n }\n }\n\n this._checkUpgradeIfAvailable()\n this._maybeResolveIfAvailableRanges()\n }\n\n onpeerdestroy() {\n if (--this._active === 0) this.core.checkIfIdle()\n }\n\n attached(protomux) {\n return this._attached.has(protomux)\n }\n\n attachTo(protomux) {\n if (this.core.closed) return\n\n const makePeer = this._makePeer.bind(this, protomux)\n\n this._attached.add(protomux)\n protomux.pair({ protocol: 'hypercore/alpha', id: this.core.discoveryKey }, makePeer)\n protomux.stream.setMaxListeners(0)\n protomux.stream.on('close', this._onstreamclose)\n\n this._ifAvailable++\n this._active++\n\n protomux.stream.opened.then((opened) => {\n this._ifAvailable--\n this._active--\n\n if (opened && !this.destroyed) makePeer()\n this._checkUpgradeIfAvailable()\n\n this.core.checkIfIdle()\n })\n }\n\n detachFrom(protomux) {\n if (this._attached.delete(protomux)) {\n protomux.stream.removeListener('close', this._onstreamclose)\n protomux.unpair({ protocol: 'hypercore/alpha', id: this.core.discoveryKey })\n }\n }\n\n idle() {\n return this.peers.length === 0 && this._active === 0\n }\n\n close() {\n const waiting = []\n\n for (const peer of this.peers) {\n waiting.push(peer.channel.fullyClosed())\n }\n\n this.destroy()\n return Promise.all(waiting)\n }\n\n destroy() {\n if (this.destroyed) return\n this.destroyed = true\n\n if (this._notDownloadingTimer) {\n clearTimeout(this._notDownloadingTimer)\n this._notDownloadingTimer = null\n }\n\n while (this.peers.length) {\n const peer = this.peers[this.peers.length - 1]\n this.detachFrom(peer.protomux)\n peer.channel.close() // peer is removed from array in onclose\n }\n\n for (const protomux of this._attached) {\n this.detachFrom(protomux)\n }\n }\n\n _makePeer(protomux) {\n const replicator = this\n if (protomux.opened({ protocol: 'hypercore/alpha', id: this.core.discoveryKey })) {\n return onnochannel()\n }\n\n const channel = protomux.createChannel({\n userData: null,\n protocol: 'hypercore/alpha',\n aliases: ['hypercore'],\n id: this.core.discoveryKey,\n handshake: m.wire.handshake,\n messages: [\n { encoding: m.wire.sync, onmessage: onwiresync },\n { encoding: m.wire.request, onmessage: onwirerequest },\n { encoding: m.wire.cancel, onmessage: onwirecancel },\n { encoding: m.wire.data, onmessage: onwiredata },\n { encoding: m.wire.noData, onmessage: onwirenodata },\n { encoding: m.wire.want, onmessage: onwirewant },\n { encoding: m.wire.unwant, onmessage: onwireunwant },\n { encoding: m.wire.bitfield, onmessage: onwirebitfield },\n { encoding: m.wire.range, onmessage: onwirerange },\n { encoding: m.wire.extension, onmessage: onwireextension }\n ],\n onopen: onwireopen,\n onclose: onwireclose,\n ondrain: onwiredrain,\n ondestroy: onwiredestroy\n })\n\n if (channel === null) return onnochannel()\n\n const peer = new Peer(replicator, protomux, channel, this.inflightRange)\n const stream = protomux.stream\n\n peer.channel.open({\n seeks: true,\n capability: caps.replicate(stream.isInitiator, this.core.key, stream.handshakeHash)\n })\n\n return true\n\n function onnochannel() {\n return false\n }\n }\n\n _onpeerupdate(added, peer) {\n const name = added ? 'peer-add' : 'peer-remove'\n const sessions = this.core.monitors\n\n for (let i = sessions.length - 1; i >= 0; i--) {\n sessions[i].emit(name, peer)\n\n if (added) {\n for (const ext of sessions[i].extensions.values()) {\n peer.extensions.set(ext.name, ext)\n }\n }\n }\n }\n\n _ondownload(index, byteLength, from, req) {\n const sessions = this.core.monitors\n\n for (let i = sessions.length - 1; i >= 0; i--) {\n const s = sessions[i]\n s.emit('download', index, byteLength - s.padding, from, req)\n }\n }\n\n _onupload(index, byteLength, from) {\n const sessions = this.core.monitors\n\n for (let i = sessions.length - 1; i >= 0; i--) {\n const s = sessions[i]\n s.emit('upload', index, byteLength - s.padding, from)\n }\n }\n\n _oninvaliddata(err, req, res, from) {\n const sessions = this.core.monitors\n\n this.stats.invalidData++\n from.stats.invalidData++\n for (let i = 0; i < sessions.length; i++) {\n sessions[i].emit('verification-error', err, req, res, from)\n }\n }\n\n _oninvalidrequest(err, req, from) {\n const sessions = this.core.monitors\n\n from.stats.invalidRequests++\n this.stats.invalidRequests++\n for (let i = 0; i < sessions.length; i++) {\n sessions[i].emit('invalid-request', err, req, from)\n }\n }\n}\n\nfunction removeHotswap(block) {\n if (block.hotswap === null) return false\n block.hotswap.ref.remove(block)\n return true\n}\n\nfunction removeInflight(inf, req) {\n const i = inf.indexOf(req)\n if (i === -1) return false\n if (i < inf.length - 1) inf[i] = inf.pop()\n else inf.pop()\n return true\n}\n\nfunction toLength(start, end) {\n return end === -1 ? -1 : end < start ? 0 : end - start\n}\n\nfunction clampRange(core, r) {\n if (r.blocks === null) {\n const start = core.bitfield.firstUnset(r.start)\n\n if (r.end === -1) r.start = start === -1 ? core.state.length : start\n else if (start === -1 || start >= r.end) r.start = r.end\n else {\n r.start = start\n\n const end = core.bitfield.lastUnset(r.end - 1)\n\n if (end === -1 || start >= end + 1) r.end = r.start\n else r.end = end + 1\n }\n } else {\n while (r.start < r.end && core.bitfield.get(r.blocks[r.start])) r.start++\n while (r.start < r.end && core.bitfield.get(r.blocks[r.end - 1])) r.end--\n }\n}\n\nfunction onrequesttimeout(req) {\n if (req.context) req.context.detach(req, REQUEST_TIMEOUT())\n}\n\nfunction destroyRequestTimeout(req) {\n if (req.timeout !== null) {\n clearTimeout(req.timeout)\n req.timeout = null\n }\n}\n\nfunction isCriticalError(err) {\n // TODO: expose .critical or similar on the hypercore errors that are critical (if all not are)\n return err.name === 'HypercoreError'\n}\n\nfunction onwireopen(m, c) {\n return c.userData.onopen(m)\n}\n\nfunction onwireclose(isRemote, c) {\n return c.userData.onclose(isRemote)\n}\n\nfunction onwiredestroy(c) {\n c.userData.replicator.onpeerdestroy()\n}\n\nfunction onwiredrain(c) {\n return c.userData.ondrain()\n}\n\nfunction onwiresync(m, c) {\n incrementRx(c.userData.stats.wireSync, c.userData.replicator.stats.wireSync)\n return c.userData.onsync(m)\n}\n\nfunction onwirerequest(m, c) {\n incrementRx(c.userData.stats.wireRequest, c.userData.replicator.stats.wireRequest)\n return c.userData.onrequest(m)\n}\n\nfunction onwirecancel(m, c) {\n incrementRx(c.userData.stats.wireCancel, c.userData.replicator.stats.wireCancel)\n return c.userData.oncancel(m)\n}\n\nfunction onwiredata(m, c) {\n incrementRx(c.userData.stats.wireData, c.userData.replicator.stats.wireData)\n return c.userData.ondata(m)\n}\n\nfunction onwirenodata(m, c) {\n return c.userData.onnodata(m)\n}\n\nfunction onwirewant(m, c) {\n incrementRx(c.userData.stats.wireWant, c.userData.replicator.stats.wireWant)\n return c.userData.onwant(m)\n}\n\nfunction onwireunwant(m, c) {\n return c.userData.onunwant(m)\n}\n\nfunction onwirebitfield(m, c) {\n incrementRx(c.userData.stats.wireBitfield, c.userData.replicator.stats.wireBitfield)\n return c.userData.onbitfield(m)\n}\n\nfunction onwirerange(m, c) {\n incrementRx(c.userData.stats.wireRange, c.userData.replicator.stats.wireRange)\n return c.userData.onrange(m)\n}\n\nfunction onwireextension(m, c) {\n incrementRx(c.userData.stats.wireExtension, c.userData.replicator.stats.wireExtension)\n return c.userData.onextension(m)\n}\n\nfunction setNotDownloadingLater(repl, session) {\n repl._setDownloadingNow(false, session)\n}\n\nfunction isBlockRequest(req) {\n return req !== null && req.block !== null\n}\n\nfunction isUpgradeRequest(req) {\n return req !== null && req.upgrade !== null\n}\n\nfunction incrementTx(stats1, stats2) {\n stats1.tx++\n stats2.tx++\n}\n\nfunction incrementRx(stats1, stats2) {\n stats1.rx++\n stats2.rx++\n}\n\nfunction noop() {}\n\nfunction backoff(times) {\n const sleep = times < 2 ? 200 : times < 5 ? 500 : times < 40 ? 1000 : 5000\n return new Promise((resolve) => setTimeout(resolve, sleep))\n}\nconst crypto = require('hypercore-crypto')\nconst b4a = require('b4a')\nconst assert = require('nanoassert')\nconst flat = require('flat-tree')\nconst quickbit = require('quickbit-universal')\n\nconst { INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')\n\nconst Mutex = require('./mutex')\nconst Bitfield = require('./bitfield')\nconst { MerkleTree, MerkleTreeBatch } = require('./merkle-tree')\n\nmodule.exports = class SessionState {\n constructor(core, parent, storage, treeInfo, name) {\n this.core = core\n this.index = this.core.sessionStates.push(this) - 1\n\n this.storage = storage\n this.name = name\n this.sessions = []\n\n // small hack to close old storages as late as possible.\n // TODO: add a read lock so we can avoid that\n this.lingers = null\n\n this.parent = parent\n this.atomized = null\n this.mutex = new Mutex()\n\n // merkle state\n this.roots = treeInfo.roots.length ? treeInfo.roots : []\n this.fork = treeInfo.fork || 0\n this.length = MerkleTree.span(this.roots) / 2\n this.byteLength = MerkleTree.size(this.roots)\n this.prologue = treeInfo.prologue || null\n this.signature = treeInfo.signature || null\n\n this.snapshotCompatLength = this.isSnapshot()\n ? Math.min(this.length, this.core.state.length)\n : -1\n this.lastTruncation = null\n\n this.active = 0\n\n this._activeTx = null\n this._pendingBitfield = null\n\n this.ref()\n }\n\n isSnapshot() {\n return this.storage.snapshotted\n }\n\n isDefault() {\n return this.core.state === this || this.isAtomicDefault()\n }\n\n isAtomicDefault() {\n return !!this.storage.atom && !!this.parent && this.parent.isDefault()\n }\n\n createTreeBatch() {\n return new MerkleTreeBatch(this)\n }\n\n addSession(s) {\n if (s._stateIndex !== -1) return\n s._stateIndex = this.sessions.push(s) - 1\n if (s.weak === false) this.core.activeSessions++\n }\n\n removeSession(s) {\n if (s._stateIndex === -1) return\n const head = this.sessions.pop()\n if (head !== s) this.sessions[(head._stateIndex = s._stateIndex)] = head\n s._stateIndex = -1\n if (s.weak === false) this.core.activeSessions--\n this.core.checkIfIdle()\n }\n\n flushedLength() {\n if (this.isDefault() || this.isSnapshot()) return this.length\n const deps = this.storage.dependencies\n if (deps.length) return deps[deps.length - 1].length\n return 0\n }\n\n signedLength() {\n const l = Math.min(this.flushedLength(), this.core.state.length)\n return this.isSnapshot() && l > this.snapshotCompatLength ? this.snapshotCompatLength : l\n }\n\n unref() {\n if (--this.active > 0) return\n this.close().catch(noop) // technically async, but only for the last db session\n }\n\n ref() {\n this.active++\n return this\n }\n\n hash() {\n return MerkleTree.hash(this)\n }\n\n setRoots(roots) {\n this.roots = roots\n this.length = MerkleTree.span(roots) / 2\n this.byteLength = MerkleTree.size(roots)\n }\n\n get encryptionFork() {\n return this.core.header.tree.fork\n }\n\n async updateSnapshotStorage(storage) {\n if (!this.atomized || !this.atomized.flushing) return this.treeInfo()\n await this.atomized.flushed()\n\n let rx = storage.read()\n const headPromise = rx.getHead()\n const authPromise = rx.getAuth()\n const depPromise = rx.getDependency()\n\n rx.tryFlush()\n const [head, auth, dep] = await Promise.all([headPromise, authPromise, depPromise])\n\n storage.setDependencyHead(dep)\n\n const fork = head ? head.fork : 0\n const length = head ? head.length : 0\n\n rx = storage.read()\n const rootPromises = []\n for (const r of flat.fullRoots(length * 2)) {\n rootPromises.push(rx.getTreeNode(r))\n }\n\n rx.tryFlush()\n\n const roots = await Promise.all(rootPromises)\n\n // dbl check if we are hitting an regression from earler\n for (const root of roots) {\n if (root === null) {\n throw new Error(\n 'Bad snapshot from atomized session, id = ' +\n this.core.id +\n ' length = ' +\n length +\n ' fork = ' +\n fork\n )\n }\n }\n\n return {\n fork,\n roots,\n length,\n prologue: auth.manifest && auth.manifest.prologue,\n signature: head && head.signature\n }\n }\n\n treeInfo() {\n return {\n fork: this.fork,\n roots: this.roots.slice(),\n length: this.length,\n prologue: this.prologue,\n signature: this.signature\n }\n }\n\n async close() {\n if (this.index === -1) return\n\n this.active = 0\n this.mutex.destroy(new Error('Closed')).catch(noop)\n if (this.parent && this.parent.atomized) this.parent.atomized = null\n\n const closing = this.storage.close()\n\n const head = this.core.sessionStates.pop()\n if (head !== this) this.core.sessionStates[(head.index = this.index)] = head\n\n this.index = -1\n this.core.checkIfIdle()\n\n if (this.lingers !== null) {\n for (const storage of this.lingers) await storage.close()\n }\n\n return closing\n }\n\n async snapshot() {\n const storage = this.storage.snapshot()\n const treeInfo = await this.updateSnapshotStorage(storage)\n\n const s = new SessionState(this.core, null, storage, treeInfo, this.name)\n\n return s\n }\n\n updateDependency(tx, length) {\n const dependency = updateDependency(this, length, false)\n if (dependency) tx.setDependency(dependency)\n return dependency\n }\n\n _clearActiveBatch() {\n this._activeTx = null\n }\n\n createWriteBatch() {\n assert(!this._activeTx && !this.storage.snapshotted)\n\n this._activeTx = this.storage.write()\n return this._activeTx\n }\n\n _unlock() {\n this._clearActiveBatch()\n this.mutex.unlock()\n this.core.checkIfIdle()\n }\n\n async flush() {\n const tx = this._activeTx\n this._activeTx = null\n\n try {\n if (!(await tx.flush())) return false\n } finally {\n this._clearActiveBatch()\n }\n\n this.lastTruncation = null\n return true\n }\n\n _precommit() {\n this.commiting = true\n }\n\n async _commit() {\n await this.mutex.lock()\n\n try {\n const bitfield = this._pendingBitfield\n this._pendingBitfield = null\n this.lastTruncation = null\n await this.parent._oncommit(this, bitfield)\n } finally {\n this.commiting = false\n this.mutex.unlock()\n }\n }\n\n async _oncommit(src, bitfield) {\n await this.mutex.lock()\n\n try {\n const currLength = this.length\n\n // load dependency into memory\n const rx = this.storage.read()\n const dependencyPromise = rx.getDependency()\n\n rx.tryFlush()\n\n const dependency = await dependencyPromise\n\n this.fork = src.fork\n this.length = src.length\n this.byteLength = src.byteLength\n this.roots = src.roots.slice()\n this.signature = src.signature\n\n const tree = {\n fork: this.fork,\n length: this.length,\n rootHash: this.hash(),\n signature: this.signature\n }\n\n if (dependency) this.storage.setDependencyHead(dependency)\n\n const b = bitfield\n\n if (b && b.truncated && b.start < currLength) {\n this.ontruncate(tree, b.start, currLength, true)\n if (!b || b.appends === 0) return\n }\n\n const append = b ? { start: b.start, length: b.appends, drop: false } : null\n\n this.onappend(tree, append, true)\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n } finally {\n this.mutex.unlock()\n this.core.checkIfIdle()\n }\n }\n\n async setUserData(key, value) {\n await this.mutex.lock()\n\n try {\n const tx = this.createWriteBatch()\n tx.putUserData(key, value)\n\n return await this.flush()\n } finally {\n this._unlock()\n }\n }\n\n async _verifyBlock(batch, bitfield, value, manifest, from) {\n await this.mutex.lock()\n\n try {\n if (batch.upgraded && batch.length <= this.length) {\n await batch.downgrade()\n }\n\n if (!batch.commitable()) {\n return false\n }\n\n if (this.core.preupdate !== null) {\n await this.core.preupdate(batch, this.core.header.key)\n }\n\n const tx = this.createWriteBatch()\n this.updating = true\n\n if (bitfield) {\n tx.putBlock(bitfield.start, value)\n }\n\n if (bitfield && this.isDefault()) {\n await storeBitfieldRange(this.storage, tx, bitfield.start, bitfield.start + 1, true)\n }\n\n if (manifest) this.core._setManifest(tx, manifest, null)\n\n assert(batch.commitable(), 'Should still be commitable')\n batch.commit(tx)\n\n const head = {\n fork: batch.fork,\n length: batch.length,\n rootHash: batch.hash(),\n signature: batch.signature\n }\n\n if (batch.upgraded) tx.setHead(head)\n\n const flushed = await this.flush()\n\n if (batch.upgraded) {\n this.roots = batch.roots\n this.length = batch.length\n this.byteLength = batch.byteLength\n this.fork = batch.fork\n this.signature = batch.signature\n\n this.onappend(head, bitfield, flushed)\n }\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n } finally {\n this._clearActiveBatch()\n this.updating = false\n this.mutex.unlock()\n }\n\n return true\n }\n\n async truncate(length, fork, { signature, keyPair } = {}) {\n if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair\n\n await this.mutex.lock()\n\n try {\n if (this.prologue && length < this.prologue.length) {\n throw INVALID_OPERATION('Truncation breaks prologue', this.core.discoveryKey)\n }\n if (length > this.length) {\n throw INVALID_OPERATION(\n 'Not a truncation, ' + length + ' must be less or equal to ' + this.length,\n this.core.discoveryKey\n )\n }\n\n const batch = this.createTreeBatch()\n await MerkleTree.truncate(this, length, batch, fork)\n\n if (!signature && keyPair && length > 0) signature = this.core.verifier.sign(batch, keyPair)\n if (signature) batch.signature = signature\n\n const tx = this.createWriteBatch()\n\n // upsert compat manifest\n if (this.core.verifier === null && keyPair) this.core._setManifest(tx, null, keyPair)\n\n const { dependency, tree, roots } = await this._truncate(tx, batch)\n\n for (const sessionState of this.core.sessionStates) {\n if (\n sessionState.isSnapshot() &&\n sessionState.name === this.name &&\n length < sessionState.snapshotCompatLength\n ) {\n sessionState.snapshotCompatLength = length\n }\n }\n\n const flushed = await this.flush()\n\n this.fork = tree.fork\n this.length = tree.length\n this.byteLength = MerkleTree.size(roots)\n this.roots = roots\n this.signature = tree.signature\n\n if (dependency) this.storage.setDependencyHead(dependency)\n\n this.ontruncate(tree, tree.length, batch.treeLength, flushed)\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n } finally {\n this._unlock()\n }\n }\n\n async reorg(batch) {\n await this.mutex.lock()\n\n const storage = this.createWriteBatch()\n\n try {\n if (!batch.commitable()) return false\n\n const { dependency, tree } = await this._truncate(storage, batch)\n\n const flushed = await this.flush()\n\n this.fork = batch.fork\n this.length = batch.length\n this.byteLength = batch.byteLength\n this.roots = batch.roots\n this.signature = batch.signature\n\n if (dependency) this.storage.setDependencyHead(dependency)\n\n this.ontruncate(tree, batch.ancestors, batch.treeLength, flushed)\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n } finally {\n this._unlock()\n }\n }\n\n async _truncate(storage, batch) {\n storage.deleteBlockRange(batch.ancestors, batch.treeLength)\n\n assert(batch.commitable(), 'Batch must be commitable')\n\n const tree = {\n fork: batch.fork,\n length: batch.length,\n rootHash: batch.hash(),\n signature: batch.signature\n }\n\n storage.setHead(tree)\n batch.commit(storage)\n\n const truncated = batch.length < this.flushedLength()\n const dependency = truncated ? updateDependency(this, batch.length, true) : null\n\n if (dependency) storage.setDependency(dependency)\n\n if (this.isDefault()) {\n await storeBitfieldRange(this.storage, storage, batch.ancestors, batch.treeLength, false)\n if (batch.ancestors < this.core.header.hints.contiguousLength) {\n this.core.header.hints.remoteContiguousLength = Math.min(\n batch.length,\n this.core.header.hints.remoteContiguousLength\n )\n storage.setHints({\n contiguousLength: batch.ancestors,\n remoteContiguousLength: this.core.header.hints.remoteContiguousLength\n })\n }\n }\n\n return { dependency, tree, roots: batch.roots }\n }\n\n async clear(start, end, cleared) {\n await this.mutex.lock()\n\n try {\n const tx = this.createWriteBatch()\n\n const ite = flat.iterator(start * 2)\n while (!isRootIndex(ite.index, this.roots)) {\n const a = ite.index\n const b = ite.sibling()\n ite.parent()\n\n const [left, right] = flat.spans(b)\n const s = left / 2\n const e = right / 2 + 1\n const has = s <= start && e <= end ? false : this.core.bitfield.hasSet(s, e - s)\n if (has) break\n\n tx.deleteTreeNode(a)\n tx.deleteTreeNode(b)\n }\n\n if (this.isDefault()) {\n await storeBitfieldRange(this.storage, tx, start, end, false)\n if (start < this.core.header.hints.contiguousLength) {\n tx.setHints({\n contiguousLength: start,\n remoteContiguousLength: this.core.header.hints.remoteContiguousLength\n })\n }\n }\n\n if (end - start === 1) tx.deleteBlock(start)\n else tx.deleteBlockRange(start, end)\n\n const dependency = start < this.flushedLength() ? updateDependency(this, start, true) : null\n\n const flushed = await this.flush()\n\n if (dependency) this.storage.setDependencyHead(dependency)\n\n // todo: atomic event handle\n if (this.isDefault() && flushed) {\n const length = end - start\n\n this.core.updateContiguousLength({ start, length, drop: true })\n this.core._setBitfieldRanges(start, end, false)\n this.core.replicator.onhave(start, length, true)\n\n if (this.core.hintsChanged) await this.core.flushHints()\n }\n } finally {\n this._unlock()\n }\n }\n\n async append(values, { signature, keyPair, preappend, postappend, maxLength = -1 } = {}) {\n if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair\n\n await this.mutex.lock()\n\n try {\n if (maxLength >= 0 && this.length + values.length > maxLength) {\n return { length: this.length, byteLength: this.byteLength }\n }\n\n const tx = this.createWriteBatch()\n\n // upsert compat manifest\n if (this.core.verifier === null && keyPair) this.core._setManifest(tx, null, keyPair)\n\n if (preappend) await preappend(values)\n\n if (!values.length) {\n await this.flush()\n return { length: this.length, byteLength: this.byteLength }\n }\n\n const batch = this.createTreeBatch()\n for (const val of values) batch.append(val)\n\n // only multisig can have prologue so signature is always present\n if (this.prologue && batch.length < this.prologue.length) {\n throw INVALID_OPERATION('Append is not consistent with prologue', this.core.discoveryKey)\n }\n\n if (!signature && keyPair) signature = this.core.verifier.sign(batch, keyPair)\n if (signature) batch.signature = signature\n\n batch.commit(tx)\n\n const tree = {\n fork: batch.fork,\n length: batch.length,\n rootHash: batch.hash(),\n signature: batch.signature\n }\n\n tx.setHead(tree)\n\n if (this.isDefault()) {\n await storeBitfieldRange(this.storage, tx, batch.ancestors, batch.length, true)\n if (this.length === this.core.header.hints.contiguousLength) {\n tx.setHints({\n contiguousLength: this.length + values.length,\n remoteContiguousLength: this.core.header.hints.remoteContiguousLength\n })\n }\n }\n\n for (let i = 0; i < values.length; i++) {\n tx.putBlock(this.length + i, values[i])\n }\n\n const bitfield = {\n drop: false,\n start: batch.ancestors,\n length: values.length\n }\n\n const flushed = await this.flush()\n\n this.fork = batch.fork\n this.roots = batch.roots\n this.length = batch.length\n this.byteLength = batch.byteLength\n this.signature = batch.signature\n\n if (postappend) await postappend(values)\n\n this.onappend(tree, bitfield, flushed)\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n\n return { length: this.length, byteLength: this.byteLength }\n } finally {\n this._unlock()\n }\n }\n\n onappend(tree, bitfield, flushed) {\n if (!flushed) this._updateBitfield(bitfield)\n else if (this.isDefault()) this.core.onappend(tree, bitfield)\n\n for (let i = this.sessions.length - 1; i >= 0; i--) {\n this.sessions[i].emit('append')\n }\n }\n\n ontruncate(tree, to, from, flushed) {\n const bitfield = { start: to, length: from - to, drop: true }\n\n this.lastTruncation = { from, to }\n\n if (!flushed) this._updateBitfield(bitfield)\n else if (this.isDefault()) this.core.ontruncate(tree, bitfield)\n\n for (const sessionState of this.core.sessionStates) {\n if (\n sessionState.isSnapshot() &&\n sessionState.name === this.name &&\n to < sessionState.snapshotCompatLength\n ) {\n sessionState.snapshotCompatLength = to\n }\n }\n\n for (let i = this.sessions.length - 1; i >= 0; i--) {\n this.sessions[i].emit('truncate', to, tree.fork)\n }\n }\n\n _updateBitfield(bitfield, flushed) {\n if (!bitfield) return\n\n const p = this._pendingBitfield\n const b = bitfield\n\n if (b.drop) {\n // truncation must be from end\n if (p && b.start + b.length !== p.start + p.appends) {\n throw INVALID_OPERATION('Atomic truncations must be contiguous', this.core.discoveryKey)\n }\n\n // actual truncation\n if (p === null || b.start < p.start) {\n this._pendingBitfield = { truncated: true, start: b.start, appends: 0 }\n return\n }\n\n // just clearing batch data\n p.appends = b.start - p.start\n\n // we cleared the current batch\n if (p.appends === 0) this._pendingBitfield = null\n\n return\n }\n\n if (p === null) {\n this._pendingBitfield = { truncated: false, start: b.start, appends: b.length }\n return\n }\n\n if (b.start !== p.start + p.appends) {\n throw INVALID_OPERATION('Atomic operations must be contiguous', this.core.discoveryKey)\n }\n\n p.appends += b.length\n }\n\n async catchup(length) {\n assert(!this.isDefault(), 'Cannot catchup signed state') // TODO: make this check better\n\n await this.mutex.lock()\n\n try {\n const origLength = this.length\n\n let sharedLength = 0\n for (let i = this.storage.dependencies.length - 1; i >= 0; i--) {\n const dep = this.storage.dependencies[i]\n if (dep.dataPointer === this.core.state.storage.core.dataPointer) {\n sharedLength = dep.length\n break\n }\n }\n\n const tx = this.createWriteBatch()\n const rx = this.core.state.storage.read()\n const rootPromises = []\n\n for (const root of flat.fullRoots(length * 2)) {\n rootPromises.push(rx.getTreeNode(root))\n }\n\n rx.tryFlush()\n\n const roots = await Promise.all(rootPromises)\n const truncating = sharedLength < origLength\n\n for (const node of roots) {\n if (node === null) {\n throw INVALID_OPERATION(\n 'Invalid catchup length, tree nodes not available',\n this.core.discoveryKey\n )\n }\n }\n\n const fork = truncating ? this.fork + 1 : this.fork\n\n // overwrite it atm, TODO: keep what we can connect to the tree\n tx.deleteBlockRange(0, -1)\n tx.deleteTreeNodeRange(0, -1)\n tx.deleteBitfieldPageRange(0, -1)\n\n const tree = {\n fork,\n length,\n rootHash: crypto.tree(roots),\n signature: null\n }\n\n tx.setHead(tree)\n\n // prop a better way to do this\n const dep = updateDependency(this, sharedLength, true)\n dep.length = length\n\n tx.setDependency(dep)\n\n const flushed = await this.flush()\n\n this.storage.setDependencyHead(dep)\n\n this.fork = tree.fork\n this.roots = roots\n this.length = tree.length\n this.byteLength = MerkleTree.size(roots)\n\n if (truncating) this.ontruncate(tree, sharedLength, origLength, flushed)\n if (sharedLength < length) this.onappend(tree, null, flushed)\n\n if (this.core.hintsChanged && this.isDefault()) await this.core.flushHints()\n } finally {\n this.mutex.unlock()\n }\n }\n\n async _overwrite(source, fork, length, treeLength, signature) {\n const blockPromises = []\n const treePromises = []\n const rootPromises = []\n\n const rx = source.storage.read()\n\n for (const root of flat.fullRoots(length * 2)) {\n rootPromises.push(rx.getTreeNode(root))\n }\n\n for (const index of flat.patch(treeLength * 2, length * 2)) {\n treePromises.push(rx.getTreeNode(index))\n }\n\n for (let i = treeLength; i < length; i++) {\n treePromises.push(rx.getTreeNode(i * 2))\n treePromises.push(rx.getTreeNode(i * 2 + 1))\n blockPromises.push(rx.getBlock(i))\n }\n\n rx.tryFlush()\n\n const blocks = await Promise.all(blockPromises)\n const nodes = await Promise.all(treePromises)\n const roots = await Promise.all(rootPromises)\n\n if (this.core.destroyed) throw new Error('Core destroyed')\n\n if (signature) {\n const batch = this.createTreeBatch()\n batch.roots = roots\n batch.length = length\n\n if (!this.core.verifier.verify(batch, signature)) {\n throw INVALID_SIGNATURE(\n 'Signature is not valid over committed tree',\n this.core.discoveryKey\n )\n }\n }\n\n const tx = this.createWriteBatch()\n\n // truncate existing tree\n if (treeLength < this.length) {\n tx.deleteBlockRange(treeLength, this.length)\n }\n\n for (const root of roots) tx.putTreeNode(root)\n\n // no nodes will be copied in shallow mode\n for (const node of nodes) {\n if (node !== null) tx.putTreeNode(node)\n }\n\n for (let i = 0; i < blocks.length; i++) {\n assert(blocks[i] !== null, 'has block')\n tx.putBlock(i + treeLength, blocks[i])\n }\n\n const totalLength = Math.max(length, this.length)\n\n if (totalLength > treeLength) {\n const firstPage = getBitfieldPage(treeLength)\n const lastPage = getBitfieldPage(totalLength - 1)\n\n const srx = this.storage.read()\n const bitfieldPagePromise = srx.getBitfieldPage(firstPage)\n srx.tryFlush()\n\n const bitfieldPage = await bitfieldPagePromise\n\n let index = treeLength\n\n for (let i = firstPage; i <= lastPage; i++) {\n const page = b4a.alloc(Bitfield.BYTES_PER_PAGE)\n tx.putBitfieldPage(i, page)\n\n // copy existing bits in\n if (i === firstPage && bitfieldPage) page.set(bitfieldPage)\n\n if (index < length) {\n index = fillBitfieldPage(page, index, length, i, true)\n if (index < length) continue\n }\n\n if (index < this.length) {\n index = fillBitfieldPage(page, index, this.length, i, false)\n }\n }\n }\n\n const tree = {\n fork,\n length,\n rootHash: crypto.tree(roots),\n signature\n }\n\n const upgraded = treeLength < this.length || this.length < length || tree.fork !== this.fork\n\n if (upgraded) tx.setHead(tree)\n\n const flushed = await this.flush()\n\n this.fork = tree.fork\n this.roots = roots\n this.length = length\n this.byteLength = MerkleTree.size(roots)\n this.signature = signature\n\n return { tree, flushed }\n }\n\n async commit(state, { signature, keyPair, length = state.length, treeLength = -1 } = {}) {\n assert(\n this.isDefault() || (this.parent && this.parent.isDefault()),\n 'Can only commit into default state'\n )\n\n let srcLocked = false\n await this.mutex.lock()\n\n try {\n await state.mutex.lock()\n srcLocked = true\n\n if (treeLength === -1) treeLength = state.flushedLength()\n\n if (!(await this.core._validateCommit(state, treeLength))) return null\n if (this.length > length) return null\n\n if (this.length < length && !signature) {\n if (!keyPair) keyPair = this.core.header.keyPair\n const batch = state.createTreeBatch()\n if (length !== batch.length) await batch.restore(length)\n signature = this.core.verifier.sign(batch, keyPair)\n }\n\n const { tree, flushed } = await this._overwrite(\n state,\n this.fork,\n length,\n treeLength,\n signature\n )\n\n // gc blocks from source\n if (treeLength < length) {\n const tx = state.createWriteBatch()\n\n tx.deleteBlockRange(treeLength, length)\n const dependency = state.updateDependency(tx, length)\n\n await state.flush(tx)\n\n if (dependency) state.storage.setDependencyHead(dependency)\n }\n\n const bitfield = { start: treeLength, length: length - treeLength, drop: false }\n this.onappend(tree, bitfield, flushed)\n\n return {\n length: this.length,\n byteLength: this.byteLength\n }\n } finally {\n this.updating = false\n this.mutex.unlock()\n\n if (srcLocked) {\n state.mutex.unlock()\n state._clearActiveBatch()\n }\n\n this.core.checkIfIdle()\n }\n }\n\n async _getTreeHeadAt(length) {\n if (length === null) return this.treeInfo()\n\n const head = getDefaultTree()\n\n head.length = length\n\n const roots = await MerkleTree.getRootsFromStorage(this.storage, length)\n const rootHash = crypto.tree(roots)\n\n head.fork = this.fork\n head.rootHash = rootHash\n\n if (length === this.length) head.signature = this.signature\n\n return head\n }\n\n _moveToCore(core, truncated, appended) {\n const head = this.core.sessionStates.pop()\n if (head !== this) this.core.sessionStates[(head.index = this.index)] = head\n\n this.core = core\n this.index = this.core.sessionStates.push(this) - 1\n\n for (let i = this.sessions.length - 1; i >= 0; i--) {\n const s = this.sessions[i]\n const manifest = s.manifest\n s.transferSession(this.core)\n if (!manifest && s.manifest) s.emit('manifest')\n if (truncated) s.emit('truncate', truncated.to, truncated.fork)\n if (appended) s.emit('append')\n }\n }\n\n async moveTo(core, length) {\n const state = core.state\n\n await this.mutex.lock()\n\n try {\n // if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {\n // throw STORAGE_CONFLICT('Batch has already been created')\n // }\n\n const treeLength = this.length\n\n let truncated = null\n let appended = false\n\n if (!this.isSnapshot()) {\n if (this.lingers === null) this.lingers = []\n this.lingers.push(this.storage)\n\n const resumed = await state.storage.resumeSession(this.name)\n\n const truncation = length < this.length ? await truncateAndFlush(this, length) : null\n const treeInfo = truncation\n ? truncation.tree\n : resumed\n ? null\n : await state._getTreeHeadAt(this.length)\n\n const fork = truncation ? this.fork + 1 : this.fork\n\n // todo: validate treeInfo\n\n let storage = null\n\n if (resumed) {\n storage = resumed\n } else {\n treeInfo.fork = fork\n storage = await state.storage.createSession(this.name, treeInfo)\n }\n\n const roots = await MerkleTree.getRootsFromStorage(storage, length)\n\n this.storage = storage\n this.prologue = state.prologue\n this.fork = fork\n this.length = length\n this.byteLength = MerkleTree.size(roots)\n this.roots = roots\n\n if (truncation) {\n const { dependency } = truncation\n\n if (dependency) this.storage.setDependencyHead(dependency)\n truncated = { to: treeLength, fork }\n }\n\n if (this.length > treeLength) {\n appended = true\n }\n }\n\n for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {\n const state = this.core.sessionStates[i]\n if (state === this) continue\n if (state.name === this.name) state._moveToCore(core.core)\n }\n\n this._moveToCore(core.core, truncated, appended)\n } finally {\n this.mutex.unlock()\n }\n }\n\n async createSession(name, overwrite, atom) {\n let storage = null\n let treeInfo = null\n\n if (!atom && !overwrite && this.storage) {\n storage = await this.storage.resumeSession(name)\n\n if (storage !== null) treeInfo = (await getCoreHead(storage)) || getDefaultTree()\n }\n\n const length = treeInfo ? treeInfo.length : this.length\n\n if (storage === null) {\n treeInfo = await this._getTreeHeadAt(length)\n\n if (atom) {\n storage = await this.storage.createAtomicSession(atom, treeInfo)\n } else {\n storage = await this.storage.createSession(name, treeInfo)\n }\n }\n\n if (this.atomized && atom) {\n throw new Error('Session already atomized')\n }\n\n const head = {\n fork: this.fork,\n roots:\n length === this.length\n ? this.roots.slice()\n : await MerkleTree.getRootsFromStorage(storage, length),\n length,\n prologue: this.prologue,\n signature: length === this.length ? this.signature : null\n }\n\n const state = new SessionState(\n this.core,\n atom ? this : null,\n storage,\n head,\n atom ? this.name : name\n )\n\n if (atom) {\n this.atomized = atom\n atom.onflush(state._commit.bind(state))\n }\n\n return state\n }\n}\n\nfunction noop() {}\n\nfunction getBitfieldPage(index) {\n return Math.floor(index / Bitfield.BITS_PER_PAGE)\n}\n\nfunction fillBitfieldPage(page, start, end, pageIndex, value) {\n const offset = pageIndex * Bitfield.BITS_PER_PAGE\n const max = offset + Bitfield.BITS_PER_PAGE\n\n const index = max < end ? max : end\n\n const from = start - offset\n const to = index - offset\n\n quickbit.fill(page, value, from, to)\n\n return index\n}\n\nasync function storeBitfieldRange(storage, tx, from, to, value) {\n if (from >= to) return\n\n const firstPage = getBitfieldPage(from)\n const lastPage = getBitfieldPage(to - 1)\n\n let index = from\n\n const rx = storage.read()\n\n const promises = []\n for (let i = firstPage; i <= lastPage; i++) {\n promises.push(rx.getBitfieldPage(i))\n }\n\n rx.tryFlush()\n\n const pages = await Promise.all(promises)\n const cnt = lastPage - firstPage + 1\n\n for (let i = 0; i < cnt; i++) {\n const pageIndex = i + firstPage\n if (!pages[i]) pages[i] = b4a.alloc(Bitfield.BYTES_PER_PAGE)\n\n index = fillBitfieldPage(pages[i], index, to, pageIndex, value)\n tx.putBitfieldPage(pageIndex, pages[i])\n }\n}\n\nasync function truncateAndFlush(s, length) {\n const batch = s.createTreeBatch()\n await MerkleTree.truncate(s, length, batch, s.fork)\n const tx = s.createWriteBatch()\n\n const info = await s._truncate(tx, batch)\n const flushed = await s.flush()\n\n return {\n tree: info.tree,\n roots: info.roots,\n dependency: info.dependency,\n flushed\n }\n}\n\nfunction updateDependency(state, length, truncated) {\n const i = state.storage.findDependencyIndex(length, truncated)\n if (i === -1) return null // skip default state and overlays of default\n\n return {\n dataPointer: state.storage.dependencies[i].dataPointer,\n length\n }\n}\n\nfunction getDefaultTree() {\n return {\n fork: 0,\n length: 0,\n rootHash: null,\n signature: null\n }\n}\n\nfunction getCoreHead(storage) {\n const b = storage.read()\n const p = b.getHead()\n b.tryFlush()\n return p\n}\n\nfunction isRootIndex(index, roots) {\n for (const node of roots) {\n if (node.index === index) return true\n }\n\n return false\n}\nconst { Writable, Readable } = require('streamx')\n\nclass ReadStream extends Readable {\n constructor(core, opts = {}) {\n super()\n\n this.core = core\n this.start = opts.start || 0\n this.end = typeof opts.end === 'number' ? opts.end : -1\n this.snapshot = !opts.live && opts.snapshot !== false\n this.live = this.end === -1 ? !!opts.live : false\n this.wait = typeof opts.wait === 'boolean' ? opts.wait : this.core.wait\n this.timeout = opts.timeout || core.timeout\n }\n\n _open(cb) {\n this._openP().then(cb, cb)\n }\n\n _read(cb) {\n this._readP().then(cb, cb)\n }\n\n async _openP() {\n if (this.end === -1) await this.core.update()\n else await this.core.ready()\n if (this.snapshot && this.end === -1) this.end = this.core.length\n }\n\n async _readP() {\n const end = this.live ? -1 : this.end === -1 ? this.core.length : this.end\n if (end >= 0 && this.start >= end) {\n this.push(null)\n return\n }\n\n this.push(await this.core.get(this.start++, { wait: this.wait, timeout: this.timeout }))\n }\n}\n\nexports.ReadStream = ReadStream\n\nclass WriteStream extends Writable {\n constructor(core) {\n super()\n this.core = core\n }\n\n _writev(batch, cb) {\n this._writevP(batch).then(cb, cb)\n }\n\n async _writevP(batch) {\n await this.core.append(batch)\n }\n}\n\nexports.WriteStream = WriteStream\n\nclass ByteStream extends Readable {\n constructor(core, opts = {}) {\n super()\n\n this._core = core\n this._index = 0\n this._range = null\n\n this._byteOffset = opts.byteOffset || 0\n this._byteLength = typeof opts.byteLength === 'number' ? opts.byteLength : -1\n this._prefetch = typeof opts.prefetch === 'number' ? opts.prefetch : 32\n\n this._applyOffset = this._byteOffset > 0\n }\n\n _open(cb) {\n this._openp().then(cb, cb)\n }\n\n _read(cb) {\n this._readp().then(cb, cb)\n }\n\n async _openp() {\n if (this._byteLength === -1) {\n await this._core.update()\n this._byteLength = Math.max(this._core.byteLength - this._byteOffset, 0)\n }\n }\n\n async _readp() {\n let data = null\n\n if (this._byteLength === 0) {\n this.push(null)\n return\n }\n\n let relativeOffset = 0\n\n if (this._applyOffset) {\n this._applyOffset = false\n\n const [block, byteOffset] = await this._core.seek(this._byteOffset)\n\n this._index = block\n relativeOffset = byteOffset\n }\n\n this._predownload(this._index + 1)\n data = await this._core.get(this._index++, { valueEncoding: 'binary' })\n\n if (relativeOffset > 0) data = data.subarray(relativeOffset)\n\n if (data.byteLength > this._byteLength) data = data.subarray(0, this._byteLength)\n this._byteLength -= data.byteLength\n\n this.push(data)\n if (this._byteLength === 0) this.push(null)\n }\n\n _predownload(index) {\n if (this._range) this._range.destroy()\n this._range = this._core.download({ start: index, end: index + this._prefetch, linear: true })\n }\n\n _destroy(cb) {\n if (this._range) this._range.destroy()\n cb(null)\n }\n}\n\nexports.ByteStream = ByteStream\nconst crypto = require('hypercore-crypto')\nconst b4a = require('b4a')\nconst c = require('compact-encoding')\nconst flat = require('flat-tree')\nconst { BAD_ARGUMENT } = require('hypercore-errors')\nconst unslab = require('unslab')\n\nconst m = require('./messages')\nconst multisig = require('./multisig')\nconst caps = require('./caps')\n\nclass Signer {\n constructor(\n manifestHash,\n version,\n index,\n { signature = 'ed25519', publicKey, namespace = caps.DEFAULT_NAMESPACE } = {}\n ) {\n if (!publicKey) throw BAD_ARGUMENT('public key is required for a signer')\n if (signature !== 'ed25519') throw BAD_ARGUMENT('Only Ed25519 signatures are supported')\n\n this.manifestHash = manifestHash\n this.version = version\n this.signer = index\n this.signature = signature\n this.publicKey = publicKey\n this.namespace = namespace\n }\n\n _ctx() {\n return this.version === 0 ? this.namespace : this.manifestHash\n }\n\n verify(batch, signature) {\n return crypto.verify(batch.signable(this._ctx()), signature, this.publicKey)\n }\n\n sign(batch, keyPair) {\n return crypto.sign(batch.signable(this._ctx()), keyPair.secretKey)\n }\n}\n\nclass CompatSigner extends Signer {\n constructor(index, signer, legacy) {\n super(null, 0, index, signer)\n this.legacy = legacy\n }\n\n verify(batch, signature) {\n return crypto.verify(batch.signableCompat(this.legacy), signature, this.publicKey)\n }\n\n sign(batch, keyPair) {\n return crypto.sign(batch.signableCompat(this.legacy), keyPair.secretKey)\n }\n}\n\nmodule.exports = class Verifier {\n constructor(\n manifestHash,\n manifest,\n { compat = isCompat(manifestHash, manifest), legacy = false } = {}\n ) {\n const self = this\n\n this.manifestHash = manifestHash\n this.compat = compat || manifest === null\n this.version = this.compat ? 0 : typeof manifest.version === 'number' ? manifest.version : 1\n this.hash = manifest.hash || 'blake2b'\n this.allowPatch = !this.compat && !!manifest.allowPatch\n this.quorum = this.compat ? 1 : defaultQuorum(manifest)\n\n this.signers = manifest.signers ? manifest.signers.map(createSigner) : []\n this.prologue = this.compat ? null : manifest.prologue || null\n\n function createSigner(signer, index) {\n return self.compat\n ? new CompatSigner(index, signer, legacy)\n : new Signer(manifestHash, self.version, index, signer)\n }\n }\n\n _verifyCompat(batch, signature) {\n if (!signature) return false\n\n if (this.compat || (!this.allowPatch && this.signers.length === 1)) {\n return !!signature && this.signers[0].verify(batch, signature)\n }\n\n return this._verifyMulti(batch, signature)\n }\n\n _inflate(signature) {\n if (this.version >= 1) return multisig.inflate(signature)\n const { proofs, patch } = multisig.inflatev0(signature)\n\n return {\n proofs: proofs.map(proofToVersion1),\n patch\n }\n }\n\n _verifyMulti(batch, signature) {\n if (!signature || this.quorum === 0) return false\n\n const { proofs, patch } = this._inflate(signature)\n if (proofs.length < this.quorum) return false\n\n const tried = new Uint8Array(this.signers.length)\n const nodes = this.allowPatch && patch.length ? toMap(patch) : null\n\n for (let i = 0; i < this.quorum; i++) {\n const inp = proofs[i]\n\n let tree = batch\n\n if (inp.patch && this.allowPatch) {\n tree = batch.clone()\n\n const upgrade = generateUpgrade(nodes, batch.length, inp.patch)\n const proof = {\n fork: tree.fork,\n block: null,\n hash: null,\n seek: null,\n upgrade,\n manifest: null\n }\n\n try {\n if (!tree.verifyUpgrade(proof)) return false\n } catch {\n return false\n }\n }\n\n if (inp.signer >= this.signers.length || tried[inp.signer]) return false\n tried[inp.signer] = 1\n\n const s = this.signers[inp.signer]\n if (!s.verify(tree, inp.signature)) return false\n }\n\n return true\n }\n\n verify(batch, signature) {\n if (this.version === 0) {\n return this._verifyCompat(batch, signature)\n }\n\n if (this.prologue !== null && batch.length <= this.prologue.length) {\n return batch.length === this.prologue.length && b4a.equals(batch.hash(), this.prologue.hash)\n }\n\n return this._verifyMulti(batch, signature)\n }\n\n // TODO: better api for this that is more ... multisig-ey\n sign(batch, keyPair) {\n if (!keyPair || !keyPair.secretKey) throw BAD_ARGUMENT('No key pair was passed')\n\n for (const s of this.signers) {\n if (b4a.equals(s.publicKey, keyPair.publicKey)) {\n const signature = s.sign(batch, keyPair)\n if (this.signers.length !== 1 || this.version === 0) return signature\n return this.assemble([{ signer: 0, signature, patch: 0, nodes: null }])\n }\n }\n\n throw BAD_ARGUMENT('Public key is not a declared signer')\n }\n\n assemble(inputs) {\n return this.version === 0 ? multisig.assemblev0(inputs) : multisig.assemble(inputs)\n }\n\n static manifestHash(manifest) {\n return manifestHash(manifest)\n }\n\n static encodeManifest(manifest) {\n return c.encode(m.manifest, manifest)\n }\n\n static decodeManifest(manifest) {\n return c.decode(m.manifest, manifest)\n }\n\n static defaultSignerManifest(publicKey) {\n return {\n version: 1,\n hash: 'blake2b',\n allowPatch: false,\n quorum: 1,\n signers: [\n {\n signature: 'ed25519',\n namespace: caps.DEFAULT_NAMESPACE,\n publicKey\n }\n ],\n prologue: null,\n linked: null,\n userData: null\n }\n }\n\n static fromManifest(manifest, opts) {\n const m = this.createManifest(manifest)\n return new this(manifestHash(m), m, opts)\n }\n\n static createManifest(inp) {\n if (!inp) return null\n\n const manifest = {\n version: getManifestVersion(inp), // defaults to v1\n hash: 'blake2b',\n allowPatch: !!inp.allowPatch,\n quorum: defaultQuorum(inp),\n signers: inp.signers ? inp.signers.map(parseSigner) : [],\n prologue: null,\n linked: null,\n userData: inp.userData || null\n }\n\n if (inp.hash && inp.hash !== 'blake2b') throw BAD_ARGUMENT('Only Blake2b hashes are supported')\n\n if (inp.prologue) {\n if (\n !(b4a.isBuffer(inp.prologue.hash) && inp.prologue.hash.byteLength === 32) ||\n !(inp.prologue.length >= 0)\n ) {\n throw BAD_ARGUMENT('Invalid prologue')\n }\n\n manifest.prologue = inp.prologue\n manifest.prologue.hash = unslab(manifest.prologue.hash)\n }\n\n if (manifest.userData !== null && manifest.version < 2) {\n throw BAD_ARGUMENT('Invalid field: userData')\n }\n\n if (inp.linked && inp.linked.length) {\n if (manifest.version < 2) throw BAD_ARGUMENT('Invalid field: linked')\n\n for (const key of inp.linked) {\n if (!(b4a.isBuffer(key) && key.byteLength === 32)) {\n throw BAD_ARGUMENT('Invalid key')\n }\n }\n\n manifest.linked = inp.linked\n }\n\n return manifest\n }\n\n static isValidManifest(key, manifest) {\n return b4a.equals(key, manifestHash(manifest))\n }\n\n static isCompat(key, manifest) {\n return isCompat(key, manifest)\n }\n\n static sign(manifest, batch, keyPair, opts) {\n return Verifier.fromManifest(manifest, opts).sign(batch, keyPair)\n }\n}\n\nfunction toMap(nodes) {\n const m = new Map()\n for (const node of nodes) m.set(node.index, node)\n return m\n}\n\nfunction isCompat(key, manifest) {\n return !!(\n manifest &&\n manifest.signers.length === 1 &&\n b4a.equals(key, manifest.signers[0].publicKey)\n )\n}\n\nfunction defaultQuorum(man) {\n if (typeof man.quorum === 'number') return man.quorum\n if (!man.signers || !man.signers.length) return 0\n return (man.signers.length >> 1) + 1\n}\n\nfunction generateUpgrade(patch, start, length) {\n const upgrade = { start, length, nodes: null, additionalNodes: [], signature: null }\n\n const from = start * 2\n const to = from + length * 2\n\n for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {\n if (ite.index + ite.factor / 2 < from) continue\n\n if (upgrade.nodes === null && ite.contains(from - 2)) {\n upgrade.nodes = []\n\n const root = ite.index\n const target = from - 2\n\n ite.seek(target)\n\n while (ite.index !== root) {\n ite.sibling()\n if (ite.index > target) upgrade.nodes.push(patch.get(ite.index))\n ite.parent()\n }\n\n continue\n }\n\n if (upgrade.nodes === null) upgrade.nodes = []\n upgrade.nodes.push(patch.get(ite.index))\n }\n\n if (upgrade.nodes === null) upgrade.nodes = []\n return upgrade\n}\n\nfunction parseSigner(signer) {\n validateSigner(signer)\n return {\n signature: 'ed25519',\n namespace: unslab(signer.namespace || caps.DEFAULT_NAMESPACE),\n publicKey: unslab(signer.publicKey)\n }\n}\n\nfunction validateSigner(signer) {\n if (!signer || !signer.publicKey) throw BAD_ARGUMENT('Signer missing public key')\n if (signer.signature && signer.signature !== 'ed25519') {\n throw BAD_ARGUMENT('Only Ed25519 signatures are supported')\n }\n}\n\nfunction manifestHash(manifest) {\n const state = { start: 0, end: 32, buffer: null }\n m.manifest.preencode(state, manifest)\n state.buffer = b4a.allocUnsafe(state.end)\n c.raw.encode(state, caps.MANIFEST)\n m.manifest.encode(state, manifest)\n return crypto.hash(state.buffer)\n}\n\nfunction proofToVersion1(proof) {\n return {\n signer: proof.signer,\n signature: proof.signature,\n patch: proof.patch ? proof.patch.length : 0\n }\n}\n\nfunction getManifestVersion(inp) {\n if (typeof inp.version === 'number') return inp.version\n if (inp.linked && inp.linked.length) return 2\n if (inp.userData) return 2\n return 1\n}\nconst COMPAT = true // should be flipped once this is widely deployed\nconst BATCH_UINTS = COMPAT ? 65536 : 128\nconst BATCH_BYTES = BATCH_UINTS * 4\nconst BATCH = BATCH_BYTES * 8 // in bits\nconst MAX_REMOTE_BATCHES = 4\nconst MAX_RANGE = 8 * 256 * 1024\nconst MIN_RANGE = 32 // 4bit ints\nconst MAX = 512\nconst MAX_ANY = 16\n\nclass LocalWants {\n constructor(peer) {\n this.destroyed = false\n this.peer = peer\n this.wants = new Map()\n this.free = new Set()\n this.any = null\n }\n\n add(index, handle) {\n const b = (index - (index & (BATCH - 1))) / BATCH\n return this.addBatch(b, handle)\n }\n\n addAnyRange(start, length, handle) {\n if (this.destroyed) return null\n if (this.any === null) this.any = []\n\n if (COMPAT) {\n let r = start & (BATCH - 1)\n start -= r\n length += r\n r = length & (BATCH - 1)\n if (r) length = length - r + BATCH\n }\n\n for (let i = 0; i < this.any.length; i++) {\n const w = this.any[i]\n if (w.start === start && w.length === length) {\n w.handles.add(handle)\n handle.addWant(w)\n return null\n }\n }\n\n const w = { wants: this, start, length, any: true, handles: new Set([handle]) }\n this.any.push(w)\n handle.addWant(w)\n\n return { start, length, any: !COMPAT }\n }\n\n removeAnyRange(start, length, handle) {\n if (this.any === null) return null\n\n for (let i = 0; i < this.any.length; i++) {\n const w = this.any[i]\n if (w.start !== start || w.length !== length) continue\n\n w.handles.delete(handle)\n handle.removeWant(w)\n if (w.handles.size > 0) return null\n\n const head = this.any.pop()\n if (head !== w) this.any[i] = head\n return { start, length, any: !COMPAT }\n }\n\n return null\n }\n\n addBatch(index, handle) {\n if (this.destroyed) return null\n let w = this.wants.get(index)\n\n if (w) {\n const size = w.handles.size\n w.handles.add(handle)\n if (w.handles.size !== size) {\n handle.addWant(w)\n }\n return null\n }\n\n // start here is the batch number for simplicity....\n w = { wants: this, start: index, length: 0, any: false, handles: new Set([handle]) }\n\n if (this.free.has(index)) {\n this.free.delete(index)\n this.wants.set(index, w)\n handle.addWant(w)\n return null\n }\n\n let unwant = null\n if (this.wants.size + this.free.size === MAX) {\n if (this.free.size === 0) {\n return null\n }\n unwant = this._unwant()\n }\n\n this.wants.set(index, w)\n handle.addWant(w)\n\n return {\n want: { start: index * BATCH, length: BATCH, any: false },\n unwant\n }\n }\n\n remove(index, handle) {\n const b = (index - (index & (BATCH - 1))) / BATCH\n return this.removeBatch(b, handle)\n }\n\n removeBatch(index, handle) {\n if (this.destroyed) return false\n\n const w = this.wants.get(index)\n if (!w) {\n return false\n }\n\n w.handles.delete(handle)\n handle.removeWant(w)\n\n if (w.handles.size === 0) {\n this.free.add(index)\n this.wants.delete(index)\n return this.free.size === 1 && this.wants.size === MAX - 1\n }\n\n return false\n }\n\n destroy() {\n if (this.destroyed) return\n this.destroyed = true\n\n for (const w of this.wants.values()) {\n for (const handle of w.handles) {\n handle.removeWant(w)\n }\n }\n\n this.wants.clear()\n }\n\n _unwant() {\n for (const index of this.free) {\n this.free.delete(index)\n return { start: index * BATCH, length: BATCH, any: false }\n }\n return null\n }\n}\n\nclass RemoteWants {\n constructor() {\n this.any = null\n this.all = false\n this.size = 0\n this.batches = []\n }\n\n hasAny(start, length) {\n for (let i = 0; i < this.any.length; i++) {\n const a = this.any[i]\n const e = start + length\n const end = a.start + a.length\n\n if (a.start <= start && start < end) return true\n if (a.start < e && e <= end) return true\n }\n\n return this.all\n }\n\n hasRange(start, length) {\n if (this.any !== null && this.hasAny(start, length)) return true\n if (length === 1) return this.has(start)\n\n let smallest = -1\n\n for (let i = 0; i < this.batches.length; i++) {\n const b = this.batches[i]\n if (b.length < smallest || smallest === -1) smallest = b.length\n }\n\n if (smallest === -1) return this.all\n\n const r = start & (smallest - 1)\n const end = start + length\n\n let max = 3\n\n for (let i = start - r; i < end; i += smallest) {\n if (max-- === 0) return true // just to save work\n if (this.has(i)) return true\n }\n\n return this.all\n }\n\n has(index) {\n for (let i = 0; i < this.batches.length; i++) {\n const b = this.batches[i]\n const block = (index - (index & (b.length - 1))) / b.length\n if (b.ranges.has(block)) return true\n }\n\n return this.all\n }\n\n add(range) {\n if (range.any) {\n if (this.any === null) this.any = []\n if (this.any.length < MAX_ANY) this.any.push(range)\n else this.all = true\n return true\n }\n\n if (range.length > MAX_RANGE) return false\n\n if (!validateBatchRange(range)) {\n this.all = true\n return true\n }\n\n if (this.size >= MAX) {\n this.all = true\n return true\n }\n\n for (let i = 0; i < this.batches.length; i++) {\n const b = this.batches[i]\n if (b.length === range.length) {\n b.ranges.add(range.start / range.length)\n this.size++\n return true\n }\n }\n\n if (this.batches.length >= MAX_REMOTE_BATCHES) {\n this.all = true\n return true\n }\n\n this.batches.push({\n length: range.length,\n ranges: new Set([range.start / range.length])\n })\n this.size++\n\n return true\n }\n\n remove(range) {\n if (range.any) {\n if (this.any === null) return false\n\n for (let i = 0; i < this.any.length; i++) {\n const a = this.any[i]\n\n if (a.start === range.start && a.length === range.length) {\n const head = this.any.pop()\n if (head !== a) this.any[i] = head\n return true\n }\n }\n\n return false\n }\n\n if (!validateBatchRange(range)) return false\n\n for (let i = 0; i < this.batches.length; i++) {\n const b = this.batches[i]\n if (b.length !== range.length) continue\n\n const size = b.ranges.size\n b.ranges.delete(range.start / range.length)\n if (b.ranges.size !== size) this.size--\n\n if (b.ranges.size === 0) {\n const head = this.batches.pop()\n if (head !== b) this.batches[i] = head\n }\n\n return true\n }\n\n return false\n }\n}\n\nexports.LocalWants = LocalWants\nexports.RemoteWants = RemoteWants\nexports.WANT_BATCH = BATCH\n\nfunction validateBatchRange(range) {\n if (range.length > MAX_RANGE || range.length < MIN_RANGE) {\n return false\n }\n // check if power of two\n if ((range.length & (range.length - 1)) !== 0) {\n return false\n }\n // start must be a multiple of the length\n if (range.start & (range.length - 1)) {\n return false\n }\n\n return true\n}\n{\n \"name\": \"hypercore\",\n \"version\": \"11.24.0\",\n \"description\": \"Hypercore is a secure, distributed append-only log\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"brittle test/all.js\",\n \"test:bare\": \"bare test/all.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hypercore.git\"\n },\n \"contributors\": [\n {\n \"name\": \"Mathias Buus\",\n \"email\": \"mathiasbuus@gmail.com\",\n \"url\": \"https://mafinto.sh\"\n },\n {\n \"name\": \"Andrew Osheroff\",\n \"email\": \"andrewosh@gmail.com\",\n \"url\": \"https://andrewosh.com\"\n }\n ],\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hypercore/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hypercore#readme\",\n \"files\": [\n \"index.js\",\n \"errors.js\",\n \"messages.js\",\n \"lib/**.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"dependencies\": {\n \"@hyperswarm/secret-stream\": \"^6.0.0\",\n \"b4a\": \"^1.1.0\",\n \"bare-events\": \"^2.2.0\",\n \"big-sparse-array\": \"^1.0.3\",\n \"compact-encoding\": \"^2.11.0\",\n \"fast-fifo\": \"^1.3.0\",\n \"flat-tree\": \"^1.9.0\",\n \"hypercore-crypto\": \"^3.2.1\",\n \"hypercore-errors\": \"^1.5.0\",\n \"hypercore-id-encoding\": \"^1.2.0\",\n \"hypercore-storage\": \"^2.0.0\",\n \"is-options\": \"^1.0.1\",\n \"nanoassert\": \"^2.0.0\",\n \"protomux\": \"^3.5.0\",\n \"quickbit-universal\": \"^2.2.0\",\n \"random-array-iterator\": \"^1.0.0\",\n \"safety-catch\": \"^1.0.1\",\n \"sodium-universal\": \"^5.0.1\",\n \"streamx\": \"^2.12.4\",\n \"unslab\": \"^1.3.0\",\n \"z32\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"debugging-stream\": \"^3.1.0\",\n \"hyperswarm\": \"^4.3.6\",\n \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"rache\": \"^1.0.0\",\n \"range-parser\": \"^1.2.1\",\n \"resolve-reject-promise\": \"^1.1.0\",\n \"speedometer\": \"^1.1.0\",\n \"test-tmp\": \"^1.0.2\",\n \"tiny-byte-size\": \"^1.1.0\",\n \"udx-native\": \"^1.6.1\",\n \"uncaughts\": \"^1.1.0\"\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 Cache = require('xache')\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\nconst DEFAULTS = {\n ...DHT.DEFAULTS,\n connectionKeepAlive: 5000,\n randomPunchInterval: 20000\n}\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, relayAddresses, persistent } = defaultCacheOpts(opts)\n\n this.defaultKeyPair = opts.keyPair || createKeyPair(opts.seed)\n this.listening = new Set()\n this.connectionKeepAlive =\n opts.connectionKeepAlive === false\n ? 0\n : opts.connectionKeepAlive || DEFAULTS.connectionKeepAlive\n\n // stats is inherited from dht-rpc so fwd the ones from there\n this.stats = {\n punches: { consistent: 0, random: 0, open: 0 },\n relaying: { attempts: 0, successes: 0, aborts: 0 },\n ...this.stats\n }\n this.rawStreams = new RawStreamSet(this)\n\n this._router = new Router(this, router)\n this._socketPool = new SocketPool(this, opts.host || '0.0.0.0')\n this._persistent = null\n this._validatedLocalAddresses = new Map()\n this._relayAddressesCache = new Cache(relayAddresses)\n\n this._deferRandomPunch = !!opts.deferRandomPunch\n this._lastRandomPunch = this._deferRandomPunch ? Date.now() : 0\n this._connectable = true\n this._randomPunchInterval = opts.randomPunchInterval || DEFAULTS.randomPunchInterval // 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 static DEFAULTS = DEFAULTS\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 if (this._deferRandomPunch) this._lastRandomPunch = Date.now()\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) {\n // 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\n ._requestUnannounce(keyPair, dht, target, data.token, data.from, 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 const bump = opts.bump || 0\n\n opts = { ...opts, commit }\n\n return opts.clear ? this.lookupAndUnannounce(target, keyPair, opts) : 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 bump\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(\n { token: reply.token, target, command: COMMANDS.IMMUTABLE_PUT, value },\n reply.from\n )\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(\n { target, command: COMMANDS.MUTABLE_GET, value: c.encode(c.uint, userSeq) },\n opts\n )\n const latest = opts.latest !== false\n\n for await (const node of query) {\n if (result && node.seq <= result.seq) continue\n if (\n node.seq < userSeq ||\n !Persistent.verifyMutable(node.signature, node.seq, node.value, publicKey)\n )\n 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\n ? dht.request(\n { token: reply.token, target, command: COMMANDS.MUTABLE_PUT, value: signed },\n reply.from\n )\n : 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(\n { token: reply.token, target, command: COMMANDS.MUTABLE_PUT, value: signed },\n reply.from\n )\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(\n { target, command: COMMANDS.MUTABLE_GET, value: c.encode(c.uint, 0) },\n opts\n )\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(stream.socket, remoteId, stream.remotePort, stream.remoteHost)\n }\n\n createRawStream(opts) {\n return this.rawStreams.add(opts)\n }\n\n async _requestAnnounce(keyPair, dht, target, token, from, relayAddresses, sign, bump) {\n const ann = {\n peer: {\n publicKey: keyPair.publicKey,\n relayAddresses: relayAddresses || []\n },\n refresh: null,\n signature: null,\n bump\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 {\n token,\n target,\n command: COMMANDS.ANNOUNCE,\n value\n },\n from\n )\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 {\n token,\n target,\n command: COMMANDS.UNANNOUNCE,\n value\n },\n from\n )\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 const l = c.decode(m.lookupRawReply, node.value)\n\n try {\n return {\n token: node.token,\n from: node.from,\n to: node.to,\n peers: l.peers,\n bump: l.bump\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 (\n !(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') &&\n !(node.host === '150.136.142.116')\n )\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 relayAddresses: { maxSize: Math.min(maxSize, 512), maxAge: 0 },\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 bumps: { maxSize, maxAge }\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 = [new Map(), new Map(), new Map()]\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, {\n hash: false,\n nodes: this._closestNodes\n }))\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 {\n token: null,\n command: COMMANDS.FIND_PEER,\n target: this.target,\n value: null\n },\n to\n )\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 {\n token,\n command: COMMANDS.UNANNOUNCE,\n target: this.target,\n value: c.encode(m.announce, unann)\n },\n to\n )\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 {\n token: msg.token,\n command: COMMANDS.ANNOUNCE,\n target: this.target,\n value: c.encode(m.announce, ann)\n },\n msg.from\n )\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) {\n // 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 { isReserved, 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 id = b4a.toString(publicKey, 'hex')\n const c = {\n id,\n dht,\n session: dht.session(),\n relayAddresses: opts.relayAddresses || [],\n remoteRelayAddresses: [],\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) {\n // 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(onlyNonReserved)\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 (\n !diffAddress(serverRelay.relayAddress, relayAddress) &&\n diffAddress(serverAddress, peerAddress)\n ) {\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 (\n opts.holepunch &&\n !opts.holepunch(\n c.puncher.remoteFirewall,\n c.puncher.nat.firewall,\n c.puncher.remoteAddresses,\n c.puncher.nat.addresses\n )\n ) {\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 (!closestNodes) {\n const cachedRelayAddresses = c.dht._relayAddressesCache.get(c.id)\n if (cachedRelayAddresses) closestNodes = cachedRelayAddresses\n }\n\n if (c.dht._persistent) {\n // check if we know the route ourself...\n const route = c.dht._router.get(c.target)\n if (route && route.relay !== null) {\n closestNodes = [{ host: route.relay.host, port: route.relay.port }]\n }\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, {\n hash: false,\n session: c.session,\n closestNodes,\n onlyClosestNodes: closestNodes !== null,\n retries: closestNodes ? 1 : 3\n })\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 c.remoteRelayAddresses.push(data.from)\n attempts++\n connectThroughNode(c, data.from, null).then(signal, signal)\n }\n\n closestNodes = null\n\n if (attempts > 0) await sem.flush()\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 ? { publicKey: c.relayThrough, token: c.relayToken } : null\n })\n if (isDone(c)) return\n }\n\n const { serverAddress, clientAddress, relayed, noise } = await c.dht._router.peerHandshake(\n c.target,\n { noise: c.request, socket, session: c.session },\n address\n )\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 // cache the relay addrs for a future reconnect, we prefer the remote one so they\n // can give us the correct ones from their pov\n if (payload.relayAddresses && payload.relayAddresses.length) {\n c.dht._relayAddressesCache.set(c.id, payload.relayAddresses)\n } else if (c.remoteRelayAddresses.length) {\n c.dht._relayAddressesCache.set(c.id, c.remoteRelayAddresses)\n }\n\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(\n c.target,\n {\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 },\n relayAddr\n )\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({\n punching,\n firewall,\n addresses,\n verified: echoed ? peerAddress.host : null\n })\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 (\n c.puncher.remoteFirewall < FIREWALL.RANDOM &&\n address &&\n address.host &&\n address.port &&\n diffAddress(address, serverAddress)\n ) {\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 (\n c.puncher.remoteFirewall === FIREWALL.UNKNOWN ||\n c.puncher.nat.firewall === FIREWALL.UNKNOWN\n ) {\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 =\n c.puncher.remoteFirewall >= FIREWALL.RANDOM || c.puncher.nat.firewall >= FIREWALL.RANDOM\n if (isRandom) {\n while (\n c.dht._randomPunches >= c.dht._randomPunchLimit ||\n Date.now() - c.dht._lastRandomPunch < c.dht._randomPunchInterval\n ) {\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(\n c,\n delayed ? serverRelay.peerAddress : serverAddress,\n delayed ? serverRelay.relayAddress : clientRelay,\n {\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 )\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.pair(isInitiator, token, c.rawStream).on('error', onabort).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 { remotePort, remoteHost, socket } = 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 onlyNonReserved(addr) {\n return !isReserved(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))\n 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 =\n stream.isInitiator === existing.isInitiator ||\n 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.off('error', noop).off('close', onclose)\n\n this._attachStream(stream, opened)\n })\n .destroy(errors.DUPLICATE_CONNECTION())\n\n stream.on('error', noop).on('close', onclose)\n } else {\n stream.on('error', noop).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 [NS_ANNOUNCE, NS_UNANNOUNCE, NS_MUTABLE_PUT, NS_PEER_HANDSHAKE, 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(\n msg,\n 'HOLEPUNCH_DOUBLE_RANDOMIZED_NATS',\n DHTError.HOLEPUNCH_DOUBLE_RANDOMIZED_NATS\n )\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 (\n (this.remoteFirewall >= FIREWALL.RANDOM && firewall >= FIREWALL.RANDOM) ||\n firewall === FIREWALL.UNKNOWN\n )\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) {\n // 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 if (m.relayAddresses) ipv4Array.preencode(state, m.relayAddresses)\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 if (m.relayAddresses) flags |= 64\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 if (m.relayAddresses) ipv4Array.encode(state, m.relayAddresses)\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 relayAddresses: 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 relayAddresses: (flags & 64) !== 0 ? ipv4Array.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 =\n (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\nconst peers = (exports.peers = c.array(peer))\n\nconst rawPeers = c.array(c.raw)\n\nexports.lookupRawReply = {\n preencode(state, m) {\n rawPeers.preencode(state, m.peers)\n c.uint.preencode(state, m.bump)\n },\n encode(state, m) {\n rawPeers.encode(state, m.peers)\n c.uint.encode(state, m.bump)\n },\n decode(state) {\n return {\n peers: peers.decode(state),\n bump: state.start < state.end ? c.uint.decode(state) : 0\n }\n }\n}\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 if (m.bump) c.uint.preencode(state, m.bump)\n },\n encode(state, m) {\n const flags = (m.peer ? 1 : 0) | (m.refresh ? 2 : 0) | (m.signature ? 4 : 0) | (m.bump ? 8 : 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 if (m.bump) c.uint.encode(state, m.bump)\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 bump: (flags & 8) !== 0 ? c.uint.decode(state) : 0\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) => {\n this._resolve = resolve\n })\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 (\n let node = this.dht.nodes.latest;\n node && this.sampled + pending < maxPings;\n node = node.prev\n ) {\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)\nconst MAX_BUMP_DRIFT = 60_000\n\nmodule.exports = class Persistent {\n constructor(dht, opts) {\n this.dht = dht\n this.records = new RecordCache(opts.records)\n this.bumps = new Cache(opts.bumps)\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 bump = this.bumps.get(k) || 0\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(m.lookupRawReply, { peers: records, bump }) : 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, bump } = 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 const currentBump = this.bumps.get(k) || 0\n if (bump > currentBump && bump <= Date.now() + MAX_BUMP_DRIFT) this.bumps.set(k, bump)\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 get size() {\n return this._streams.size\n }\n\n [Symbol.iterator]() {\n return this._streams.values()\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.once('close', resolve).destroy()))\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(\n { command: COMMANDS.PEER_HANDSHAKE, target, value: requestValue },\n to,\n { socket, session }\n )\n\n const hs = decode(handshake, res.value)\n if (\n !hs ||\n hs.mode !== REPLY ||\n to.host !== res.from.host ||\n to.port !== res.from.port ||\n !hs.noise\n ) {\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(\n c.encode(handshake, { mode: REPLY, noise: reply.noise, peerAddress: null }),\n opts\n )\n return\n }\n case FROM_RELAY: {\n req.relay(\n c.encode(handshake, { mode: FROM_SERVER, noise: reply.noise, peerAddress }),\n req.from,\n opts\n )\n return\n }\n case FROM_SECOND_RELAY: {\n if (!relayAddress) return\n req.relay(\n c.encode(handshake, { mode: FROM_SERVER, noise: reply.noise, peerAddress }),\n relayAddress,\n opts\n )\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) {\n // help the user route\n req.reply(null, { token: false, closerNodes: true })\n return\n }\n req.relay(\n c.encode(handshake, {\n mode: FROM_RELAY,\n noise,\n peerAddress: req.from,\n relayAddress: null\n }),\n relayAddress || relay\n )\n return\n }\n case FROM_RELAY: {\n if (!relay || !noise) return\n req.relay(\n c.encode(handshake, {\n mode: FROM_SECOND_RELAY,\n noise,\n peerAddress,\n relayAddress: req.from\n }),\n relay\n )\n return\n }\n case FROM_SERVER: {\n if (!peerAddress || !noise) return\n req.reply(\n c.encode(handshake, { mode: REPLY, noise, peerAddress: req.from, relayAddress: null }),\n { to: peerAddress, closerNodes: false, token: false }\n )\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(\n { command: COMMANDS.PEER_HOLEPUNCH, target, value: requestValue },\n to,\n { socket, session }\n )\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(\n c.encode(holepunch, { mode: FROM_RELAY, id, payload, peerAddress: req.from }),\n peerAddress || relay\n )\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(\n c.encode(holepunch, { mode: FROM_SERVER, id: 0, payload: reply.payload, peerAddress }),\n req.from,\n opts\n )\n return\n }\n case FROM_SERVER: {\n req.reply(c.encode(holepunch, { mode: REPLY, id, payload, peerAddress: req.from }), {\n to: peerAddress,\n closerNodes: false,\n token: false\n })\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\n this.flushedPromise = null\n this.flushedResolve = null\n\n this.destroyed = false\n\n this._onwait = this._queueWaiting.bind(this)\n this._onflush = this._queueFlushed.bind(this)\n }\n\n _queueWaiting(resolve) {\n this.waiting.push(resolve)\n }\n\n _queueFlushed(resolve) {\n this.flushedResolve = 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 if (this.active === 0 && this.flushedResolve) {\n const resolve = this.flushedResolve\n this.flushedResolve = null\n this.flushedPromise = null\n resolve(true)\n }\n }\n\n async flush() {\n if (this.destroyed === true) return\n if (this.active === 0) return\n if (this.flushedPromise) return this.flushedPromise\n this.flushedPromise = new Promise(this._onflush)\n return this.flushedPromise\n }\n\n destroy() {\n this.destroyed = true\n this.active = 0\n while (this.waiting.length) this.waiting.pop()(false)\n if (this.flushedResolve) this.flushedResolve(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 this.handshakeClearWait = opts.handshakeClearWait || HANDSHAKE_CLEAR_WAIT\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.suspended) 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 aborted: false,\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 =\n remotePayload.version === 1\n ? remotePayload.udx\n ? ERROR.NONE\n : 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 // Handles the case where onsocket is never called, but the stream got setup\n // This can happen on a relayed connection which never connects directly\n // (onsocket is called there only when the direct connection is established)\n const onrawstreamclose = () => {\n if (this._closing) return\n this._clearLater(hs, id, k)\n }\n hs.rawStream.on('close', onrawstreamclose)\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 hs.rawStream.removeListener('close', onrawstreamclose)\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 relayAddresses = this.relayAddresses\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 ? { publicKey: relayThrough, token: hs.relayToken } : null,\n relayAddresses: relayAddresses.length ? relayAddresses : 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 (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 if (relayThrough || remotePayload.relayThrough) {\n this._relayConnection(hs, relayThrough, remotePayload, h)\n }\n\n const onabort = () => {\n hs.aborted = true\n if (hs.prepunching) clearTimeout(hs.prepunching)\n hs.prepunching = null\n if (hs.rawStream.destroyed) {\n this._clearLater(hs, id, k)\n return\n }\n\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), this.handshakeClearWait)\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 (\n this._holepunches.length > 0 &&\n this._holepunches[this._holepunches.length - 1] === null\n ) {\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 } =\n 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 (\n isConsistent(p.nat.firewall) &&\n remoteAddress &&\n hasSameAddr(p.nat.addresses, remoteAddress)\n ) {\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 (\n this.dht._randomPunches >= this.dht._randomPunchLimit ||\n Date.now() - this.dht._lastRandomPunch < this.dht._randomPunchInterval\n ) {\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 this.dht.stats.relaying.attempts++\n\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 this.dht.stats.relaying.successes++\n\n if (hs.prepunching) clearTimeout(hs.prepunching)\n hs.prepunching = null\n\n const { remotePort, remoteHost, socket } = 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 const dht = this.dht\n function onabort() {\n if (!hs.relayPaired) dht.stats.relaying.aborts++\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 if (hs.aborted && hs.rawStream) hs.rawStream.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)) {\n return relayThrough[Math.floor(Math.random() * relayThrough.length)]\n }\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.29.0\",\n \"description\": \"The DHT powering Hyperswarm\",\n \"main\": \"index.js\",\n \"browser\": \"browser.js\",\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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"test\": \"prettier --check . && node test/all.js\",\n \"test:bare\": \"bare test/all.js\",\n \"test:generate\": \"brittle -r test/all.js test/*.js\",\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}\nconst Hyperbee = require('hyperbee')\nconst Hyperblobs = require('hyperblobs')\nconst isOptions = require('is-options')\nconst { Writable, Readable } = require('streamx')\nconst unixPathResolve = require('unix-path-resolve')\nconst MirrorDrive = require('mirror-drive')\nconst SubEncoder = require('sub-encoder')\nconst ReadyResource = require('ready-resource')\nconst safetyCatch = require('safety-catch')\nconst crypto = require('hypercore-crypto')\nconst Hypercore = require('hypercore')\nconst { BLOCK_NOT_AVAILABLE, BAD_ARGUMENT } = require('hypercore-errors')\nconst Monitor = require('./lib/monitor')\nconst Download = require('./lib/download')\n\nconst keyEncoding = new SubEncoder('files', 'utf-8')\n\nconst [BLOBS] = crypto.namespace('hyperdrive', 1)\n\nmodule.exports = class Hyperdrive extends ReadyResource {\n constructor(corestore, key, opts = {}) {\n super()\n\n if (isOptions(key)) {\n opts = key\n key = null\n }\n\n this.corestore = corestore\n this.db = opts._db || makeBee(key, corestore, opts)\n this.core = this.db.core\n this.blobs = null\n this.supportsMetadata = true\n this.encryptionKey = opts.encryptionKey || null\n this.monitors = new Set()\n\n this._active = opts.active !== false\n this._openingBlobs = null\n this._onwait = opts.onwait || null\n this._batching = !!(opts._checkout === null && opts._db)\n this._checkout = opts._checkout || null\n\n this.ready().catch(safetyCatch)\n }\n\n [Symbol.asyncIterator]() {\n return this.entries()[Symbol.asyncIterator]()\n }\n\n static async getDriveKey(corestore) {\n const core = makeBee(undefined, corestore)\n await core.ready()\n const key = core.key\n await core.close()\n return key\n }\n\n static getContentKey(m, key) {\n if (m instanceof Hypercore) {\n if (m.core.compat) return null\n return Hyperdrive.getContentKey(m.manifest, m.key)\n }\n\n const manifest = generateContentManifest(m, key)\n if (!manifest) return null\n\n return Hypercore.key(manifest)\n }\n\n static getContentManifest(m, key) {\n return generateContentManifest(m, key)\n }\n\n _generateBlobsManifest() {\n const m = this.db.core.manifest\n if (this.db.core.core.compat) return null\n\n return generateContentManifest(m, this.core.key)\n }\n\n get id() {\n return this.core.id\n }\n\n get key() {\n return this.core.key\n }\n\n get discoveryKey() {\n return this.core.discoveryKey\n }\n\n get contentKey() {\n return this.blobs?.core.key\n }\n\n get version() {\n return this.db.version\n }\n\n get writable() {\n return this.core.writable\n }\n\n get readable() {\n return this.core.readable\n }\n\n findingPeers() {\n return this.corestore.findingPeers()\n }\n\n async truncate(version, { blobs = -1 } = {}) {\n if (!this.opened) await this.ready()\n\n if (version > this.core.length) {\n throw BAD_ARGUMENT('Bad truncation length')\n }\n\n const blobsVersion = blobs === -1 ? await this.getBlobsLength(version) : blobs\n const bl = await this.getBlobs()\n\n if (blobsVersion > bl.core.length) {\n throw BAD_ARGUMENT('Bad truncation length')\n }\n\n await this.core.truncate(version)\n await bl.core.truncate(blobsVersion)\n }\n\n async getBlobsLength(checkout) {\n if (!this.opened) await this.ready()\n\n if (!checkout) checkout = this.version\n\n const c = this.db.checkout(checkout)\n\n try {\n return await getBlobsLength(c)\n } finally {\n await c.close()\n }\n }\n\n replicate(isInitiator, opts) {\n return this.corestore.replicate(isInitiator, opts)\n }\n\n update(opts) {\n return this.db.update(opts)\n }\n\n _makeCheckout(snapshot) {\n return new Hyperdrive(this.corestore, this.key, {\n onwait: this._onwait,\n encryptionKey: this.encryptionKey,\n _checkout: this._checkout || this,\n _db: snapshot\n })\n }\n\n checkout(version) {\n return this._makeCheckout(this.db.checkout(version))\n }\n\n batch() {\n return new Hyperdrive(this.corestore, this.key, {\n onwait: this._onwait,\n encryptionKey: this.encryptionKey,\n _checkout: null,\n _db: this.db.batch()\n })\n }\n\n setActive(bool) {\n const active = !!bool\n if (active === this._active) return\n this._active = active\n this.core.setActive(active)\n if (this.blobs) this.blobs.core.setActive(active)\n }\n\n async flush() {\n await this.db.flush()\n return this.close()\n }\n\n async _close() {\n if (this.blobs && (!this._checkout || this.blobs !== this._checkout.blobs)) {\n await this.blobs.core.close()\n }\n\n await this.db.close()\n\n if (!this._checkout && !this._batching) {\n await this.corestore.close()\n }\n\n await this.closeMonitors()\n }\n\n async _openBlobsFromHeader(opts) {\n if (this.blobs) return true\n\n const header = await getBee(this.db).getHeader(opts)\n if (!header) return false\n\n if (this.blobs) return true\n\n const contentKey =\n header.metadata && header.metadata.contentFeed && header.metadata.contentFeed.subarray(0, 32)\n const blobsKey = contentKey || Hypercore.key(this._generateBlobsManifest())\n if (!blobsKey || blobsKey.length < 32) throw new Error('Invalid or no Blob store key set')\n\n const blobsCore = this.corestore.get({\n key: blobsKey,\n cache: false,\n onwait: this._onwait,\n encryptionKey: this.encryptionKey,\n keyPair: !contentKey && this.db.core.writable ? this.db.core.keyPair : null,\n active: this._active\n })\n await blobsCore.ready()\n\n if (this.closing) {\n await blobsCore.close()\n return false\n }\n\n this.blobs = new Hyperblobs(blobsCore)\n\n this.emit('blobs', this.blobs)\n this.emit('content-key', blobsCore.key)\n\n return true\n }\n\n async _open() {\n if (this._checkout) {\n await this._checkout.ready()\n this.blobs = this._checkout.blobs\n return\n }\n\n await this._openBlobsFromHeader({ wait: false })\n\n if (this.db.core.writable && !this.blobs) {\n const m = this._generateBlobsManifest()\n const blobsCore = this.corestore.get({\n manifest: m,\n name: m ? null : this.db.core.id + '/blobs', // simple trick to avoid blobs clashing if no namespace is provided...\n cache: false,\n onwait: this._onwait,\n encryptionKey: this.encryptionKey,\n compat: this.db.core.core.compat,\n active: this._active,\n keyPair: m && this.db.core.writable ? this.db.core.keyPair : null\n })\n await blobsCore.ready()\n\n this.blobs = new Hyperblobs(blobsCore)\n\n if (!m) getBee(this.db).metadata.contentFeed = this.blobs.core.key\n\n this.emit('blobs', this.blobs)\n this.emit('content-key', blobsCore.key)\n }\n\n await this.db.ready()\n\n if (!this.blobs) {\n // eagerly load the blob store....\n this._openingBlobs = this._openBlobsFromHeader()\n this._openingBlobs.catch(safetyCatch)\n }\n }\n\n async getBlobs() {\n if (this.blobs) return this.blobs\n\n if (this._checkout) {\n this.blobs = await this._checkout.getBlobs()\n } else {\n await this.ready()\n await this._openingBlobs\n }\n\n return this.blobs\n }\n\n monitor(name, opts = {}) {\n const monitor = new Monitor(this, { name, ...opts })\n this.monitors.add(monitor)\n return monitor\n }\n\n async closeMonitors() {\n const closing = []\n for (const monitor of this.monitors) closing.push(monitor.close())\n await Promise.allSettled(closing)\n }\n\n async get(name, opts) {\n const node = await this.entry(name, opts)\n if (!node?.value.blob) return null\n await this.getBlobs()\n const res = await this.blobs.get(node.value.blob, opts)\n\n if (res === null) throw BLOCK_NOT_AVAILABLE()\n return res\n }\n\n async putEntry(name, { executable = false, linkname = null, blob = null, metadata = null } = {}) {\n return this.db.put(\n std(name, false),\n { executable, linkname: null, blob, metadata },\n { keyEncoding }\n )\n }\n\n async put(name, buf, { executable = false, metadata = null } = {}) {\n await this.getBlobs()\n const blob = await this.blobs.put(buf)\n return this.db.put(\n std(name, false),\n { executable, linkname: null, blob, metadata },\n { keyEncoding }\n )\n }\n\n async del(name) {\n return this.db.del(std(name, false), { keyEncoding })\n }\n\n compare(a, b) {\n const diff = a.seq - b.seq\n return diff > 0 ? 1 : diff < 0 ? -1 : 0\n }\n\n async clear(name, opts) {\n if (!this.opened) await this.ready()\n\n let node = null\n\n try {\n node = await this.entry(name, { wait: false })\n } catch {\n // do nothing, prop not available\n }\n\n if (node === null || this.blobs === null) {\n return opts && opts.diff ? { blocks: 0 } : null\n }\n\n return this.blobs.clear(node.value.blob, opts)\n }\n\n async clearAll(opts) {\n if (!this.opened) await this.ready()\n\n if (this.blobs === null) {\n return opts && opts.diff ? { blocks: 0 } : null\n }\n\n return this.blobs.core.clear(0, this.blobs.core.length, opts)\n }\n\n async purge() {\n if (this._checkout || this._batch) throw new Error('Can only purge the main session')\n\n await this.ready() // Ensure blobs loaded if present\n await this.close()\n\n const proms = [this.core.purge()]\n if (this.blobs) proms.push(this.blobs.core.purge())\n await Promise.all(proms)\n }\n\n async symlink(name, dst, { metadata = null } = {}) {\n return this.db.put(\n std(name, false),\n { executable: false, linkname: dst, blob: null, metadata },\n { keyEncoding }\n )\n }\n\n async entry(name, opts) {\n if (!opts || !opts.follow) return this._entry(name, opts)\n\n for (let i = 0; i < 16; i++) {\n const node = await this._entry(name, opts)\n if (!node || !node.value.linkname) return node\n\n name = unixPathResolve(node.key, node.value.linkname)\n }\n\n throw new Error('Recursive symlink')\n }\n\n async _entry(name, opts) {\n if (typeof name !== 'string') return name\n\n return this.db.get(std(name, false), { ...opts, keyEncoding })\n }\n\n async exists(name) {\n return (await this.entry(name)) !== null\n }\n\n watch(folder) {\n folder = std(folder || '/', true)\n\n return this.db.watch(prefixRange(folder), {\n keyEncoding,\n map: (snap) => this._makeCheckout(snap)\n })\n }\n\n diff(length, folder, opts) {\n if (typeof folder === 'object' && folder && !opts) return this.diff(length, null, folder)\n\n folder = std(folder || '/', true)\n\n return this.db.createDiffStream(length, prefixRange(folder), {\n ...opts,\n keyEncoding\n })\n }\n\n async downloadDiff(length, folder, opts) {\n const dls = []\n\n for await (const entry of this.diff(length, folder, opts)) {\n if (!entry.left) continue\n const b = entry.left.value.blob\n if (!b) continue\n const blobs = await this.getBlobs()\n dls.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength }))\n }\n\n return new Download(this, null, { downloads: dls })\n }\n\n async downloadRange(dbRanges, blobRanges) {\n const dls = []\n\n await this.ready()\n\n for (const range of dbRanges) {\n dls.push(this.db.core.download(range))\n }\n\n const blobs = await this.getBlobs()\n\n for (const range of blobRanges) {\n dls.push(blobs.core.download(range))\n }\n\n return new Download(this, null, { downloads: dls })\n }\n\n entries(range, opts) {\n const stream = this.db.createReadStream(range, { ...opts, keyEncoding })\n if (opts && opts.ignore) stream._readableState.map = createStreamMapIgnore(opts.ignore)\n return stream\n }\n\n download(folder = '/', opts) {\n if (typeof folder === 'object') return this.download(undefined, folder)\n\n return new Download(this, folder, opts)\n }\n\n async has(path) {\n const blobs = await this.getBlobs()\n const entry = !path || path.endsWith('/') ? null : await this.entry(path)\n if (entry) {\n const b = entry.value.blob\n if (!b) return false\n return await blobs.core.has(b.blockOffset, b.blockOffset + b.blockLength)\n }\n for await (const entry of this.list(path)) {\n const b = entry.value.blob\n if (!b) continue\n const has = await blobs.core.has(b.blockOffset, b.blockOffset + b.blockLength)\n if (!has) return false\n }\n return true\n }\n\n // atm always recursive, but we should add some depth thing to it\n list(folder, opts = {}) {\n if (typeof folder === 'object' && folder !== null) return this.list(undefined, folder)\n\n folder = std(folder || '/', true)\n\n const ignore = opts.ignore ? toIgnoreFunction(opts.ignore) : null\n const stream =\n opts && opts.recursive === false\n ? shallowReadStream(this.db, folder, false, ignore, opts)\n : this.entries(prefixRange(folder), { ...opts, ignore })\n return stream\n }\n\n readdir(folder, opts) {\n folder = std(folder || '/', true)\n return shallowReadStream(this.db, folder, true, null, opts)\n }\n\n mirror(out, opts) {\n return new MirrorDrive(this, out, opts)\n }\n\n createReadStream(name, opts) {\n const self = this\n\n let destroyed = false\n let rs = null\n\n const stream = new Readable({\n open(cb) {\n self.getBlobs().then(onblobs, cb)\n\n function onblobs() {\n self.entry(name).then(onnode, cb)\n }\n\n function onnode(node) {\n if (destroyed) return cb(null)\n if (!node) return cb(new Error('Blob does not exist'))\n if (self.closing) return cb(new Error('Closed'))\n\n if (!node.value.blob) {\n stream.push(null)\n return cb(null)\n }\n\n rs = self.blobs.createReadStream(node.value.blob, opts)\n\n rs.on('data', function (data) {\n if (!stream.push(data)) rs.pause()\n })\n\n rs.on('end', function () {\n stream.push(null)\n })\n\n rs.on('error', function (err) {\n stream.destroy(err)\n })\n\n cb(null)\n }\n },\n read(cb) {\n rs.resume()\n cb(null)\n },\n predestroy() {\n destroyed = true\n if (rs) rs.destroy()\n }\n })\n\n return stream\n }\n\n createWriteStream(name, { executable = false, metadata = null } = {}) {\n const self = this\n\n let destroyed = false\n let ws = null\n let ondrain = null\n let onfinish = null\n\n const stream = new Writable({\n open(cb) {\n self.getBlobs().then(onblobs, cb)\n\n function onblobs() {\n if (destroyed) return cb(null)\n\n ws = self.blobs.createWriteStream()\n\n ws.on('error', function (err) {\n stream.destroy(err)\n })\n\n ws.on('close', function () {\n const err = new Error('Closed')\n callOndrain(err)\n callOnfinish(err)\n })\n\n ws.on('finish', function () {\n callOnfinish(null)\n })\n\n ws.on('drain', function () {\n callOndrain(null)\n })\n\n cb(null)\n }\n },\n write(data, cb) {\n if (ws.write(data) === true) return cb(null)\n ondrain = cb\n },\n final(cb) {\n onfinish = cb\n ws.end()\n },\n predestroy() {\n destroyed = true\n if (ws) ws.destroy()\n }\n })\n\n return stream\n\n function callOnfinish(err) {\n if (!onfinish) return\n\n const cb = onfinish\n onfinish = null\n\n if (err) return cb(err)\n self.db\n .put(\n std(name, false),\n { executable, linkname: null, blob: ws.id, metadata },\n { keyEncoding }\n )\n .then(() => cb(null), cb)\n }\n\n function callOndrain(err) {\n if (ondrain) {\n const cb = ondrain\n ondrain = null\n cb(err)\n }\n }\n }\n\n static normalizePath(name) {\n return std(name, false)\n }\n}\n\nfunction shallowReadStream(files, folder, keys, ignore, opts) {\n let prev = '/'\n let prevName = ''\n\n return new Readable({\n async read(cb) {\n let node = null\n\n try {\n node = await files.peek(prefixRange(folder, prev), {\n ...opts,\n keyEncoding\n })\n } catch (err) {\n return cb(err)\n }\n\n if (!node) {\n this.push(null)\n return cb(null)\n }\n\n const suffix = node.key.slice(folder.length + 1)\n const i = suffix.indexOf('/')\n const name = i === -1 ? suffix : suffix.slice(0, i)\n\n prev = '/' + name + (i === -1 ? '' : '0')\n\n // just in case someone does /foo + /foo/bar, but we should prop not even support that\n if (name === prevName) {\n this._read(cb)\n return\n }\n\n prevName = name\n\n if (ignore && ignore(node.key)) {\n this._read(cb)\n return\n }\n\n this.push(keys ? name : node)\n cb(null)\n }\n })\n}\n\nfunction makeBee(key, corestore, opts = {}) {\n const name = key ? undefined : 'db'\n const core = corestore.get({\n key,\n name,\n exclusive: true,\n onwait: opts.onwait,\n encryptionKey: opts.encryptionKey,\n compat: opts.compat,\n active: opts.active\n })\n\n return new Hyperbee(core, {\n keyEncoding: 'utf-8',\n valueEncoding: 'json',\n metadata: { contentFeed: null },\n extension: opts.extension\n })\n}\n\nfunction getBee(bee) {\n // A Batch instance will have a .tree property for the actual Hyperbee\n return bee.tree || bee\n}\n\nfunction std(name, removeSlash) {\n // Note: only remove slash if you're going to use it as prefix range\n name = unixPathResolve('/', name)\n if (removeSlash && name.endsWith('/')) name = name.slice(0, -1)\n validateFilename(name)\n return name\n}\n\nfunction validateFilename(name) {\n if (name === '/') throw new Error('Invalid filename: ' + name)\n}\n\nfunction prefixRange(name, prev = '/') {\n // '0' is binary +1 of /\n return { gt: name + prev, lt: name + '0' }\n}\n\nfunction generateContentManifest(m, key) {\n if (m.version < 1) return null\n\n const signers = []\n\n if (!key) key = Hypercore.key(m)\n\n for (const s of m.signers) {\n const namespace = crypto.hash([BLOBS, key, s.namespace])\n signers.push({ ...s, namespace })\n }\n\n return {\n version: m.version,\n hash: 'blake2b',\n allowPatch: m.allowPatch,\n quorum: m.quorum,\n signers,\n prologue: null // TODO: could be configurable through the header still...\n }\n}\n\nasync function getBlobsLength(db) {\n let length = 0\n\n for await (const { value } of db.createReadStream()) {\n const b = value && value.blob\n if (!b) continue\n const len = b.blockOffset + b.blockLength\n if (len > length) length = len\n }\n\n return length\n}\n\nfunction toIgnoreFunction(ignore) {\n if (typeof ignore === 'function') return ignore\n\n const all = [].concat(ignore).map((e) => unixPathResolve('/', e))\n return (key) => all.some((path) => path === key || key.startsWith(path + '/'))\n}\n\nfunction createStreamMapIgnore(ignore) {\n return (node) => (ignore(node.key) ? null : node)\n}\nconst ReadyResource = require('ready-resource')\n\nmodule.exports = class Download extends ReadyResource {\n constructor(drive, folder, options) {\n super()\n\n this.drive = drive\n this.folder = folder\n this.options = options || {}\n this.downloads = this.options.downloads || []\n this.destroyed = false\n this.ready().catch(noop)\n }\n\n async _open() {\n const drive = this.drive\n const entry =\n !this.folder || this.folder.endsWith('/')\n ? null\n : await drive.entry(this.folder, this.options)\n\n if (entry) {\n const b = entry.value.blob\n if (!b) return\n const blobs = await drive.getBlobs()\n const download = blobs.core.download({\n start: b.blockOffset,\n length: b.blockLength\n })\n this.downloads.push(download)\n return\n }\n\n if (this.folder !== null) {\n // first preload the list so we can use the full power afterwards to actually preload everything\n // eslint-disable-next-line\n for await (const _ of drive.list(this.folder, this.options)) {\n // ignore\n }\n\n for await (const entry of drive.list(this.folder, this.options)) {\n const b = entry.value.blob\n if (!b) continue\n\n const blobs = await drive.getBlobs()\n this.downloads.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength }))\n }\n }\n }\n\n _close() {\n for (const d of this.downloads) {\n d.destroy()\n }\n }\n\n destroy() {\n this.destroyed = true\n this._safeBackgroundDestroy()\n }\n\n async _safeBackgroundDestroy() {\n try {\n await this.ready()\n } catch {}\n\n await this.close()\n }\n\n async done() {\n await this.ready()\n await Promise.allSettled(this.downloads.map((d) => d.done()))\n }\n}\n\nfunction noop() {}\nconst ReadyResource = require('ready-resource')\nconst safetyCatch = require('safety-catch')\nconst speedometer = require('speedometer')\n\nmodule.exports = class Monitor extends ReadyResource {\n constructor(drive, opts = {}) {\n super()\n this.drive = drive\n this.blobs = null\n this.name = opts.name || null\n this.entry = opts.entry || null\n this.peers = 0\n\n this._boundOnUpload = this._onUpload.bind(this)\n this._boundOnDownload = this._onDownload.bind(this)\n this._boundPeerUpdate = this._updatePeers.bind(this)\n\n const stats = {\n startTime: 0,\n percentage: 0,\n peers: 0,\n speed: 0,\n blocks: 0,\n totalBytes: 0, // local + bytes loaded during monitoring\n monitoringBytes: 0, // bytes loaded during monitoring\n targetBytes: 0,\n targetBlocks: 0\n }\n\n // Updated on each upload/download event\n this.uploadStats = { ...stats }\n this.downloadStats = { ...stats }\n\n this.uploadSpeedometer = null\n this.downloadSpeedometer = null\n\n this.ready().catch(safetyCatch)\n }\n\n async _open() {\n await this.drive.ready()\n this.blobs = await this.drive.getBlobs()\n if (!this.entry && this.name) this.entry = await this.drive.entry(this.name)\n if (this.entry) this._setEntryInfo()\n\n this.uploadSpeedometer = speedometer()\n this.downloadSpeedometer = speedometer()\n\n this._updatePeers()\n\n // Handlers\n this.blobs.core.on('peer-add', this._boundPeerUpdate)\n this.blobs.core.on('peer-remove', this._boundPeerUpdate)\n this.blobs.core.on('upload', this._boundOnUpload)\n this.blobs.core.on('download', this._boundOnDownload)\n\n if (!this.entry) {\n this.drive.db.core.on('peer-add', this._boundPeerUpdate)\n this.drive.db.core.on('peer-remove', this._boundPeerUpdate)\n this.drive.db.core.on('upload', this._boundOnUpload)\n this.drive.db.core.on('download', this._boundOnDownload)\n }\n }\n\n async _close() {\n this.blobs.core.off('peer-add', this._boundPeerUpdate)\n this.blobs.core.off('peer-remove', this._boundPeerUpdate)\n this.blobs.core.off('upload', this._boundOnUpload)\n this.blobs.core.off('download', this._boundOnDownload)\n\n if (!this.entry) {\n this.drive.db.core.off('peer-add', this._boundPeerUpdate)\n this.drive.db.core.off('peer-remove', this._boundPeerUpdate)\n this.drive.db.core.off('upload', this._boundOnUpload)\n this.drive.db.core.off('download', this._boundOnDownload)\n }\n\n this.drive.monitors.delete(this)\n }\n\n _setEntryInfo() {\n if (!this.downloadStats.targetBytes || !this.downloadStats.targetBlocks) {\n this.downloadStats.targetBytes = this.entry.value.blob.byteLength\n this.downloadStats.targetBlocks = this.entry.value.blob.blockLength\n }\n\n if (!this.uploadStats.targetBytes || !this.uploadStats.targetBlocks) {\n this.uploadStats.targetBytes = this.entry.value.blob.byteLength\n this.uploadStats.targetBlocks = this.entry.value.blob.blockLength\n }\n }\n\n _onUpload(index, bytes, from) {\n this._updateStats(this.uploadSpeedometer, this.uploadStats, index, bytes, from)\n }\n\n _onDownload(index, bytes, from) {\n this._updateStats(this.downloadSpeedometer, this.downloadStats, index, bytes, from)\n }\n\n _updatePeers() {\n this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length\n }\n\n _updateStats(speed, stats, index, bytes, from) {\n if (this.closing) return\n if (this.entry && !isWithinRange(index, this.entry)) return\n\n if (!stats.startTime) stats.startTime = Date.now()\n\n stats.speed = speed(bytes)\n stats.blocks++\n stats.monitoringBytes += bytes\n stats.totalBytes += bytes\n // NOTE: you should not rely on the percentage until the monitor is initialized with the local state of the file\n stats.percentage = toFixed((stats.blocks / stats.targetBlocks) * 100)\n\n this.emit('update')\n }\n\n downloadSpeed() {\n return this.downloadSpeedometer ? this.downloadSpeedometer() : 0\n }\n\n uploadSpeed() {\n return this.uploadSpeedometer ? this.uploadSpeedometer() : 0\n }\n}\n\nfunction isWithinRange(index, entry) {\n if (!entry || !entry.value) return\n const { blockOffset, blockLength } = entry.value.blob\n return index >= blockOffset && index < blockOffset + blockLength\n}\n\nfunction toFixed(n) {\n return Math.round(n * 100) / 100\n}\n{\n \"name\": \"hyperdrive\",\n \"version\": \"13.2.1\",\n \"description\": \"Hyperdrive is a secure, real-time distributed file system\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/**.js\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hyperdrive.git\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:bare\": \"brittle-bare --coverage test.js\",\n \"test:node\": \"brittle-node --coverage test.js\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperdrive/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperdrive#readme\",\n \"dependencies\": {\n \"hyperbee\": \"^2.11.1\",\n \"hyperblobs\": \"^2.3.0\",\n \"hypercore\": \"^11.0.0\",\n \"hypercore-errors\": \"^1.0.0\",\n \"is-options\": \"^1.0.2\",\n \"mirror-drive\": \"^1.2.0\",\n \"ready-resource\": \"^1.0.0\",\n \"safety-catch\": \"^1.0.2\",\n \"speedometer\": \"^1.1.0\",\n \"streamx\": \"^2.12.4\",\n \"sub-encoder\": \"^2.1.1\",\n \"unix-path-resolve\": \"^1.0.2\"\n },\n \"devDependencies\": {\n \"b4a\": \"^1.6.0\",\n \"bare-events\": \"^2.5.4\",\n \"bare-fs\": \"^4.1.2\",\n \"bare-path\": \"^3.0.0\",\n \"brittle\": \"^3.1.0\",\n \"corestore\": \"^7.0.0\",\n \"hypercore-crypto\": \"^3.4.0\",\n \"hyperdht\": \"^6.6.0\",\n \"hyperswarm\": \"^4.0.0\",\n \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"test-tmp\": \"^1.3.0\"\n },\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n },\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n }\n}\n{\n \"name\": \"hyperschema\",\n \"version\": \"1.19.1\",\n \"description\": \"Create registries of declarative compact-encoding schemas\",\n \"files\": [\n \"lib/*.js\",\n \"builder.mjs\",\n \"builder.cjs\",\n \"runtime.mjs\",\n \"runtime.cjs\",\n \"primitives.mjs\",\n \"primitives.cjs\"\n ],\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"import\": \"./builder.mjs\",\n \"default\": \"./builder.cjs\"\n },\n \"./runtime\": {\n \"import\": \"./runtime.mjs\",\n \"default\": \"./runtime.cjs\"\n },\n \"./primitives\": {\n \"import\": \"./primitives.mjs\",\n \"default\": \"./primitives.cjs\"\n }\n },\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"scripts\": {\n \"format\": \"prettier . --write\",\n \"test\": \"prettier . --check && brittle test/index.js\",\n \"test:bare\": \"prettier . --check && bare test/index.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/hyperschema.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/hyperschema/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/hyperschema#readme\",\n \"dependencies\": {\n \"bare-fs\": \"^4.0.1\",\n \"compact-encoding\": \"^2.15.0\",\n \"generate-object-property\": \"^2.0.0\",\n \"generate-string\": \"^1.0.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.7.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"test-tmp\": \"^1.3.0\"\n }\n}\nmodule.exports = {\n c: require('compact-encoding')\n}\nconst { EventEmitter } = require('events')\nconst { getStreamError } = require('streamx')\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'\nconst ERR_FIREWALL = 'Peer is firewalled'\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 =\n opts.dht ||\n new DHT({\n bootstrap: opts.bootstrap,\n nodes: opts.nodes,\n port: opts.port,\n deferRandomPunch: opts.deferRandomPunch,\n randomPunchInterval: opts.randomPunchInterval\n })\n this.server = this.dht.createServer(\n {\n firewall: this._handleFirewall.bind(this),\n relayThrough: this._maybeRelayConnection.bind(this),\n handshakeClearWait: opts.handshakeClearWait\n },\n this._handleServerConnection.bind(this)\n )\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 ? toRelayFunction(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 bannedPeers: 0\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.dht.on('network-update', this._handleNetworkUpdate.bind(this))\n this.on('update', this._handleUpdate)\n }\n\n _maybeRelayConnection(force) {\n if (!this.relayThrough) return null\n return this.relayThrough(force, this)\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 (\n peerInfo._updatePriority() === false ||\n this._allConnections.has(peerInfo.publicKey) ||\n peerInfo.queued\n )\n 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 (\n this.connecting > 0 ||\n (this._allConnections.size < this.maxPeers &&\n this._clientConnections < this.maxClientConnections)\n ) {\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 && !this.suspended && this.connecting < this.maxParallel\n }\n\n _shouldConnect() {\n return (\n !this.destroyed &&\n !this.suspended &&\n this.connecting < this.maxParallel &&\n this._allConnections.size < this.maxPeers &&\n this._clientConnections < this.maxClientConnections\n )\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 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 const err = getStreamError(conn)\n if (shouldBan(err)) {\n this._banPeer(peerInfo, true, err)\n }\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 || this.suspended) return\n this._drainingQueue = true\n\n for (const peerInfo of this.explicitPeers) {\n if (!this._shouldConnectExplicit()) break\n if (\n peerInfo.attempts >= 5 ||\n Date.now() - peerInfo.disconnectedTime < peerInfo.attempts * 1000\n )\n 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 (b4a.equals(remotePublicKey, this.keyPair.publicKey)) return true\n\n let peerInfo = this.peers.get(b4a.toString(remotePublicKey, 'hex'))\n if (peerInfo && peerInfo.banned) return true\n\n const firewalled = this._firewall(remotePublicKey, payload)\n if (firewalled) {\n if (!peerInfo) peerInfo = this._upsertPeer(remotePublicKey)\n this._banPeer(peerInfo, true, new Error(ERR_FIREWALL))\n }\n\n return firewalled\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 const err = getStreamError(conn)\n if (shouldBan(err)) {\n this._banPeer(peerInfo, true, err)\n }\n\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 _handleNetworkUpdate() {\n if (!this.online) return\n this._handleNetworkChange()\n }\n\n async _handleNetworkChange() {\n if (this.suspended) return\n\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 _banPeer(peerInfo, banned, err) {\n peerInfo.ban(banned)\n this.stats.bannedPeers++\n this.emit('ban', peerInfo, err)\n }\n\n status(key) {\n return this._discovery.get(b4a.toString(key, 'hex')) || null\n }\n\n listen() {\n if (!this.listening) {\n if (this.destroyed) throw new Error('Swarm destroyed')\n this.listening = this.server.listen(this.keyPair)\n }\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 (this.destroyed) throw new Error('Swarm destroyed')\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 const pending = []\n for (const connection of this._allConnections) {\n connection.destroy()\n pending.push(new Promise((resolve) => connection.on('close', resolve)))\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 await Promise.all(pending)\n\n // reset queue\n this._timer.destroy()\n this._timer = new RetryTimer(this._requeue.bind(this), {\n backoffs: this._timer.backoffs,\n jitter: this._timer.jitter\n })\n this._queue = spq()\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.suspended = false\n this._attemptClientConnections()\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 (\n code === 'HOLEPUNCH_ABORTED' ||\n code === 'HOLEPUNCH_DOUBLE_RANDOMIZED_NATS' ||\n code === 'REMOTE_NOT_HOLEPUNCHABLE'\n )\n}\n\nfunction shouldBan() {\n // return !!err && err.name === 'HypercoreError' && err.code === 'INVALID_OPERATION'\n return false\n}\n\nfunction toRelayFunction(relayThrough) {\n return typeof relayThrough === 'function'\n ? relayThrough\n : (force, swarm) => (force || swarm.dht.randomized ? relayThrough : null)\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(toHex(publicKey))\n }\n\n get(publicKey) {\n return this._byPublicKey.get(toHex(publicKey))\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}\n\nfunction toHex(b) {\n return typeof b === 'string' ? b : b4a.toString(b, 'hex')\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\nconst MAX_DISCOVERY_CACHE = 64\n\nmodule.exports = class PeerDiscovery {\n constructor(\n swarm,\n topic,\n { limit = Infinity, wait = null, suspended = false, onpeer = noop, onerror = safetyCatch }\n ) {\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._discovered = new Set()\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 ? REFRESH_INTERVAL + jitter : 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 let limit = this.limit\n\n if (limit < Infinity && limit > 0) {\n for (const id of this._discovered) {\n if (!this.swarm.connections.has(id)) continue\n if (--limit === 0) break\n }\n }\n\n this._discovered.clear()\n\n const announcing = this.isServer\n const query = (this._activeQuery = announcing\n ? this.swarm.dht.announce(\n this.topic,\n this.swarm.keyPair,\n this.swarm.server.relayAddresses,\n opts\n )\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 (limit < Infinity) {\n const id = b4a.toString(peer.publicKey, 'hex')\n\n if (this._discovered.size < MAX_DISCOVERY_CACHE) {\n this._discovered.add(id)\n }\n\n if (limit === 0) continue\n\n // there is a chance it has updated a connection during discovery and we go over\n // the limit - thats acceptable to avoid a complexity spiral here.\n if (!this.swarm.connections.has(id)) limit--\n }\n\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) {\n // 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)\n await this.swarm.dht.unannounce(this.topic, this.swarm.keyPair, {\n closestNodes: nodes,\n onlyClosestNodes: true,\n force: true\n })\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(\n push,\n { backoffs = [BACKOFF_S, BACKOFF_M, BACKOFF_L, BACKOFF_X], jitter = BACKOFF_JITTER } = {}\n ) {\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:\n return this._sTimer\n case 2:\n return this._mTimer\n case 3:\n return this._lTimer\n }\n } else {\n switch (peerInfo.attempts) {\n case 1:\n return this._mTimer\n case 2:\n return this._lTimer\n case 3:\n 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.16.0\",\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.21.0\",\n \"safety-catch\": \"^1.0.2\",\n \"shuffled-priority-queue\": \"^2.1.0\",\n \"streamx\": \"^2.22.1\",\n \"unslab\": \"^1.3.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.2\",\n \"hypercore-crypto\": \"^3.4.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check .\",\n \"test\": \"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}\nconst b4a = require('b4a')\n\nconst EMPTY = b4a.alloc(0)\nconst MAX = b4a.from([0xff])\nconst BUFFER = {}\n\nBUFFER.preencode = function (state, buf) {\n if (buf === null) buf = EMPTY\n\n let i = 0\n let extra = 3\n\n while ((i = b4a.indexOf(buf, 0x00, i)) > -1) {\n i++\n extra++\n }\n\n state.end += buf.byteLength + extra\n}\n\nBUFFER.encode = function (state, buf) {\n if (buf === null) buf = EMPTY\n\n state.buffer[state.start++] = 0x00\n\n let prev = 0\n let i = 0\n\n while ((i = b4a.indexOf(buf, 0x00, i)) > -1) {\n const slice = buf.subarray(prev, ++i)\n\n state.buffer.set(slice, state.start)\n state.start += slice.byteLength\n state.buffer[state.start++] = 0x02\n prev = i\n }\n\n const slice = buf.subarray(prev)\n\n state.buffer.set(slice, state.start)\n state.start += slice.byteLength\n\n state.buffer[state.start++] = 0x00\n state.buffer[state.start++] = 0x01\n}\n\nBUFFER.decode = function (state) {\n if (state.start >= state.end) throw new Error('Out of bounds')\n if (state.buffer[state.start++] !== 0x00) throw new Error('Invalid start of string')\n\n let escaped = null\n\n let prev = state.start\n let i = state.start\n\n while ((i = b4a.indexOf(state.buffer, 0x00, i)) > -1) {\n const next = ++i < state.end ? state.buffer[i] : 0x00\n\n i++\n\n if (next === 0x01) {\n break\n }\n\n if (next === 0x02) {\n if (escaped === null) escaped = []\n escaped.push(state.buffer.subarray(prev, i - 1))\n prev = i\n continue\n }\n\n throw new Error('Unknown value in terminator')\n }\n\n if (i === -1) {\n throw new Error('No terminator found')\n }\n\n state.start = i\n const last = state.buffer.subarray(prev, i - 2)\n if (escaped === null) return last\n\n escaped.push(last)\n return b4a.concat(escaped)\n}\n\n// TODO: can be optimised a lot\n\nconst STRING = {}\n\nSTRING.preencode = function (state, str) {\n BUFFER.preencode(state, b4a.from(str || ''))\n}\n\nSTRING.encode = function (state, str) {\n BUFFER.encode(state, b4a.from(str || ''))\n}\n\nSTRING.decode = function (state, str) {\n return b4a.toString(BUFFER.decode(state))\n}\n\nconst UINT = {}\n\nUINT.preencode = function (state, n) {\n state.end += n <= 0xfb ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : (n === Infinity ? 1 : 9)\n}\n\nUINT.encode = function (state, n) {\n if (n === Infinity) {\n state.buffer[state.start++] = 0xff\n return\n }\n\n if (n <= 0xfb) {\n state.buffer[state.start++] = n\n return\n }\n\n if (n <= 0xffff) {\n state.buffer[state.start++] = 0xfc\n state.buffer[state.start++] = n >>> 8\n state.buffer[state.start++] = n\n return\n }\n\n if (n <= 0xffffffff) {\n state.buffer[state.start++] = 0xfd\n encodeUint32(state, n)\n return\n }\n\n if (Number.isSafeInteger(n)) {\n state.buffer[state.start++] = 0xfe\n\n const r = Math.floor(n / 0x100000000)\n encodeUint32(state, r)\n encodeUint32(state, n)\n return\n }\n\n throw new Error('Invalid number ' + n)\n}\n\nUINT.decode = function (state) {\n if (state.start >= state.end) throw new Error('Out of bounds')\n\n const a = state.buffer[state.start++]\n\n if (a <= 0xfb) return a\n\n if (a === 0xfc) {\n if (state.end - state.start < 2) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] * 0x100 +\n state.buffer[state.start++]\n )\n }\n\n if (a === 0xfd) {\n return decodeUint32(state)\n }\n\n if (a === 0xfe) {\n return (\n decodeUint32(state) * 0x100000000 +\n decodeUint32(state)\n )\n }\n\n return Infinity\n}\n\nconst BOOL = {}\n\nBOOL.preencode = (state, b) => UINT.preencode(state, b ? 1 : 0)\nBOOL.encode = (state, b) => UINT.encode(state, b ? 1 : 0)\nBOOL.decode = (state, b) => !!UINT.decode(state)\n\nmodule.exports = class IndexEncoder {\n constructor (encodings, { prefix = -1 } = {}) {\n this.encodings = encodings\n this.prefix = prefix\n }\n\n static BUFFER = BUFFER\n static STRING = STRING\n static UINT = UINT\n static BOOL = BOOL\n\n static lookup (c) {\n switch (c) {\n case 'uint': return UINT\n case 'uint8': return UINT\n case 'uint16': return UINT\n case 'uint24': return UINT\n case 'uint32': return UINT\n case 'uint40': return UINT\n case 'uint48': return UINT\n case 'uint56': return UINT\n case 'uint64': return UINT\n case 'string': return STRING\n case 'utf8': return STRING\n case 'ascii': return STRING\n case 'hex': return STRING\n case 'base64': return STRING\n case 'fixed32': return BUFFER\n case 'fixed64': return BUFFER\n case 'buffer': return BUFFER\n case 'bool': return BOOL\n }\n\n throw new Error('Unknown type')\n }\n\n encode (keys) {\n return this._encode(keys, false)\n }\n\n _encode (keys, terminate) {\n if (b4a.isBuffer(keys)) return keys\n\n const state = { start: 0, end: 0, buffer: null }\n\n if (this.prefix !== -1) UINT.preencode(state, this.prefix)\n for (let i = 0; i < keys.length; i++) {\n this.encodings[i].preencode(state, keys[i])\n }\n\n if (terminate && keys.length < this.encodings.length) {\n state.end++\n }\n\n state.buffer = b4a.allocUnsafe(state.end)\n\n if (this.prefix !== -1) UINT.encode(state, this.prefix)\n for (let i = 0; i < keys.length; i++) {\n this.encodings[i].encode(state, keys[i])\n }\n\n if (terminate && keys.length < this.encodings.length) {\n state.buffer[state.start++] = MAX[0]\n }\n\n return state.buffer\n }\n\n decode (buffer) {\n const state = { start: 0, end: buffer.byteLength, buffer }\n const result = []\n\n if (this.prefix !== -1) UINT.decode(state)\n for (const enc of this.encodings) {\n const key = state.start < state.end ? enc.decode(state) : (enc === UINT ? 0 : null)\n result.push(key)\n }\n\n return result\n }\n\n encodeRange ({ gt, gte, lt, lte }) {\n const range = {\n gt: gt && this._encode(gt, true),\n gte: gte && this._encode(gte, false),\n lt: lt && this._encode(lt, false),\n lte: lte && this._encode(lte, true)\n }\n\n if (this.prefix !== -1) {\n if (!gt && !gte) range.gte = encodeUint(this.prefix)\n if (!lt && !lte) range.lt = encodeUint(this.prefix + 1)\n }\n\n return range\n }\n}\n\nfunction encodeUint (n) {\n const state = { start: 0, end: 0, buffer: null }\n UINT.preencode(state, n)\n state.buffer = b4a.allocUnsafe(state.end)\n UINT.encode(state, n)\n return state.buffer\n}\n\nfunction encodeUint32 (state, n) {\n state.buffer[state.start++] = n >>> 24\n state.buffer[state.start++] = n >>> 16\n state.buffer[state.start++] = n >>> 8\n state.buffer[state.start++] = n\n}\n\nfunction decodeUint32 (state, n) {\n if (state.end - state.start < 4) throw new Error('Out of bounds')\n return (\n state.buffer[state.start++] * 0x1000000 +\n state.buffer[state.start++] * 0x10000 +\n state.buffer[state.start++] * 0x100 +\n state.buffer[state.start++]\n )\n}\n{\n \"name\": \"index-encoder\",\n \"version\": \"3.4.0\",\n \"description\": \"Encode multiple values into sorted keys\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.6.4\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.2.1\",\n \"hyperbee\": \"^2.10.5\",\n \"hypercore\": \"^10.9.2\",\n \"random-access-memory\": \"^6.2.0\",\n \"standard\": \"^17.0.0\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/index-encoder.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/index-encoder/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/index-encoder\"\n}\nconst b4a = require('b4a')\n\nmodule.exports = function isOptions (opts) {\n return typeof opts === 'object' && opts && !b4a.isBuffer(opts)\n}\n{\n \"name\": \"is-options\",\n \"version\": \"1.0.2\",\n \"description\": \"Easily check if input is an options map\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.1.1\"\n },\n \"devDependencies\": {\n \"standard\": \"^11.0.1\",\n \"tape\": \"^4.9.0\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/is-options.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/is-options/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/is-options\"\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}\nconst fs = require('fs')\nconst fsp = require('fs/promises')\nconst path = require('path')\nconst b4a = require('b4a')\nconst unixPathResolve = require('unix-path-resolve')\nconst { FileReadStream, FileWriteStream } = require('./streams.js')\nconst mutexify = require('mutexify/promise')\nconst MirrorDrive = require('mirror-drive')\n\nmodule.exports = class Localdrive {\n constructor (root, opts = {}) {\n this.root = path.resolve(root)\n this.metadata = handleMetadataHooks(opts.metadata) || {}\n this.supportsMetadata = !!opts.metadata\n\n this._followLinks = !!opts.followLinks\n this._followExternalLinks = !!opts.followExternalLinks\n this._lock = mutexify()\n this._atomics = opts.atomic ? new Set() : null\n }\n\n async ready () { /* No-op, compatibility */ }\n async close () { /* No-op, compatibility */ }\n async flush () { /* No-op, compatibility */ }\n\n batch () {\n return this\n }\n\n checkout () {\n return this\n }\n\n toPath (key) {\n const keyname = unixPathResolve('/', key)\n const filename = path.join(this.root, keyname)\n return filename\n }\n\n async entry (name, opts) {\n if (!opts || !opts.follow) return this._entry(name)\n\n for (let i = 0; i < 16; i++) {\n const node = await this._entry(name)\n if (!node || !node.value.linkname) return node\n\n name = unixPathResolve(node.key, node.value.linkname)\n }\n\n throw new Error('Recursive symlink')\n }\n\n async _resolve (filename) {\n if (this._followLinks) {\n const st = await stat(filename)\n return { st, filename }\n }\n\n if (!this._followExternalLinks) {\n const st = await lstat(filename)\n return { st, filename }\n }\n\n // 256 is the max recursion...\n for (let i = 0; i < 256; i++) {\n const st = await lstat(filename)\n\n if (!st || !st.isSymbolicLink()) return { st, filename }\n\n const link = await fsp.readlink(filename)\n const resolved = path.resolve(path.dirname(filename), link)\n\n // not external\n if (resolved.startsWith(this.root)) return { st, filename }\n\n filename = resolved\n }\n\n // too much recursion, bail\n throw new Error('Reached symlink recursion limit')\n }\n\n async _entry (key) {\n if (typeof key === 'object') key = key.key\n\n const keyname = unixPathResolve('/', key)\n const { st, filename } = await this._resolve(path.join(this.root, keyname))\n\n if (!st || st.isDirectory()) {\n return null\n }\n\n const entry = {\n key: keyname,\n value: {\n executable: false,\n linkname: null,\n blob: null,\n metadata: null\n },\n mtime: st.mtimeMs\n }\n\n if (st.isSymbolicLink()) {\n let link = await fsp.readlink(filename)\n if (link.startsWith(this.root)) link = link.slice(this.root.length)\n entry.value.linkname = link.replace(/\\\\/g, '/')\n return entry\n }\n\n entry.value.executable = isExecutable(st.mode)\n if (this.metadata.get) entry.value.metadata = await this.metadata.get(keyname)\n\n if (st.isFile()) {\n const blockLength = st.blocks || Math.ceil(st.size / st.blksize) * 8\n entry.value.blob = { byteOffset: 0, blockOffset: 0, blockLength, byteLength: st.size }\n return entry\n }\n\n return null\n }\n\n async get (key, opts) {\n const entry = await this.entry(key, opts)\n if (!entry || !entry.value.blob) return null\n\n const rs = this.createReadStream(key)\n const chunks = []\n for await (const chunk of rs) {\n chunks.push(chunk)\n }\n return b4a.concat(chunks)\n }\n\n put (key, buffer, opts) {\n return new Promise((resolve, reject) => {\n const ws = this.createWriteStream(key, opts)\n let error = null\n ws.on('error', (err) => {\n error = err\n })\n ws.on('close', () => {\n if (error) reject(error)\n else resolve()\n })\n ws.end(buffer)\n })\n }\n\n async del (key) {\n const keyname = unixPathResolve('/', key)\n const filename = path.join(this.root, keyname)\n\n try {\n await fsp.unlink(filename)\n } catch (error) {\n if (error.code === 'ENOENT') return\n throw error\n }\n\n const dir = path.dirname(filename)\n\n if (dir.startsWith(this.root)) {\n const release = await this._lock()\n try {\n await gcEmptyFolders(this.root, path.dirname(filename))\n } finally {\n release()\n }\n }\n\n if (this.metadata.del) await this.metadata.del(keyname)\n }\n\n async symlink (key, linkname) {\n const entry = await this.entry(key)\n if (entry) await this.del(key)\n\n const pointer = this.toPath(key)\n\n const release = await this._lock()\n try {\n await fsp.mkdir(path.dirname(pointer), { recursive: true })\n\n const target = linkname.startsWith('/')\n ? this.toPath(linkname)\n : linkname.replace(/\\//g, path.sep)\n\n const st = await lstat(target)\n const type = st && st.isDirectory() ? 'junction' : null\n\n await fsp.symlink(target, pointer, type)\n } finally {\n release()\n }\n }\n\n compare (a, b) {\n const diff = a.mtime - b.mtime\n return diff > 0 ? 1 : (diff < 0 ? -1 : 0)\n }\n\n async * list (folder, opts = {}) {\n if (typeof folder === 'object') {\n opts = folder\n folder = undefined\n }\n\n const ignore = opts.ignore ? toIgnoreFunction(opts.ignore) : null\n const keyname = unixPathResolve('/', folder)\n const fulldir = path.join(this.root, keyname)\n const follow = this._followLinks || this._followExternalLinks\n\n const iterator = await opendir(fulldir)\n\n if (!iterator) return\n\n for await (const dirent of iterator) {\n const key = unixPathResolve(keyname, dirent.name)\n\n if (ignore && ignore(key)) continue\n\n let isDirectory = dirent.isDirectory()\n\n if (dirent.isSymbolicLink() && follow) {\n const { st } = await this._resolve(path.join(fulldir, dirent.name))\n if (st && st.isDirectory()) isDirectory = true\n }\n\n if (isDirectory) {\n yield * this.list(key, opts)\n continue\n }\n\n const entry = await this.entry(key)\n if (entry) yield entry\n }\n }\n\n async * readdir (folder) {\n const keyname = unixPathResolve('/', folder)\n const fulldir = path.join(this.root, keyname)\n const follow = this._followLinks || this._followExternalLinks\n\n const iterator = await readdir(fulldir)\n\n if (!iterator) return\n\n for await (const dirent of iterator) {\n const key = unixPathResolve(keyname, dirent.name)\n\n let suffix = key.slice(keyname.length)\n const i = suffix.indexOf('/')\n if (i > -1) suffix = suffix.slice(i + 1)\n\n let isDirectory = dirent.isDirectory()\n\n if (dirent.isSymbolicLink() && follow) {\n const { st } = await this._resolve(path.join(fulldir, dirent.name))\n if (st && st.isDirectory()) isDirectory = true\n }\n\n if (isDirectory) {\n if (!(await isEmptyDirectory(this, key))) {\n yield suffix\n }\n continue\n }\n\n const entry = await this.entry(key)\n if (entry) yield suffix\n }\n }\n\n async exists (name) {\n return await this.entry(name) !== null\n }\n\n mirror (out, opts) {\n return new MirrorDrive(this, out, opts)\n }\n\n createReadStream (key, opts) {\n if (typeof key === 'object') key = key.key\n\n const filename = this.toPath(key)\n return new FileReadStream(filename, opts)\n }\n\n createWriteStream (key, opts) {\n const keyname = unixPathResolve('/', key)\n const filename = path.join(this.root, keyname)\n\n return new FileWriteStream(filename, keyname, this, opts)\n }\n\n _alloc (filename) {\n if (!this._atomics) return filename\n let c = 0\n while (this._atomics.has(filename + '.' + c + '.localdrive.tmp')) c++\n filename += '.' + c + '.localdrive.tmp'\n this._atomics.add(filename)\n return filename\n }\n\n _free (atomicFilename) {\n this._atomics.delete(atomicFilename)\n }\n}\n\nfunction handleMetadataHooks (metadata) {\n if (metadata instanceof Map) {\n return {\n get: (key) => metadata.has(key) ? metadata.get(key) : null,\n put: (key, value) => metadata.set(key, value),\n del: (key) => metadata.delete(key)\n }\n }\n\n return metadata\n}\n\nfunction isExecutable (mode) {\n return !!(mode & fs.constants.S_IXUSR)\n}\n\nfunction toIgnoreFunction (ignore) {\n if (typeof ignore === 'function') {\n return ignore\n }\n const all = [].concat(ignore).map(e => unixPathResolve('/', e))\n return key => all.includes(key)\n}\n\nasync function lstat (filename) {\n try {\n return await fsp.lstat(filename)\n } catch {\n return null\n }\n}\n\nasync function stat (filename) {\n try {\n return await fsp.stat(filename)\n } catch {\n return null\n }\n}\n\nasync function opendir (dir) {\n try {\n return await fsp.opendir(dir)\n } catch {\n return null\n }\n}\n\nasync function readdir (dir) {\n try {\n return await fsp.readdir(dir, { withFileTypes: true })\n } catch {\n return null\n }\n}\n\nasync function gcEmptyFolders (root, dir) {\n try {\n while (dir !== root) {\n await fsp.rmdir(dir)\n dir = path.dirname(dir)\n }\n } catch {\n // silent error\n }\n}\n\nasync function isEmptyDirectory (drive, key) {\n for await (const entry of drive.list(key)) { // eslint-disable-line\n return false\n }\n return true\n}\n{\n \"name\": \"localdrive\",\n \"version\": \"2.2.0\",\n \"description\": \"File system interoperable with Hyperdrive\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"streams.js\"\n ],\n \"imports\": {\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"fs/*\": {\n \"bare\": \"bare-fs/*\",\n \"default\": \"fs/*\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"scripts\": {\n \"test\": \"standard && brittle test/*.js && brittle test/*.js --relative-tmp-dir\",\n \"lint\": \"standard\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/localdrive.git\"\n },\n \"author\": \"Lucas Barrena (@LuKks)\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/localdrive/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/localdrive\",\n \"dependencies\": {\n \"b4a\": \"^1.6.1\",\n \"bare-fs\": \"^4.0.1\",\n \"bare-path\": \"^3.0.0\",\n \"mirror-drive\": \"^1.2.0\",\n \"mutexify\": \"^1.4.0\",\n \"streamx\": \"^2.12.5\",\n \"unix-path-resolve\": \"^1.0.2\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.0\",\n \"standard\": \"^17.0.0\"\n }\n}\nconst { Readable, Writable } = require('streamx')\nconst fs = require('fs')\nconst fsp = require('fs/promises')\nconst path = require('path')\nconst b4a = require('b4a')\n\nclass FileWriteStream extends Writable {\n constructor (filename, key, drive, opts = {}) {\n super({ map })\n\n this.filename = filename\n this.atomicFilename = this.filename\n this.key = key\n this.drive = drive\n this.executable = !!opts.executable\n this.metadata = opts.metadata || null\n this.fd = 0\n }\n\n _open (cb) {\n this._openp().then(cb, cb)\n }\n\n _final (cb) {\n this._finalp().then(cb, cb)\n }\n\n _destroy (cb) {\n this._destroyp().then(cb, cb)\n }\n\n async _openp () {\n this.atomicFilename = this.drive._alloc(this.filename)\n\n const release = await this.drive._lock()\n const mode = this.executable ? 0o744 : 0o644\n\n try {\n await fsp.mkdir(path.dirname(this.filename), { recursive: true })\n this.fd = await openFilePromise(this.atomicFilename, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_APPEND, mode)\n } finally {\n release()\n }\n\n const st = await fstatPromise(this.fd)\n if (this.executable !== !!(st.mode & fs.constants.S_IXUSR)) {\n await fchmodPromise(this.fd, mode)\n }\n }\n\n _writev (datas, cb) {\n fs.writev(this.fd, datas, cb)\n }\n\n async _destroyp (cb) {\n if (this.fd) await closeFilePromise(this.fd)\n\n if (this.atomicFilename !== this.filename) {\n await unlinkSafe(this.atomicFilename)\n this._free()\n }\n }\n\n async _finalp () {\n if (this.metadata === null) {\n if (this.drive.metadata.del) {\n await this.drive.metadata.del(this.key)\n }\n } else if (this.drive.metadata.put) {\n await this.drive.metadata.put(this.key, this.metadata)\n }\n\n const fd = this.fd\n this.fd = 0\n await closeFilePromise(fd)\n\n if (this.atomicFilename !== this.filename) {\n await renameFilePromise(this.atomicFilename, this.filename)\n this._free()\n }\n }\n\n _free () {\n if (this.atomicFilename === this.filename) return\n this.drive._free(this.atomicFilename)\n this.atomicFilename = this.filename\n }\n}\n\nclass FileReadStream extends Readable {\n constructor (filename, opts = {}) {\n super()\n\n this.filename = filename\n this.fd = 0\n\n this._offset = opts.start || 0\n this._missing = 0\n\n if (opts.length) this._missing = opts.length\n else if (typeof opts.end === 'number') this._missing = opts.end - this._offset + 1\n else this._missing = -1\n }\n\n _open (cb) {\n fs.open(this.filename, fs.constants.O_RDONLY, (err, fd) => {\n if (err) return cb(err)\n\n const onerror = (err) => fs.close(fd, () => cb(err))\n\n fs.fstat(fd, (err, st) => {\n if (err) return onerror(err)\n if (!st.isFile()) return onerror(new Error(this.filename + ' is not a file'))\n\n this.fd = fd\n if (this._missing === -1) this._missing = st.size\n\n if (st.size < this._offset) {\n this._offset = st.size\n this._missing = 0\n return cb(null)\n }\n if (st.size < this._offset + this._missing) {\n this._missing = st.size - this._offset\n return cb(null)\n }\n\n cb(null)\n })\n })\n }\n\n _read (cb) {\n if (!this._missing) {\n this.push(null)\n return cb(null)\n }\n\n const data = b4a.allocUnsafe(Math.min(this._missing, 65536))\n\n fs.read(this.fd, data, 0, data.byteLength, this._offset, (err, read) => {\n if (err) return cb(err)\n\n if (!read) {\n this.push(null)\n return cb(null)\n }\n\n if (this._missing < read) read = this._missing\n this.push(data.subarray(0, read))\n this._missing -= read\n this._offset += read\n if (!this._missing) this.push(null)\n\n cb(null)\n })\n }\n\n _destroy (cb) {\n if (!this.fd) return cb(null)\n fs.close(this.fd, () => cb(null))\n }\n}\n\nmodule.exports = { FileWriteStream, FileReadStream }\n\nfunction map (s) {\n return typeof s === 'string' ? b4a.from(s) : s\n}\n\nfunction openFilePromise (filename, flags, mode) {\n return new Promise((resolve, reject) => {\n fs.open(filename, flags, mode, function (error, fd) {\n if (error) reject(error)\n else resolve(fd)\n })\n })\n}\n\nfunction fstatPromise (fd) {\n return new Promise((resolve, reject) => {\n fs.fstat(fd, function (error, stats) {\n if (error) reject(error)\n else resolve(stats)\n })\n })\n}\n\nfunction fchmodPromise (fd, mode) {\n return new Promise((resolve, reject) => {\n fs.fchmod(fd, mode, function (error) {\n if (error) reject(error)\n else resolve()\n })\n })\n}\n\nfunction closeFilePromise (fd) {\n return new Promise((resolve, reject) => {\n fs.close(fd, function (error) {\n if (error) reject(error)\n else resolve()\n })\n })\n}\n\nfunction renameFilePromise (oldPath, newPath) {\n return new Promise((resolve, reject) => {\n fs.rename(oldPath, newPath, function (err) {\n if (err) reject(err)\n else resolve()\n })\n })\n}\n\nasync function unlinkSafe (filename) {\n try {\n await fsp.unlink(filename)\n } catch (err) {\n if (err.code !== 'ENOENT') throw err\n }\n}\nconst EventEmitter = require('events')\nconst sameData = require('same-data')\nconst unixPathResolve = require('unix-path-resolve')\nconst streamEquals = require('binary-stream-equals')\nconst speedometer = require('speedometer')\nconst { pipelinePromise, isStream } = require('streamx')\n\nconst SAME = 0\nconst DIFF = 1\nconst DIFF_META = 2\n\nclass Monitor extends EventEmitter {\n constructor(mirror, { interval = 250 } = {}) {\n super()\n\n this.mirror = mirror\n this.interval = setInterval(this.update.bind(this), interval)\n this.stats = null\n this.index = mirror.monitors.push(this) - 1\n\n this.update() // populate latest stats\n }\n\n get preloaded() {\n return this.mirror.preloaded\n }\n\n get destroyed() {\n return this.index === -1\n }\n\n update() {\n if (this.index === -1) return\n\n // NOTE: immutable (append-only) data structure\n this.stats = {\n peers: this.mirror.peers.length,\n download: {\n bytes: this.mirror.downloadedBytes,\n blocks: this.mirror.downloadedBlocks,\n speed: this.mirror.downloadSpeed(),\n progress: this.mirror.downloadProgress\n },\n upload: {\n bytes: this.mirror.uploadedBytes,\n blocks: this.mirror.uploadedBlocks,\n speed: this.mirror.uploadSpeed()\n }\n }\n\n this.emit('update', this.stats)\n }\n\n destroy() {\n if (this.index === -1) return\n\n clearInterval(this.interval)\n\n const head = this.mirror.monitors.pop()\n if (head !== this) {\n this.mirror.monitors[this.index] = head\n head.index = this.index\n }\n\n this.index = -1\n this.emit('destroy')\n }\n}\n\nmodule.exports = class MirrorDrive {\n constructor(src, dst, opts = {}) {\n this.src = src\n this.dst = dst\n\n this.prefix = toArray(opts.prefix || '/')\n this.dryRun = !!opts.dryRun\n this.prune = opts.prune !== false\n this.preload = opts.preload !== false && !!src.getBlobs\n this.preloaded = false\n this.includeProgress = !!opts.progress && !!src.getBlobs\n this.includeEquals = !!opts.includeEquals\n this.filter = opts.filter || null\n this.metadataEquals = opts.metadataEquals || null\n this.batch = !!opts.batch\n this.entries = opts.entries || null\n this.transformers = opts.transformers || []\n\n this.count = { files: 0, add: 0, remove: 0, change: 0 }\n this.bytesRemoved = 0\n this.bytesAdded = 0\n this.ignore = opts.ignore ? toIgnoreFunction(opts.ignore) : null\n this.finished = false\n\n this.downloadedBlocks = 0\n this.downloadedBlocksEstimate = 0\n this.downloadedBytes = 0\n this.downloadSpeed = this.includeProgress ? speedometer() : null\n\n this.uploadedBlocks = 0\n this.uploadedBytes = 0\n this.uploadSpeed = this.includeProgress ? speedometer() : null\n\n this.monitors = []\n this.iterator = this._init()\n }\n\n [Symbol.asyncIterator]() {\n return this.iterator\n }\n\n get peers() {\n return this.src.core?.peers || []\n }\n\n get downloadProgress() {\n if (this.finished) return 1\n if (!this.downloadedBlocksEstimate) return 0\n // leave 3% incase our estimatation is wrong - then at least it wont appear done...\n return Math.min(0.99, this.downloadedBlocks / this.downloadedBlocksEstimate)\n }\n\n monitor(opts) {\n this.includeProgress = true\n if (this.downloadSpeed === null) this.downloadSpeed = speedometer()\n if (this.uploadSpeed === null) this.uploadSpeed = speedometer()\n return new Monitor(this, opts)\n }\n\n async done() {\n while (true) {\n const { done } = await this.iterator.next()\n if (done) break\n }\n }\n\n _onupload(index, byteLength) {\n this.uploadedBlocks++\n this.uploadedBytes += byteLength\n this.uploadSpeed(byteLength)\n }\n\n _ondownload(index, byteLength) {\n this.downloadedBlocks++\n this.downloadedBytes += byteLength\n this.downloadSpeed(byteLength)\n }\n\n async _flushPreload(entries) {\n const ranges = []\n const blobs = await this.src.getBlobs()\n\n for (const entry of entries) {\n const blob = entry.value.blob\n if (!blob) continue\n const dl = blobs.core.download({ start: blob.blockOffset, length: blob.blockLength })\n await dl.ready()\n ranges.push(dl)\n }\n\n this.downloadedBlocksEstimate = this.downloadedBlocks\n for (const dl of ranges) {\n if (!dl.request.context) continue\n this.downloadedBlocksEstimate += dl.request.context.end - dl.request.context.start\n }\n this.preloaded = true\n for (const m of this.monitors) {\n m.emit('preloaded')\n }\n\n for (const dl of ranges) {\n await dl.done()\n }\n }\n\n async *_init() {\n try {\n for await (const out of this._mirror()) yield out\n } finally {\n while (this.monitors.length) {\n this.monitors[this.monitors.length - 1].destroy()\n }\n }\n }\n\n async *_mirror() {\n await this.src.ready()\n await this.dst.ready()\n\n if (this.dst.core && !this.dst.core.writable) throw new Error('Destination must be writable')\n\n const blobs = this.includeProgress ? await this.src.getBlobs() : null\n const onupload = this._onupload.bind(this)\n const ondownload = this._ondownload.bind(this)\n\n if (blobs) {\n blobs.core.on('upload', onupload)\n blobs.core.on('download', ondownload)\n }\n\n const dst = this.batch ? this.dst.batch() : this.dst\n\n if (this.prune) {\n for await (const [key, dstEntry, srcEntry] of this._list(this.dst, this.src, null)) {\n if (srcEntry) continue\n\n this.count.remove++\n this.bytesRemoved += blobLength(dstEntry)\n yield { op: 'remove', key, bytesRemoved: blobLength(dstEntry), bytesAdded: 0 }\n\n if (!this.dryRun) await dst.del(key)\n }\n }\n\n if (this.preload) {\n const entries = []\n\n for await (const [, srcEntry] of this._list(this.src, null, this.filter)) {\n entries.push(srcEntry)\n }\n\n // flush in bg\n this._flushPreload(entries).catch(noop)\n }\n\n for await (const [key, srcEntry, dstEntry] of this._list(this.src, dst, this.filter)) {\n if (!srcEntry) continue // Due entries option, src entry might not exist probably because it was pruned\n\n this.count.files++\n\n // If transformers are provided, we can't know if same before running them\n const hasTransformers = this.transformers && this.transformers.length > 0\n\n const s = hasTransformers === false ? (await same(this, srcEntry, dstEntry)) : DIFF\n\n if (s === SAME) {\n if (this.includeEquals) {\n yield { op: 'equal', key, bytesRemoved: 0, bytesAdded: 0 }\n }\n continue\n }\n\n const onlyMetadata = s === DIFF_META && !!dst.putEntry\n\n if (dstEntry) {\n const removed = onlyMetadata ? 0 : blobLength(dstEntry)\n const added = onlyMetadata ? 0 : blobLength(srcEntry)\n this.count.change++\n this.bytesRemoved += removed\n this.bytesAdded += added\n yield {\n op: 'change',\n key,\n bytesRemoved: removed,\n bytesAdded: added\n }\n } else {\n this.count.add++\n this.bytesAdded += blobLength(srcEntry)\n yield { op: 'add', key, bytesRemoved: 0, bytesAdded: blobLength(srcEntry) }\n }\n\n if (this.dryRun) {\n continue\n }\n\n const transformers = []\n\n for (const transformer of this.transformers) {\n if (typeof transformer !== 'function') throw new Error('transformer must be a function')\n\n const stream = transformer(key)\n\n if (stream === null) continue\n if (!isStream(stream)) throw new Error('transformer must return a stream')\n\n transformers.push(stream)\n }\n\n if (srcEntry.value.linkname) {\n await dst.symlink(key, srcEntry.value.linkname)\n } else if (!onlyMetadata) {\n await pipelinePromise(\n this.src.createReadStream(srcEntry),\n ...transformers,\n dst.createWriteStream(key, {\n executable: srcEntry.value.executable,\n metadata: srcEntry.value.metadata\n })\n )\n } else {\n await dst.putEntry(key, {\n executable: srcEntry.value.executable,\n linkname: srcEntry.value.linkname,\n blob: dstEntry.value.blob,\n metadata: srcEntry.value.metadata\n })\n }\n }\n\n if (this.batch) await dst.flush()\n\n if (blobs) {\n blobs.core.off('upload', onupload)\n blobs.core.off('download', ondownload)\n }\n\n this.finished = true\n }\n\n async *_list(a, b, filter) {\n const lists = []\n\n for (const prefix of this.prefix) {\n if (this.entries) {\n lists.push(this.entries)\n } else {\n const stream = a.list(prefix, { ignore: this.ignore })\n if (stream.on) {\n stream.on('error', noop)\n stream.resume()\n stream.pause()\n }\n lists.push(stream)\n }\n }\n\n for (let i = 0; i < this.prefix.length; i++) {\n const prefix = this.prefix[i]\n const list = lists[i]\n\n for await (const entry of list) {\n const key = typeof entry === 'object' ? entry.key : entry\n\n if (filter && !filter(key)) continue\n\n const entryA = await a.entry(entry)\n const entryB = b ? await b.entry(key) : null\n\n yield [key, entryA, entryB]\n }\n\n if (prefix !== '/' && (!filter || filter(prefix))) {\n const entryA = await a.entry(prefix)\n const entryB = b ? await b.entry(prefix) : null\n\n if (!entryA && !entryB) continue\n\n yield [prefix, entryA, entryB]\n }\n }\n }\n}\n\nfunction blobLength(entry) {\n return entry.value.blob ? entry.value.blob.byteLength : 0\n}\n\nasync function same(m, srcEntry, dstEntry) {\n if (!dstEntry) return DIFF\n\n if (srcEntry.value.linkname || dstEntry.value.linkname) {\n return srcEntry.value.linkname === dstEntry.value.linkname ? SAME : DIFF\n }\n\n if (!sizeEquals(srcEntry, dstEntry)) return DIFF\n\n const eq = await streamEquals(m.src.createReadStream(srcEntry), m.dst.createReadStream(dstEntry))\n const diff = eq ? DIFF_META : DIFF\n\n if (srcEntry.value.executable !== dstEntry.value.executable) return diff\n if (!metadataEquals(m, srcEntry, dstEntry)) return diff\n\n return eq ? SAME : DIFF\n}\n\nfunction sizeEquals(srcEntry, dstEntry) {\n const srcBlob = srcEntry.value.blob\n const dstBlob = dstEntry.value.blob\n\n if (!srcBlob && !dstBlob) return true\n if (!srcBlob || !dstBlob) return false\n\n return srcBlob.byteLength === dstBlob.byteLength\n}\n\nfunction metadataEquals(m, srcEntry, dstEntry) {\n if (!m.src.supportsMetadata || !m.dst.supportsMetadata) return true\n\n const srcMetadata = srcEntry.value.metadata\n const dstMetadata = dstEntry.value.metadata\n\n if (m.metadataEquals) {\n return m.metadataEquals(srcMetadata, dstMetadata)\n }\n\n const noMetadata = !srcMetadata && !dstMetadata\n const identicalMetadata = !!(srcMetadata && dstMetadata && sameData(srcMetadata, dstMetadata))\n\n return noMetadata || identicalMetadata\n}\n\nfunction toIgnoreFunction(ignore) {\n if (typeof ignore === 'function') return ignore\n\n const all = [].concat(ignore).map((e) => unixPathResolve('/', e))\n return (key) => all.some((path) => path === key || key.startsWith(path + '/'))\n}\n\nfunction toArray(prefix) {\n return Array.isArray(prefix) ? prefix : [prefix]\n}\n\nfunction noop() {}\n{\n \"name\": \"mirror-drive\",\n \"version\": \"1.12.0\",\n \"description\": \"Mirror a hyperdrive or localdrive into another one\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"lib/**.js\"\n ],\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\"\n },\n \"os\": {\n \"bare\": \"bare-os\",\n \"default\": \"os\"\n },\n \"stream\": {\n \"bare\": \"bare-stream\",\n \"default\": \"stream\"\n }\n },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:node\": \"brittle-node test/*.js\",\n \"test:bare\": \"brittle-bare test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/mirror-drive.git\"\n },\n \"author\": \"Lucas Barrena (@LuKks)\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/mirror-drive/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/mirror-drive\",\n \"devDependencies\": {\n \"b4a\": \"^1.6.0\",\n \"bare-os\": \"^3.0.1\",\n \"bare-stream\": \"^2.6.5\",\n \"brittle\": \"^3.1.0\",\n \"corestore\": \"^6.0.6\",\n \"hyperdrive\": \"^13.0.0\",\n \"localdrive\": \"^2.2.0\",\n \"lunte\": \"^1.0.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n },\n \"dependencies\": {\n \"bare-events\": \"^2.8.2\",\n \"binary-stream-equals\": \"^1.0.0\",\n \"same-data\": \"^1.0.0\",\n \"speedometer\": \"^1.1.0\",\n \"streamx\": \"^2.22.1\",\n \"unix-path-resolve\": \"^1.0.2\"\n }\n}\nvar queueTick = require('queue-tick')\n\nvar mutexify = function () {\n var queue = []\n var used = null\n\n var call = function () {\n used(release)\n }\n\n var acquire = function (fn) {\n if (used) return queue.push(fn)\n used = fn\n acquire.locked = true\n queueTick(call)\n return 0\n }\n\n acquire.locked = false\n\n var release = function (fn, err, value) {\n used = null\n acquire.locked = false\n if (queue.length) acquire(queue.shift())\n if (fn) fn(err, value)\n }\n\n return acquire\n}\n\nmodule.exports = mutexify\n{\n \"name\": \"mutexify\",\n \"version\": \"1.4.0\",\n \"description\": \"mutex lock for javascript\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"queue-tick\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"standard\": \"^14.3.3\",\n \"tape\": \"^3.0.2\"\n },\n \"scripts\": {\n \"test\": \"tape test.js\",\n \"posttest\": \"npm run lint\",\n \"lint\": \"standard\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/mutexify.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/mutexify/issues\"\n },\n \"keywords\": [\n \"mutex\",\n \"lock\"\n ],\n \"homepage\": \"https://github.com/mafintosh/mutexify\"\n}\nvar mutexify = require('.')\n\nvar mutexifyPromise = function () {\n var lock = mutexify()\n\n var acquire = function () {\n return new Promise(lock)\n }\n\n Object.defineProperty(acquire, 'locked', {\n get: function () { return lock.locked },\n enumerable: true\n })\n\n return acquire\n}\n\nmodule.exports = mutexifyPromise\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 <github@tixz.dk>\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/emilbayes/nanoassert/issues\"\n },\n \"homepage\": \"https://github.com/emilbayes/nanoassert#readme\"\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}\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}\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 XK: [\n PRESHARE_RS,\n [TOK_E, TOK_ES],\n [TOK_E, TOK_EE],\n [TOK_S, 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.2.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}\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}\n'use strict'\nconst hypercoreid = require('hypercore-id-encoding')\n\nexports.ALIASES = {\n pear: hypercoreid.decode(\n 'pzcjqmpoo6szkoc4bpkw65ib9ctnrq7b6mneeinbhbheihaq6p6o'\n ),\n keet: hypercoreid.decode(\n 'oeeoz3w6fjjt7bym3ndpa6hhicm8f8naxyk11z4iypeoupn6jzpo'\n ),\n runtime: hypercoreid.decode(\n 'qz5ihrqfmwtp4q36hnjaht1edur6c7w11dbb7ym9jqaex71wogso'\n ),\n doctor: hypercoreid.decode(\n 'fs1xuyzx6c9mu6zu6t5ubhkcbzz913h814te9ay9zzbc9hzf15fo'\n ),\n templates: hypercoreid.decode(\n 'z7n5sgqipwaejgonu6hh4baw95imy8dicj8678tej98itbs9tcgy'\n ),\n electron: hypercoreid.decode(\n 'cktxzetiwt6un3ado5kgqedge6ya4nfazjckzq76zcapefwxakdy'\n ),\n pass: hypercoreid.decode(\n 'tywsat7gz8m65ejx4zjn3773pbdc4j8m66tukis8dgzekraymtzo'\n )\n}\n\nexports.EOLS = {\n keet: [\n hypercoreid.decode('jc38t9nr7fasay4nqfxwfaawywfd3y14krnsitj67ymoubiezqdy')\n ],\n pear: [\n hypercoreid.decode('pqbzjhqyonxprx8hghxexnmctw75mr91ewqw5dxe1zmntfyaddqy')\n ]\n}\n{\n \"name\": \"pear-aliases\",\n \"version\": \"1.0.7\",\n \"main\": \"index.js\",\n \"type\": \"commonjs\",\n \"description\": \"pear://<alias> list\",\n \"license\": \"Apache-2.0\",\n \"files\": [],\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test:gen\": \"brittle -r test/all.js test/*.test.js\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"cov\": \"brittle-bare --coverage test/all.js\",\n \"test:node\": \"brittle-node --coverage test/all.js\",\n \"test:bare\": \"brittle-bare --coverage test/all.js\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\n },\n \"dependencies\": {\n \"hypercore-id-encoding\": \"^1.3.0\"\n }\n}\n'use strict'\nclass PearError extends Error {\n static ERR_INVALID_INPUT = ERR_INVALID_INPUT\n static ERR_INVALID_LINK = ERR_INVALID_LINK\n static ERR_INVALID_APPLING = ERR_INVALID_APPLING\n static ERR_INVALID_APP_NAME = ERR_INVALID_APP_NAME\n static ERR_INVALID_APP_STORAGE = ERR_INVALID_APP_STORAGE\n static ERR_INVALID_PROJECT_DIR = ERR_INVALID_PROJECT_DIR\n static ERR_INVALID_GC_RESOURCE = ERR_INVALID_GC_RESOURCE\n static ERR_INVALID_CONFIG = ERR_INVALID_CONFIG\n static ERR_INVALID_TEMPLATE = ERR_INVALID_TEMPLATE\n static ERR_PERMISSION_REQUIRED = ERR_PERMISSION_REQUIRED\n static ERR_INTERNAL_ERROR = ERR_INTERNAL_ERROR\n static ERR_UNSTAGED = ERR_UNSTAGED\n static ERR_NOT_FOUND = ERR_NOT_FOUND\n static ERR_DIR_NONEMPTY = ERR_DIR_NONEMPTY\n static ERR_OPERATION_FAILED = ERR_OPERATION_FAILED\n static ERR_SECRET_NOT_FOUND = ERR_SECRET_NOT_FOUND\n static ERR_CONNECTION = ERR_CONNECTION\n static ERR_INVALID_MANIFEST = ERR_INVALID_MANIFEST\n static ERR_ASSERTION = ERR_ASSERTION\n static ERR_UNKNOWN = ERR_UNKNOWN\n static ERR_LEGACY = ERR_LEGACY\n static known = known\n constructor (msg, fn = PearError, info = null, stackless = false) {\n super(msg)\n this.code = fn.name\n this.name = fn.name\n if (this.info !== null) this.info = info\n if (stackless) this.stack = this.message\n else if (Error.captureStackTrace) Error.captureStackTrace(this, fn)\n }\n}\n\nfunction known (prefix = 'ERR_', ...prefixes) {\n return [...Object.getOwnPropertyNames(PearError).filter((name) => name.startsWith(prefix)), ...prefixes.flatMap((prefix) => known(prefix))]\n}\n\nfunction ERR_INVALID_INPUT (msg, info = null) {\n return new PearError(msg, ERR_INVALID_INPUT, info)\n}\n\nfunction ERR_INVALID_LINK (msg, info = null) {\n return new PearError(msg, ERR_INVALID_LINK, info)\n}\n\nfunction ERR_INVALID_APPLING (msg, info = null) {\n return new PearError(msg, ERR_INVALID_APPLING, info)\n}\n\nfunction ERR_INVALID_APP_NAME (msg, info = null) {\n return new PearError(msg, ERR_INVALID_APP_NAME, info)\n}\n\nfunction ERR_INVALID_APP_STORAGE (msg, info = null) {\n return new PearError(msg, ERR_INVALID_APP_STORAGE, info)\n}\n\nfunction ERR_INVALID_PROJECT_DIR (msg, info = null) {\n return new PearError(msg, ERR_INVALID_PROJECT_DIR, info)\n}\n\nfunction ERR_INVALID_GC_RESOURCE (msg, info = null) {\n return new PearError(msg, ERR_INVALID_GC_RESOURCE, info)\n}\n\nfunction ERR_INVALID_CONFIG (msg, info = null) {\n return new PearError(msg, ERR_INVALID_CONFIG, info)\n}\n\nfunction ERR_INVALID_TEMPLATE (msg, info = null) {\n return new PearError(msg, ERR_INVALID_TEMPLATE, info)\n}\n\nfunction ERR_PERMISSION_REQUIRED (msg, info = {}) {\n return new PearError(msg, ERR_PERMISSION_REQUIRED, info)\n}\n\nfunction ERR_SECRET_NOT_FOUND (msg, info = null) {\n return new PearError(msg, ERR_SECRET_NOT_FOUND, info)\n}\n\nfunction ERR_CONNECTION (msg, info = null) {\n return new PearError(msg, ERR_CONNECTION, info)\n}\n\nfunction ERR_INVALID_MANIFEST (msg, info = null) {\n return new PearError(msg, ERR_INVALID_MANIFEST, info)\n}\n\nfunction ERR_INTERNAL_ERROR (msg, info = null) {\n return new PearError(msg, ERR_INTERNAL_ERROR, info)\n}\n\nfunction ERR_UNSTAGED (msg, info = null) {\n return new PearError(msg, ERR_UNSTAGED, info)\n}\n\nfunction ERR_NOT_FOUND (msg, info = null) {\n return new PearError(msg, ERR_NOT_FOUND, info)\n}\n\nfunction ERR_DIR_NONEMPTY (msg, info = null) {\n return new PearError(msg, ERR_DIR_NONEMPTY, info)\n}\n\nfunction ERR_OPERATION_FAILED (msg, info = {}) {\n return new PearError(msg, ERR_OPERATION_FAILED, info)\n}\n\nfunction ERR_ASSERTION (msg, info = null) {\n return new PearError(msg, ERR_ASSERTION, info)\n}\n\nfunction ERR_UNKNOWN (msg, info = null) {\n return new PearError(msg, ERR_UNKNOWN, info)\n}\n\nfunction ERR_LEGACY (msg, info = null) {\n return new PearError(msg, ERR_LEGACY, info, true)\n}\n\nmodule.exports = PearError\n{\n \"name\": \"pear-errors\",\n \"version\": \"1.0.1\",\n \"main\": \"index.js\",\n \"type\": \"commonjs\",\n \"description\": \"Pear Core Error Types\",\n \"license\": \"Apache-2.0\",\n \"scripts\": {\n \"test:gen\": \"brittle -r test/all.js test/*.test.js\",\n \"test\": \"brittle test/all.js\",\n \"lint\": \"standard\",\n \"cov\": \"brittle-bare --coverage test/all.js\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.0.0\",\n \"standard\": \"^17.1.2\"\n }\n}\n'use strict'\nconst path = require('path')\nconst { ERR_INVALID_LINK } = require('pear-errors')\nconst { ALIASES } = require('pear-aliases')\nconst hid = require('hypercore-id-encoding')\nconst FILE = 'file:'\nconst PEAR = 'pear:'\nconst DOUB = '//'\n\nfunction decode(v, info = {}) {\n try {\n return hid.decode(v)\n } catch (err) {\n if (typeof v === 'string')\n throw ERR_INVALID_LINK('alias not found: \"' + v + '\"', { ...info, err })\n throw err\n }\n}\n\nclass PearLink {\n normalize(link) {\n // if link has link format, separator is always '/' even in Windows\n if (link.startsWith(FILE + DOUB))\n return link.endsWith('/') ? link.slice(0, -1) : link\n else return link.endsWith(path.sep) ? link.slice(0, -1) : link\n }\n\n serialize(o) {\n o = hid.isValid(o) ? { drive: { key: o } } : o\n let { protocol, pathname = '', search = '', hash = '', drive } = o\n if (protocol === FILE) return `${protocol}//${pathname}${search}${hash}`\n if (!protocol && drive) protocol = PEAR\n if (protocol === PEAR) {\n const key = drive.alias || hid.normalize(drive.key)\n const base = [\n drive.fork,\n drive.length,\n key,\n drive.hash && hid.encode(drive.hash)\n ]\n .filter((p) => (p ?? '') + '')\n .join('.')\n return `${protocol}//${base}${pathname}${search}${hash}`\n }\n throw ERR_INVALID_LINK('Unsupported protocol', {\n protocol,\n pathname,\n search,\n hash,\n drive\n })\n }\n\n parse(link) {\n if (!link) throw ERR_INVALID_LINK('No link specified', { link })\n const isPath =\n link.startsWith(PEAR + DOUB) === false &&\n link.startsWith(FILE + DOUB) === false\n const isRelativePath = isPath && link[0] !== '/' && link[1] !== ':'\n const keys = Object.fromEntries(\n Object.entries(ALIASES).map(([k, v]) => [hid.encode(v), k])\n )\n const { protocol, pathname, hostname, search, hash } = isRelativePath\n ? new URL(link, FILE + DOUB + path.resolve('.') + '/')\n : new URL(isPath ? FILE + DOUB + link : link)\n const info = { link, protocol, hostname, pathname, search, hash }\n if (protocol === FILE) {\n // file:///some/path/to/a/file.js\n const startsWithRoot = hostname === ''\n if (!pathname) throw ERR_INVALID_LINK('Path is missing', info)\n if (!startsWithRoot)\n throw ERR_INVALID_LINK('Path needs to start from the root, \"/\"', info)\n return {\n protocol,\n pathname,\n search,\n hash,\n origin: this.normalize(`${protocol}//${hostname}${pathname}`),\n drive: {\n key: null,\n length: null,\n fork: null,\n hash: null\n }\n }\n } else if (protocol === PEAR) {\n const [fork, length, keyOrAlias, apphash] = hostname.split('.')\n const parts = hostname.split('.').length\n\n if (parts === 1) {\n // pear://keyOrAlias[/some/path]\n const key = ALIASES[hostname] || decode(hostname, info)\n const origin = keys[hid.encode(key)]\n ? `${protocol}//${keys[hid.encode(key)]}`\n : `${protocol}//${hostname}`\n const alias = ALIASES[hostname] ? hostname : null\n return {\n protocol,\n pathname,\n search,\n hash,\n origin,\n drive: {\n key,\n length: null,\n fork: null,\n hash: null,\n alias\n }\n }\n }\n\n if (parts === 2) {\n // pear://fork.length[/some/path]\n throw ERR_INVALID_LINK('Incorrect hostname', info)\n }\n\n const alias = ALIASES[keyOrAlias] ? keyOrAlias : null\n const key = ALIASES[keyOrAlias] || decode(keyOrAlias, info)\n const origin = keys[hid.encode(key)]\n ? `${protocol}//${keys[hid.encode(key)]}`\n : `${protocol}//${keyOrAlias}`\n\n if (parts === 3) {\n // pear://fork.length.keyOrAlias[/some/path]\n if (!Number.isInteger(+fork) || !Number.isInteger(+length))\n throw ERR_INVALID_LINK('Incorrect hostname', info)\n return {\n protocol,\n pathname,\n search,\n hash,\n origin,\n drive: {\n key,\n length: Number(length),\n fork: Number(fork),\n hash: null,\n alias\n }\n }\n }\n\n if (parts === 4) {\n // pear://fork.length.keyOrAlias.dhash[/some/path]\n if (!Number.isInteger(+fork) || !Number.isInteger(+length))\n throw ERR_INVALID_LINK('Incorrect hostname', info)\n\n return {\n protocol,\n pathname,\n search,\n hash,\n origin,\n drive: {\n key,\n length: Number(length),\n fork: Number(fork),\n hash: hid.decode(apphash),\n alias\n }\n }\n }\n\n throw ERR_INVALID_LINK('Incorrect hostname', info)\n }\n\n throw ERR_INVALID_LINK('Protocol is not supported', info)\n }\n}\n\nmodule.exports = new PearLink()\n{\n \"name\": \"pear-link\",\n \"version\": \"4.2.1\",\n \"description\": \"Parse Pear Links\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check .\",\n \"test\": \"brittle-bare test/index.test.js\",\n \"test:node\": \"node test/index.test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/pear-link.git\"\n },\n \"files\": [\n \"index.js\"\n ],\n \"imports\": {\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/pear-link/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/pear-link#readme\",\n \"dependencies\": {\n \"bare-path\": \"^3.0.0\",\n \"hypercore-id-encoding\": \"^1.2.0\",\n \"pear-aliases\": \"^1.0.0\",\n \"pear-errors\": \"^1.0.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.4.1\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\",\n \"standard\": \"^17.1.0\",\n \"url-file-url\": \"^1.0.5\",\n \"which-runtime\": \"^1.2.1\"\n }\n}\nvar varint = require('varint')\nvar svarint = require('signed-varint')\nvar b4a = require('b4a')\n\nexports.make = encoder\n\nexports.name = function (enc) {\n var keys = Object.keys(exports)\n for (var i = 0; i < keys.length; i++) {\n if (exports[keys[i]] === enc) return keys[i]\n }\n return null\n}\n\nexports.skip = function (type, buffer, offset) {\n switch (type) {\n case 0:\n varint.decode(buffer, offset)\n return offset + varint.decode.bytes\n\n case 1:\n return offset + 8\n\n case 2:\n var len = varint.decode(buffer, offset)\n return offset + varint.decode.bytes + len\n\n case 3:\n case 4:\n throw new Error('Groups are not supported')\n\n case 5:\n return offset + 4\n }\n\n throw new Error('Unknown wire type: ' + type)\n}\n\nexports.bytes = encoder(2,\n function encode (val, buffer, offset) {\n var oldOffset = offset\n var len = bufferLength(val)\n\n varint.encode(len, buffer, offset)\n offset += varint.encode.bytes\n\n if (b4a.isBuffer(val)) b4a.copy(val, buffer, offset)\n else b4a.write(buffer, val, offset, len)\n offset += len\n\n encode.bytes = offset - oldOffset\n return buffer\n },\n function decode (buffer, offset) {\n var oldOffset = offset\n\n var len = varint.decode(buffer, offset)\n offset += varint.decode.bytes\n\n var val = buffer.subarray(offset, offset + len)\n offset += val.length\n\n decode.bytes = offset - oldOffset\n return val\n },\n function encodingLength (val) {\n var len = bufferLength(val)\n return varint.encodingLength(len) + len\n }\n)\n\nexports.string = encoder(2,\n function encode (val, buffer, offset) {\n var oldOffset = offset\n var len = b4a.byteLength(val)\n\n varint.encode(len, buffer, offset, 'utf-8')\n offset += varint.encode.bytes\n\n b4a.write(buffer, val, offset, len)\n offset += len\n\n encode.bytes = offset - oldOffset\n return buffer\n },\n function decode (buffer, offset) {\n var oldOffset = offset\n\n var len = varint.decode(buffer, offset)\n offset += varint.decode.bytes\n\n var val = b4a.toString(buffer, 'utf-8', offset, offset + len)\n offset += len\n\n decode.bytes = offset - oldOffset\n return val\n },\n function encodingLength (val) {\n var len = b4a.byteLength(val)\n return varint.encodingLength(len) + len\n }\n)\n\nexports.bool = encoder(0,\n function encode (val, buffer, offset) {\n buffer[offset] = val ? 1 : 0\n encode.bytes = 1\n return buffer\n },\n function decode (buffer, offset) {\n var bool = buffer[offset] > 0\n decode.bytes = 1\n return bool\n },\n function encodingLength () {\n return 1\n }\n)\n\nexports.int32 = encoder(0,\n function encode (val, buffer, offset) {\n varint.encode(val < 0 ? val + 4294967296 : val, buffer, offset)\n encode.bytes = varint.encode.bytes\n return buffer\n },\n function decode (buffer, offset) {\n var val = varint.decode(buffer, offset)\n decode.bytes = varint.decode.bytes\n return val > 2147483647 ? val - 4294967296 : val\n },\n function encodingLength (val) {\n return varint.encodingLength(val < 0 ? val + 4294967296 : val)\n }\n)\n\nexports.int64 = encoder(0,\n function encode (val, buffer, offset) {\n if (val < 0) {\n var last = offset + 9\n varint.encode(val * -1, buffer, offset)\n offset += varint.encode.bytes - 1\n buffer[offset] = buffer[offset] | 0x80\n while (offset < last - 1) {\n offset++\n buffer[offset] = 0xff\n }\n buffer[last] = 0x01\n encode.bytes = 10\n } else {\n varint.encode(val, buffer, offset)\n encode.bytes = varint.encode.bytes\n }\n return buffer\n },\n function decode (buffer, offset) {\n var val = varint.decode(buffer, offset)\n if (val >= Math.pow(2, 63)) {\n var limit = 9\n while (buffer[offset + limit - 1] === 0xff) limit--\n limit = limit || 9\n var subset = b4a.allocUnsafe(limit)\n b4a.copy(buffer, subset, 0, offset, offset + limit)\n subset[limit - 1] = subset[limit - 1] & 0x7f\n val = -1 * varint.decode(subset, 0)\n decode.bytes = 10\n } else {\n decode.bytes = varint.decode.bytes\n }\n return val\n },\n function encodingLength (val) {\n return val < 0 ? 10 : varint.encodingLength(val)\n }\n)\n\nexports.sint32 =\nexports.sint64 = encoder(0,\n svarint.encode,\n svarint.decode,\n svarint.encodingLength\n)\n\nexports.uint32 =\nexports.uint64 =\nexports.enum =\nexports.varint = encoder(0,\n varint.encode,\n varint.decode,\n varint.encodingLength\n)\n\n// we cannot represent these in javascript so we just use buffers\nexports.fixed64 =\nexports.sfixed64 = encoder(1,\n function encode (val, buffer, offset) {\n b4a.copy(val, buffer, offset)\n encode.bytes = 8\n return buffer\n },\n function decode (buffer, offset) {\n var val = buffer.subarray(offset, offset + 8)\n decode.bytes = 8\n return val\n },\n function encodingLength () {\n return 8\n }\n)\n\nexports.double = encoder(1,\n function encode (val, buffer, offset) {\n b4a.writeDoubleLE(buffer, val, offset)\n encode.bytes = 8\n return buffer\n },\n function decode (buffer, offset) {\n var val = b4a.readDoubleLE(buffer, offset)\n decode.bytes = 8\n return val\n },\n function encodingLength () {\n return 8\n }\n)\n\nexports.fixed32 = encoder(5,\n function encode (val, buffer, offset) {\n b4a.writeUInt32LE(buffer, val, offset)\n encode.bytes = 4\n return buffer\n },\n function decode (buffer, offset) {\n var val = b4a.readUInt32LE(buffer, offset)\n decode.bytes = 4\n return val\n },\n function encodingLength () {\n return 4\n }\n)\n\nexports.sfixed32 = encoder(5,\n function encode (val, buffer, offset) {\n b4a.writeInt32LE(buffer, val, offset)\n encode.bytes = 4\n return buffer\n },\n function decode (buffer, offset) {\n var val = b4a.readInt32LE(buffer, offset)\n decode.bytes = 4\n return val\n },\n function encodingLength () {\n return 4\n }\n)\n\nexports.float = encoder(5,\n function encode (val, buffer, offset) {\n b4a.writeFloatLE(buffer, val, offset)\n encode.bytes = 4\n return buffer\n },\n function decode (buffer, offset) {\n var val = b4a.readFloatLE(buffer, offset)\n decode.bytes = 4\n return val\n },\n function encodingLength () {\n return 4\n }\n)\n\nfunction encoder (type, encode, decode, encodingLength) {\n encode.bytes = decode.bytes = 0\n\n return {\n type: type,\n encode: encode,\n decode: decode,\n encodingLength: encodingLength\n }\n}\n\nfunction bufferLength (val) {\n return b4a.isBuffer(val) ? val.length : b4a.byteLength(val)\n}\n{\n \"name\": \"protocol-buffers-encodings\",\n \"version\": \"1.2.0\",\n \"description\": \"Base encodings for protocol-buffers\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"b4a\": \"^1.6.0\",\n \"signed-varint\": \"^2.0.1\",\n \"varint\": \"5.0.0\"\n },\n \"devDependencies\": {\n \"standard\": \"^14.3.4\",\n \"tape\": \"^5.0.1\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/protocol-buffers-encodings.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/protocol-buffers-encodings/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/protocol-buffers-encodings\"\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}\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\": \"./queue-microtask.js\",\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}\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)\nrequire.addon = require('require-addon')\n\nmodule.exports = require.addon('.', __filename)\nconst binding = require('./binding')\n\nexports.get = function get(field, bit) {\n const n = field.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n return binding.quickbit_napi_get(toBuffer(field), bit) !== 0\n}\n\nexports.set = function set(field, bit, value = true) {\n const n = field.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n return binding.quickbit_napi_set(toBuffer(field), bit, value ? 1 : 0) !== 0\n}\n\nexports.fill = function fill(\n field,\n value,\n start = 0,\n end = field.byteLength * 8\n) {\n const n = field.byteLength * 8\n\n if (start < 0) start += n\n if (end < 0) end += n\n if (start < 0 || start >= field.byteLength * 8 || start >= end) return field\n\n binding.quickbit_napi_fill(toBuffer(field), value ? 1 : 0, start, end)\n return field\n}\n\nexports.clear = function clear(field, ...chunks) {\n binding.quickbit_napi_clear(toBuffer(field), chunks.map(toBufferChunk))\n}\n\nexports.findFirst = function findFirst(field, value, position = 0) {\n const n = field.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) position = 0\n if (position >= n) return -1\n\n return binding.quickbit_napi_find_first(\n toBuffer(field),\n value ? 1 : 0,\n position\n )\n}\n\nexports.findLast = function findLast(\n field,\n value,\n position = field.byteLength * 8 - 1\n) {\n const n = field.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) return -1\n if (position >= n) position = n - 1\n\n return binding.quickbit_napi_find_last(\n toBuffer(field),\n value ? 1 : 0,\n position\n )\n}\n\nfunction toBuffer(field) {\n if (field.BYTES_PER_ELEMENT === 1) return field\n return new Uint8Array(field.buffer, field.byteOffset, field.byteLength)\n}\n\nfunction toBufferChunk(chunk) {\n return { field: toBuffer(chunk.field), offset: chunk.offset }\n}\n\nclass Index {\n static from(fieldOrChunks, byteLength = -1) {\n if (Array.isArray(fieldOrChunks)) {\n return new SparseIndex(fieldOrChunks, byteLength)\n } else {\n return new DenseIndex(fieldOrChunks, byteLength)\n }\n }\n\n constructor(byteLength) {\n this._byteLength = byteLength\n this.handle = Buffer.allocUnsafe(binding.sizeof_quickbit_index_t)\n }\n\n get byteLength() {\n return this._byteLength\n }\n\n skipFirst(value, position = 0) {\n const n = this.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) position = 0\n if (position >= n) return n - 1\n\n return binding.quickbit_napi_skip_first(\n this.handle,\n this.byteLength,\n value ? 1 : 0,\n position\n )\n }\n\n skipLast(value, position = this.byteLength * 8 - 1) {\n const n = this.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) return 0\n if (position >= n) position = n - 1\n\n return binding.quickbit_napi_skip_last(\n this.handle,\n this.byteLength,\n value ? 1 : 0,\n position\n )\n }\n}\n\nexports.Index = Index\n\nclass DenseIndex extends Index {\n constructor(field, byteLength) {\n super(byteLength)\n this.field = field\n\n binding.quickbit_napi_index_init(this.handle, toBuffer(this.field))\n }\n\n get byteLength() {\n if (this._byteLength !== -1) return this._byteLength\n return this.field.byteLength\n }\n\n update(bit) {\n const n = this.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n return (\n binding.quickbit_napi_index_update(\n this.handle,\n toBuffer(this.field),\n bit\n ) !== 0\n )\n }\n}\n\nfunction selectChunk(chunks, offset) {\n for (let i = 0; i < chunks.length; i++) {\n const next = chunks[i]\n\n const start = next.offset\n const end = next.offset + next.field.byteLength\n\n if (offset >= start && offset + 16 <= end) {\n return next\n }\n }\n\n return null\n}\n\nclass SparseIndex extends Index {\n constructor(chunks, byteLength) {\n super(byteLength)\n this.chunks = chunks\n\n binding.quickbit_napi_index_init_sparse(\n this.handle,\n this.chunks.map(toBufferChunk)\n )\n }\n\n get byteLength() {\n if (this._byteLength !== -1) return this._byteLength\n const last = this.chunks[this.chunks.length - 1]\n return last ? last.offset + last.field.byteLength : 0\n }\n\n update(bit) {\n const n = this.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n const j = Math.floor(bit / 128)\n\n const offset = j * 16\n\n const chunk = selectChunk(this.chunks, offset)\n\n if (chunk === null) return false\n\n return (\n binding.quickbit_napi_index_update_sparse(\n this.handle,\n toBuffer(chunk.field),\n chunk.offset,\n bit\n ) !== 0\n )\n }\n}\n{\n \"name\": \"quickbit-native\",\n \"version\": \"2.4.8\",\n \"description\": \"libquickbit JavaScript bindings for Node.js\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"macros.h\",\n \"binding.cc\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"prebuilds\"\n ],\n \"addon\": true,\n \"scripts\": {\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:bare\": \"bare test.mjs\",\n \"test:node\": \"node test.mjs\",\n \"lint\": \"prettier . --check\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/quickbit-native.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/quickbit-native/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/quickbit-native#readme\",\n \"dependencies\": {\n \"require-addon\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"b4a\": \"^1.6.7\",\n \"bare-compat-napi\": \"^1.3.2\",\n \"brittle\": \"^3.1.0\",\n \"cmake-bare\": \"^1.1.7\",\n \"cmake-fetch\": \"^1.1.0\",\n \"cmake-napi\": \"^1.0.2\",\n \"prettier\": \"^3.5.3\",\n \"prettier-config-standard\": \"^7.0.0\"\n }\n}\nconst simdle = require('simdle-universal')\n\nconst INDEX_LEN = (16 /* root */ + 128 * 16 /* children */) * 2\n\nconst get = exports.get = function get (field, bit) {\n const n = field.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n const m = field.BYTES_PER_ELEMENT * 8\n\n const offset = bit & (m - 1)\n const i = (bit - offset) / m\n\n return (field[i] & (1 << offset)) !== 0\n}\n\nconst set = exports.set = function set (field, bit, value = true) {\n const n = field.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n const m = field.BYTES_PER_ELEMENT * 8\n\n const offset = bit & (m - 1)\n const i = (bit - offset) / m\n const mask = 1 << offset\n\n if (value) {\n if ((field[i] & mask) !== 0) return false\n } else {\n if ((field[i] & mask) === 0) return false\n }\n\n field[i] ^= mask\n\n return true\n}\n\nexports.fill = function fill (field, value, start = 0, end = field.byteLength * 8) {\n const n = field.byteLength * 8\n\n if (start < 0) start += n\n if (end < 0) end += n\n if (start < 0 || start >= field.byteLength * 8 || start >= end) return field\n\n const m = field.BYTES_PER_ELEMENT * 8\n\n let i, j\n\n {\n const offset = start & (m - 1)\n i = (start - offset) / m\n\n if (offset !== 0) {\n let shift = m - offset\n if (end - start < shift) shift = end - start\n\n const mask = ((1 << shift) - 1) << offset\n\n if (value) field[i] |= mask\n else field[i] &= ~mask\n\n i++\n }\n }\n\n {\n const offset = end & (m - 1)\n j = (end - offset) / m\n\n if (offset !== 0 && j >= i) {\n const mask = (1 << offset) - 1\n\n if (value) field[j] |= mask\n else field[j] &= ~mask\n }\n }\n\n if (i < j) field.fill(value ? (2 ** m) - 1 : 0, i, j)\n\n return field\n}\n\nexports.clear = function clear (field, ...chunks) {\n const n = field.byteLength\n\n for (const chunk of chunks) {\n if (chunk.offset >= n) continue\n\n const m = chunk.field.byteLength\n\n let i = chunk.offset\n let j = 0\n\n while (((i & 15) !== 0 || (j & 15) !== 0) && i < n && j < m) {\n field[i] = field[i] & ~chunk.field[j]\n i++\n j++\n }\n\n if (i + 15 < n && j + 15 < m) {\n const len = Math.min(n - (n & 15) - i, m - (m & 15) - j)\n\n simdle.clear(field.subarray(i, i + len), chunk.field.subarray(j, j + len), field.subarray(i, i + len))\n }\n\n while (i < n && j < m) {\n field[i] = field[i] & ~chunk.field[j]\n i++\n j++\n }\n }\n}\n\nfunction bitOffset (bit, offset) {\n return !bit ? offset : (INDEX_LEN * 8 / 2) + offset\n}\n\nfunction byteOffset (bit, offset) {\n return !bit ? offset : (INDEX_LEN / 2) + offset\n}\n\nexports.findFirst = function findFirst (field, value, position = 0) {\n const n = field.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) position = 0\n if (position >= n) return -1\n\n value = !!value\n\n for (let i = position; i < n; i++) {\n if (get(field, i) === value) return i\n }\n\n return -1\n}\n\nexports.findLast = function findLast (field, value, position = field.byteLength * 8 - 1) {\n const n = field.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) return -1\n if (position >= n) position = n - 1\n\n value = !!value\n\n for (let i = position; i >= 0; i--) {\n if (get(field, i) === value) return i\n }\n\n return -1\n}\n\nconst Index = exports.Index = class Index {\n static from (fieldOrChunks, byteLength = -1) {\n if (Array.isArray(fieldOrChunks)) {\n return new SparseIndex(fieldOrChunks, byteLength)\n } else {\n return new DenseIndex(fieldOrChunks, byteLength)\n }\n }\n\n constructor (byteLength) {\n this._byteLength = byteLength\n this.handle = new Uint32Array(INDEX_LEN / 4)\n }\n\n get byteLength () {\n return this._byteLength\n }\n\n skipFirst (value, position = 0) {\n const n = this.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) position = 0\n if (position >= n) return n - 1\n\n let i = Math.floor(position / 16384)\n\n if (i > 127) return position\n\n while (i <= 127 && get(this.handle, bitOffset(value, i))) {\n i++\n }\n\n if (i === 128) return n - 1\n\n let k = i * 16384\n let j = 0\n\n if (position > k) j = Math.floor((position - k) / 128)\n\n while (j <= 127 && get(this.handle, bitOffset(value, i * 128 + j + 128))) {\n j++\n k += 128\n }\n\n if (j === 128 && i !== 127) return this.skipFirst(value, (i + 1) * 16384)\n\n if (k > position) position = k\n\n return position < n ? position : n - 1\n }\n\n skipLast (value, position = this.byteLength * 8 - 1) {\n const n = this.byteLength * 8\n\n if (position < 0) position += n\n if (position < 0) return 0\n if (position >= n) position = n - 1\n\n let i = Math.floor(position / 16384)\n\n if (i > 127) return position\n\n while (i >= 0 && get(this.handle, bitOffset(value, i))) {\n i--\n }\n\n if (i === -1) return 0\n\n let k = ((i + 1) * 16384) - 1\n let j = 127\n\n if (position < k) j = 128 - Math.ceil((k - position) / 128)\n\n while (j >= 0 && get(this.handle, bitOffset(value, i * 128 + j + 128))) {\n j--\n k -= 128\n }\n\n if (j === -1 && i !== 0) return this.skipLast(value, i * 16384 - 1)\n\n if (k < position) position = k\n\n return position\n }\n}\n\nclass DenseIndex extends Index {\n constructor (field, byteLength) {\n super(byteLength)\n this.field = field\n\n const m = field.BYTES_PER_ELEMENT\n\n for (let i = 0; i < 128; i++) {\n for (let j = 0; j < 128; j++) {\n const offset = (i * 128 + j) * 16\n let allz = true\n let allo = false\n\n if (offset + 16 <= this.field.byteLength) {\n const vec = this.field.subarray(offset / m, (offset + 16) / m)\n\n allz = simdle.allz(vec)\n allo = simdle.allo(vec)\n }\n\n const k = i * 128 + 128 + j\n\n set(this.handle, bitOffset(false, k), allz)\n set(this.handle, bitOffset(true, k), allo)\n }\n\n {\n const offset = byteOffset(false, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(false, i), allo)\n }\n\n {\n const offset = byteOffset(true, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(true, i), allo)\n }\n }\n }\n\n get byteLength () {\n if (this._byteLength !== -1) return this._byteLength\n return this.field.byteLength\n }\n\n update (bit) {\n const n = this.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n const m = this.field.BYTES_PER_ELEMENT\n\n const i = Math.floor(bit / 16384)\n const j = Math.floor(bit / 128)\n\n const offset = (j * 16) / m\n const vec = this.field.subarray(offset, offset + (16 / m))\n\n const allz = simdle.allz(vec)\n const allo = simdle.allo(vec)\n\n let changed = false\n\n if (set(this.handle, bitOffset(false, 128 + j), allz)) {\n changed = true\n\n const offset = byteOffset(false, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(false, i), allo)\n }\n\n if (set(this.handle, bitOffset(true, 128 + j), allo)) {\n changed = true\n\n const offset = byteOffset(true, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(true, i), allo)\n }\n\n return changed\n }\n}\n\nfunction selectChunk (chunks, offset) {\n for (let i = 0; i < chunks.length; i++) {\n const next = chunks[i]\n\n const start = next.offset\n const end = next.offset + next.field.byteLength\n\n if (offset >= start && offset + 16 <= end) {\n return next\n }\n }\n\n return null\n}\n\nclass SparseIndex extends Index {\n constructor (chunks, byteLength) {\n super(byteLength)\n this.chunks = chunks\n\n for (let i = 0; i < 128; i++) {\n for (let j = 0; j < 128; j++) {\n const offset = (i * 128 + j) * 16\n let allz = true\n let allo = false\n\n const chunk = selectChunk(this.chunks, offset)\n\n if (chunk !== null) {\n const m = chunk.field.BYTES_PER_ELEMENT\n\n const vec = chunk.field.subarray((offset - chunk.offset) / m, (offset - chunk.offset + 16) / m)\n\n allz = simdle.allz(vec)\n allo = simdle.allo(vec)\n }\n\n const k = i * 128 + 128 + j\n\n set(this.handle, bitOffset(false, k), allz)\n set(this.handle, bitOffset(true, k), allo)\n }\n\n {\n const offset = byteOffset(false, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(false, i), allo)\n }\n\n {\n const offset = byteOffset(true, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(true, i), allo)\n }\n }\n }\n\n get byteLength () {\n if (this._byteLength !== -1) return this._byteLength\n const last = this.chunks[this.chunks.length - 1]\n return last ? last.offset + last.field.byteLength : 0\n }\n\n update (bit) {\n const n = this.byteLength * 8\n\n if (bit < 0) bit += n\n if (bit < 0 || bit >= n) return false\n\n const i = Math.floor(bit / 16384)\n const j = Math.floor(bit / 128)\n\n const offset = j * 16\n\n const chunk = selectChunk(this.chunks, offset)\n\n if (chunk === null) return false\n\n const m = chunk.field.BYTES_PER_ELEMENT\n\n const vec = chunk.field.subarray((offset - chunk.offset) / m, (offset - chunk.offset + 16) / m)\n\n const allz = simdle.allz(vec)\n const allo = simdle.allo(vec)\n\n let changed = false\n\n if (set(this.handle, bitOffset(false, 128 + j), allz)) {\n changed = true\n\n const offset = byteOffset(false, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(false, i), allo)\n }\n\n if (set(this.handle, bitOffset(true, 128 + j), allo)) {\n changed = true\n\n const offset = byteOffset(true, i * 16 + 16) / 4\n const allo = simdle.allo(this.handle.subarray(offset, offset + 4))\n\n set(this.handle, bitOffset(true, i), allo)\n }\n\n return changed\n }\n}\nconst fallback = require('./fallback')\n\ntry {\n const native = require('quickbit-native')\n\n // These functions are faster in JavaScript\n exports.get = fallback.get\n exports.set = fallback.set\n exports.fill = fallback.fill\n\n // These functions are faster in C\n exports.clear = native.clear\n exports.findFirst = native.findFirst\n exports.findLast = native.findLast\n exports.Index = native.Index\n} catch {\n module.exports = fallback\n}\n{\n \"name\": \"quickbit-universal\",\n \"version\": \"2.2.0\",\n \"description\": \"Universal wrapper for libquickbit with a JavaScript fallback\",\n \"main\": \"index.js\",\n \"files\": [\n \"fallback.js\",\n \"index.js\"\n ],\n \"browser\": {\n \"./index.js\": \"./fallback.js\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/quickbit-universal.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð <kasper@funktionel.co>\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/quickbit-universal/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/quickbit-universal#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.6.0\",\n \"simdle-universal\": \"^1.1.0\"\n },\n \"optionalDependencies\": {\n \"quickbit-native\": \"^2.2.0\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.0\",\n \"standard\": \"^17.0.0\"\n }\n}\nclass CacheEntry {\n constructor (key, index, map) {\n this.key = key\n this.index = index\n this.map = map\n }\n}\n\nclass CacheValue {\n constructor (entry, value) {\n this.entry = entry\n this.value = value\n }\n}\n\nclass Rache {\n constructor ({ maxSize = 65536, parent = null } = {}) {\n this.maxSize = parent?.maxSize || maxSize\n\n this._array = parent?._array || []\n this._map = new Map()\n }\n\n static from (cache) {\n return cache ? new this({ parent: cache }) : new this()\n }\n\n get globalSize () {\n return this._array.length\n }\n\n get size () {\n return this._map.size\n }\n\n sub () {\n return new Rache({ parent: this })\n }\n\n set (key, value) { // ~constant time\n const existing = this._map.get(key)\n if (existing !== undefined) {\n existing.value = value\n return\n }\n\n if (this._array.length >= this.maxSize) this._gc()\n\n const entry = new CacheEntry(key, this._array.length, this._map)\n this._array.push(entry)\n const cacheValue = new CacheValue(entry, value)\n this._map.set(key, cacheValue)\n }\n\n delete (key) {\n const existing = this._map.get(key)\n if (existing === undefined) return false\n\n this._delete(existing.entry.index)\n return true\n }\n\n get (key) {\n const existing = this._map.get(key)\n return existing === undefined ? undefined : existing.value\n }\n\n * [Symbol.iterator] () {\n for (const [key, { value }] of this._map) {\n yield [key, value]\n }\n }\n\n keys () {\n return this._map.keys()\n }\n\n * values () {\n for (const { value } of this._map.values()) {\n yield value\n }\n }\n\n clear () {\n // The entries in map linger on in _array,\n // so on top of clearing the map, we also kill the ref,\n // so that any gc running later on the old map won't interfere\n // (in case a new entry was added with the same key as a cleared entry)\n\n this._map.clear()\n this._map = new Map()\n }\n\n destroy () {\n this._map = null\n this._array = null\n }\n\n _gc () {\n this._delete(Math.floor(Math.random() * this._array.length))\n }\n\n _delete (index) { // ~constant time\n if (index >= this._array.length) throw new Error('Cannot delete unused index (logic bug?)')\n\n const head = this._array.pop()\n let removed = head\n\n if (index < this._array.length) {\n removed = this._array[index]\n head.index = index\n this._array[index] = head\n }\n\n removed.map.delete(removed.key)\n }\n}\n\nmodule.exports = Rache\n{\n \"name\": \"rache\",\n \"version\": \"1.0.0\",\n \"description\": \"Random-eviction cache\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test.js --coverage\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/rache.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/rache/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/rache#readme\",\n \"files\": [\n \"index.js\"\n ],\n \"dependencies\": {},\n \"devDependencies\": {\n \"brittle\": \"^3.5.2\",\n \"standard\": \"^17.1.0\"\n }\n}\nmodule.exports = class RandomArrayIterator {\n constructor (values) {\n this.values = values\n this.start = 0\n this.length = this.values.length\n }\n\n next () {\n if (this.length === 0) {\n if (this.start === 0) return { done: true, value: undefined }\n this.length = this.start\n this.start = 0\n }\n\n const i = this.start + ((Math.random() * this.length) | 0)\n const j = this.start + --this.length\n const value = this.values[i]\n\n this.values[i] = this.values[j]\n this.values[j] = value\n\n return { done: false, value }\n }\n\n dequeue () {\n this.values[this.start + this.length] = this.values[this.values.length - 1]\n this.values.pop()\n }\n\n requeue () {\n const i = this.start + this.length\n const value = this.values[i]\n this.values[i] = this.values[this.start]\n this.values[this.start++] = value\n }\n\n restart () {\n this.start = 0\n this.length = this.values.length\n return this\n }\n\n [Symbol.iterator] () {\n return this\n }\n}\n{\n \"name\": \"random-array-iterator\",\n \"version\": \"1.0.0\",\n \"description\": \"An iterator to iterate an array in random order with controls to requeue or dequeue elements during the iteration\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"standard\": \"^16.0.3\",\n \"tape\": \"^5.0.1\"\n },\n \"scripts\": {\n \"test\": \"standard && tape test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/random-array-iterator.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/random-array-iterator/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/random-array-iterator\"\n}\nconst EventEmitter = require('events')\n\nmodule.exports = class ReadyResource extends EventEmitter {\n constructor () {\n super()\n\n this.opening = null\n this.closing = null\n\n this.opened = false\n this.closed = false\n }\n\n ready () {\n if (this.opening !== null) return this.opening\n this.opening = open(this)\n return this.opening\n }\n\n close () {\n if (this.closing !== null) return this.closing\n this.closing = close(this)\n return this.closing\n }\n\n async _open () {\n // add impl here\n }\n\n async _close () {\n // add impl here\n }\n}\n\nasync function open (self) {\n // open after close\n if (self.closing !== null) return\n\n try {\n await self._open()\n } catch (err) {\n self.close() // safe to run in bg\n throw err\n }\n\n self.opened = true\n self.emit('ready')\n}\n\nasync function close (self) {\n try {\n if (self.opened === false && self.opening !== null) await self.opening\n } catch {\n // ignore errors on closing\n }\n if (self.opened === true || self.opening === null) await self._close()\n self.closed = true\n self.emit('close')\n}\n{\n \"name\": \"ready-resource\",\n \"version\": \"1.2.0\",\n \"description\": \"Modern single resource management\",\n \"main\": \"index.js\",\n \"imports\": {\n \"events\": {\n \"bare\": \"bare-events\",\n \"default\": \"events\"\n }\n },\n \"types\": \"index.d.ts\",\n \"files\": [\n \"index.js\",\n \"index.d.ts\"\n ],\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.0\",\n \"standard\": \"^17.0.0\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/ready-resource.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/ready-resource/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/ready-resource\",\n \"dependencies\": {\n \"bare-events\": \"^2.2.0\"\n }\n}\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}\nmodule.exports = class RefCounter {\n constructor() {\n this.count = 0\n\n this._onidle = null\n this._idle = null\n }\n\n isIdle() {\n return this.count === 0\n }\n\n idle() {\n if (this.count === 0) return Promise.resolve()\n if (this._idle !== null) return this._idle\n\n this._idle = new Promise((resolve) => {\n this._onidle = resolve\n })\n\n return this._idle\n }\n\n inc() {\n this.count++\n }\n\n dec() {\n if (--this.count > 0) return\n\n if (this._onidle !== null) {\n const resolve = this._onidle\n this._idle = null\n this._onidle = null\n resolve()\n }\n }\n}\n{\n \"name\": \"refcounter\",\n \"version\": \"1.0.0\",\n \"description\": \"Simple refcounter\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {},\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/refcounter.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/refcounter/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/refcounter\"\n}\nmodule.exports = require.addon.bind(require)\n{\n \"name\": \"require-addon\",\n \"version\": \"1.2.0\",\n \"description\": \"Import native addons across JavaScript runtimes\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"bare\": \"./lib/bare.js\",\n \"node\": \"./lib/node.js\",\n \"default\": \"./lib/default.js\"\n }\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 \"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 \"format\": \"prettier --write .\"\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 },\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 \"bare-url\": \"^2.1.0\",\n \"brittle\": \"^3.7.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\n }\n}\nlet tmpResolve = null\nlet tmpReject = null\n\nif (Promise.withResolvers) {\n module.exports = Promise.withResolvers.bind(Promise)\n} else {\n module.exports = function resolveRejectPromise () {\n const promise = new Promise(setTmp)\n const result = { promise, resolve: tmpResolve, reject: tmpReject }\n tmpResolve = tmpReject = null\n return result\n }\n}\n\nfunction setTmp (resolve, reject) {\n tmpResolve = resolve\n tmpReject = reject\n}\n{\n \"name\": \"resolve-reject-promise\",\n \"version\": \"1.1.0\",\n \"description\": \"Create an inverted promise with no function allocs\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard\"\n },\n \"devDependencies\": {\n \"standard\": \"^17.1.2\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/resolve-reject-promise.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/resolve-reject-promise/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/resolve-reject-promise\"\n}\nconst resources = new Map()\n\nexports.add = function(resource, teardown) {\n resources.set(resource, teardown)\n}\n\nexports.remove = function(resource) {\n resources.delete(resource)\n}\n\nif (global.Bare) global.Bare.on('exit', run)\nelse global.process.on('exit', run)\n\nfunction run() {\n for (const [resource, teardown] of resources) {\n teardown(resource)\n }\n}\n{\n \"name\": \"resource-on-exit\",\n \"version\": \"1.0.0\",\n \"description\": \"Register sync teardown handlers for a resource\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {},\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/bare-teardown.git\"\n },\n \"author\": \"Holepunch Inc.\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/bare-teardown/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/bare-teardown\"\n}\nrequire.addon = require('require-addon')\n\nmodule.exports = require.addon('.', __filename)\nconst ColumnFamily = require('./lib/column-family')\nconst Iterator = require('./lib/iterator')\nconst Snapshot = require('./lib/snapshot')\nconst State = require('./lib/state')\nconst { BloomFilterPolicy, RibbonFilterPolicy } = require('./lib/filter-policy')\nconst constants = require('./lib/constants')\n\nclass RocksDB {\n constructor(path, opts = {}) {\n const {\n columnFamily,\n state = new State(this, path, opts),\n snapshot = null,\n keyEncoding = null,\n valueEncoding = null\n } = opts\n\n this.stats = {\n readBatches: 0,\n writeBatches: 0,\n gets: 0,\n puts: 0\n }\n\n this._state = state\n this._snapshot = snapshot\n this._columnFamily = state.getColumnFamily(columnFamily)\n this._keyEncoding = keyEncoding\n this._valueEncoding = valueEncoding\n this._index = -1\n\n this._state.addSession(this)\n }\n\n get opened() {\n return this._state.opened\n }\n\n get closed() {\n return this.isRoot() ? this._state.closed : this._index === -1\n }\n\n get path() {\n return this._state.path\n }\n\n get snapshotted() {\n return this._snapshot !== null\n }\n\n get defaultColumnFamily() {\n return this._columnFamily\n }\n\n session({\n columnFamily = this._columnFamily,\n snapshot = this._snapshot !== null,\n keyEncoding = this._keyEncoding,\n valueEncoding = this._valueEncoding\n } = {}) {\n maybeClosed(this)\n\n return new RocksDB(null, {\n state: this._state,\n columnFamily,\n snapshot: snapshot ? this._snapshot || new Snapshot(this._state) : null,\n keyEncoding,\n valueEncoding\n })\n }\n\n columnFamily(name, opts) {\n return this.session({ ...opts, columnFamily: name })\n }\n\n snapshot() {\n return this.session({ snapshot: true })\n }\n\n isRoot() {\n return this === this._state.db\n }\n\n ready() {\n return this._state.ready()\n }\n\n async close({ force } = {}) {\n if (!this._state.opened) await this._state.ready()\n\n if (this._index !== -1) this._state.removeSession(this)\n\n if (force) {\n while (this._state.sessions.length > 0) {\n await this._state.sessions[this._state.sessions.length - 1].close()\n }\n }\n\n return this.isRoot() ? this._state.close() : Promise.resolve()\n }\n\n suspend() {\n maybeClosed(this)\n\n return this._state.suspend()\n }\n\n resume() {\n maybeClosed(this)\n\n return this._state.resume()\n }\n\n isIdle() {\n return this._state.handles.isIdle()\n }\n\n idle() {\n return this._state.handles.idle()\n }\n\n iterator(range, opts) {\n maybeClosed(this)\n\n return new Iterator(this, { ...range, ...opts })\n }\n\n async *keys(range, opts) {\n for await (const { key } of this.iterator(range, {\n ...opts,\n values: false\n })) {\n yield key\n }\n }\n\n async peek(range, opts) {\n for await (const value of this.iterator({ ...range, ...opts, limit: 1 })) {\n return value\n }\n\n return null\n }\n\n read(opts) {\n maybeClosed(this)\n this.stats.readBatches++\n\n return this._state.createReadBatch(this, opts)\n }\n\n write(opts) {\n maybeClosed(this)\n this.stats.writeBatches++\n\n return this._state.createWriteBatch(this, opts)\n }\n\n flush(opts) {\n maybeClosed(this)\n\n return this._state.flush(this, opts)\n }\n\n async get(key, opts) {\n const batch = this.read({ ...opts, capacity: 1, autoDestroy: true })\n const value = batch.get(key)\n batch.tryFlush()\n return value\n }\n\n async put(key, value, opts) {\n const batch = this.write({ ...opts, capacity: 1, autoDestroy: true })\n batch.tryPut(key, value)\n await batch.flush()\n }\n\n async delete(key, opts) {\n const batch = this.write({ ...opts, capacity: 1, autoDestroy: true })\n batch.tryDelete(key)\n await batch.flush()\n }\n\n async deleteRange(start, end, opts) {\n const batch = this.write({ ...opts, capacity: 1, autoDestroy: true })\n batch.tryDeleteRange(start, end)\n await batch.flush()\n }\n\n async compactRange(start = null, end = null, opts = {}) {\n if (typeof end === 'object' && end !== null) {\n opts = end\n end = null\n } else if (typeof start === 'object' && start !== null) {\n opts = start\n start = null\n end = null\n }\n\n await this._state.compactRange(this, start, end, opts)\n }\n\n async approximateSize(start, end, opts = {}) {\n return this._state.approximateSize(this, start, end, opts)\n }\n\n _ref() {\n if (this._snapshot) this._snapshot.ref()\n this._state.handles.inc()\n }\n\n _unref() {\n if (this._snapshot) this._snapshot.unref()\n this._state.handles.dec()\n }\n}\n\nmodule.exports = exports = RocksDB\n\nexports.constants = constants\n\nexports.ColumnFamily = ColumnFamily\nexports.BloomFilterPolicy = BloomFilterPolicy\nexports.RibbonFilterPolicy = RibbonFilterPolicy\n\nfunction maybeClosed(db) {\n if (db._state.closing || db._index === -1) throw new Error('RocksDB session is closed')\n}\nconst c = require('compact-encoding')\nconst binding = require('../binding')\n\nconst empty = Buffer.alloc(0)\nconst resolved = Promise.resolve()\n\nclass RocksDBBatch {\n constructor(db, opts = {}) {\n const { capacity = 8, autoDestroy = false } = opts\n\n db._ref()\n\n this._db = db\n this._destroyed = false\n this._capacity = capacity\n this._operations = []\n this._promises = []\n\n this._enqueuePromise = this._enqueuePromise.bind(this)\n\n this._request = null\n this._resolve = null\n this._reject = null\n\n this._handle = null\n this._buffer = null\n this._autoDestroy = autoDestroy\n\n if (db._state.opened === true) this.ready()\n }\n\n _reuse(db, opts = {}) {\n const { autoDestroy = false } = opts\n\n db._ref()\n\n this._db = db\n this._destroyed = false\n this._autoDestroy = autoDestroy\n }\n\n _onfinished(err) {\n const resolve = this._resolve\n const reject = this._reject\n\n if (this._request) this._db._state.io.dec()\n\n this._operations = []\n this._promises = []\n this._request = null\n this._resolve = null\n this._reject = null\n\n if (this._autoDestroy === true) this.destroy()\n\n if (reject !== null && err) reject(err)\n else if (resolve !== null) resolve()\n }\n\n _resize() {\n if (this._operations.length <= this._capacity) return false\n\n while (this._operations.length > this._capacity) {\n this._capacity *= 2\n }\n\n return true\n }\n\n async ready() {\n if (this._handle !== null) return\n\n if (this._db._state.opened === false) await this._db._state.ready()\n\n this._init()\n }\n\n destroy() {\n if (this._request) throw new Error('Request in progress')\n if (this._destroyed) return\n\n this._destroyed = true\n\n if (this._promises.length) this._abort()\n\n this._db._unref()\n this._onfree()\n }\n\n _onfree() {\n this._db._state.freeBatch(this, false)\n this._db = null\n }\n\n _abort() {\n for (let i = 0; i < this._promises.length; i++) {\n const promise = this._promises[i]\n if (promise !== null) promise.reject(new Error('Batch is destroyed'))\n }\n\n this._onfinished(new Error('Batch is destroyed'))\n }\n\n async flush() {\n if (this._request) throw new Error('Request in progress')\n if (this._destroyed) throw new Error('Batch is destroyed')\n\n this._request = new Promise((resolve, reject) => {\n this._resolve = resolve\n this._reject = reject\n })\n\n this._flush()\n\n return this._request\n }\n\n tryFlush() {\n if (this._request) throw new Error('Request in progress')\n if (this._destroyed) throw new Error('Batch is destroyed')\n\n this._request = resolved\n\n this._flush()\n }\n\n async _flush() {\n if (this._handle === null) await this.ready()\n\n this._db._state.io.inc()\n\n if (this._db._state.resumed !== null) {\n const resumed = await this._db._state.resumed.promise\n\n if (!resumed) {\n if (this._destroyed) {\n this._db._state.io.dec()\n } else {\n this._destroyed = true\n this._abort()\n this._db._unref()\n }\n }\n }\n }\n\n _enqueuePromise(resolve, reject) {\n this._promises.push({ resolve, reject })\n }\n\n _encodeKey(k) {\n if (this._db._keyEncoding) return c.encode(this._db._keyEncoding, k)\n if (typeof k === 'string') return Buffer.from(k)\n return k\n }\n\n _encodeValue(v) {\n if (this._db._valueEncoding) return c.encode(this._db._valueEncoding, v)\n if (v === null) return empty\n if (typeof v === 'string') return Buffer.from(v)\n return v\n }\n\n _decodeValue(b) {\n if (this._db._valueEncoding) return c.decode(this._db._valueEncoding, b)\n return b\n }\n}\n\nexports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch {\n constructor(db, opts = {}) {\n super(db, opts)\n\n const { asyncIO = false, fillCache = true } = opts\n\n this._asyncIO = asyncIO\n this._fillCache = fillCache\n }\n\n _init() {\n this._handle = binding.readInit()\n this._buffer = binding.readBuffer(this._handle, this._capacity)\n }\n\n _resize() {\n if (super._resize() && this._handle !== null) {\n this._buffer = binding.readBuffer(this._handle, this._capacity)\n }\n }\n\n async _flush() {\n await super._flush()\n\n if (this._destroyed) return\n\n try {\n binding.read(\n this._db._state._handle,\n this._handle,\n this._operations,\n this._db._snapshot ? this._db._snapshot._handle : undefined,\n this._asyncIO,\n this._fillCache,\n this,\n this._onread\n )\n } catch (err) {\n this._db._state.io.dec()\n throw err\n }\n }\n\n _onread(errs, values) {\n let applied = true\n\n for (let i = 0, n = this._promises.length; i < n; i++) {\n const err = errs[i]\n if (err) applied = false\n\n const promise = this._promises[i]\n if (promise === null) continue\n\n if (err) promise.reject(new Error(err))\n else promise.resolve(values[i] ? this._decodeValue(Buffer.from(values[i])) : null)\n }\n\n this._onfinished(applied ? null : new AggregateError(errs, 'Batch was not applied'))\n }\n\n get(key) {\n if (this._request) throw new Error('Request already in progress')\n this._db.stats.gets++\n\n const promise = new Promise(this._enqueuePromise)\n\n this._operations.push(new RocksDBGet(this._encodeKey(key), this._db._columnFamily))\n\n this._resize()\n\n return promise\n }\n}\n\nexports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch {\n _init() {\n this._handle = binding.writeInit()\n this._buffer = binding.writeBuffer(this._handle, this._capacity)\n }\n\n _resize() {\n if (super._resize() && this._handle !== null) {\n this._buffer = binding.writeBuffer(this._handle, this._capacity)\n }\n }\n\n _onfree() {\n this._db._state.freeBatch(this, true)\n }\n\n async _flush() {\n await super._flush()\n\n if (this._destroyed) return\n\n try {\n binding.write(this._db._state._handle, this._handle, this._operations, this, this._onwrite)\n } catch (err) {\n this._db._state.io.dec()\n throw err\n }\n }\n\n _onwrite(err) {\n const applied = !err\n\n for (let i = 0, n = this._promises.length; i < n; i++) {\n const promise = this._promises[i]\n if (promise === null) continue\n\n if (err) promise.reject(new Error(err))\n else promise.resolve()\n }\n\n this._onfinished(applied ? null : new Error('Batch was not applied', { cause: err }))\n }\n\n put(key, value) {\n if (this._request) throw new Error('Request already in progress')\n this._db.stats.puts++\n\n const promise = new Promise(this._enqueuePromise)\n\n this._operations.push(\n new RocksDBPut(this._encodeKey(key), this._encodeValue(value), this._db._columnFamily)\n )\n\n this._resize()\n\n return promise\n }\n\n tryPut(key, value) {\n if (this._request) throw new Error('Request already in progress')\n\n this._operations.push(\n new RocksDBPut(this._encodeKey(key), this._encodeValue(value), this._db._columnFamily)\n )\n\n this._promises.push(null)\n\n this._resize()\n }\n\n delete(key) {\n if (this._request) throw new Error('Request already in progress')\n\n const promise = new Promise(this._enqueuePromise)\n\n this._operations.push(new RocksDBDelete(this._encodeKey(key), this._db._columnFamily))\n\n this._resize()\n\n return promise\n }\n\n tryDelete(key) {\n if (this._request) throw new Error('Request already in progress')\n\n this._operations.push(new RocksDBDelete(this._encodeKey(key), this._db._columnFamily))\n\n this._promises.push(null)\n\n this._resize()\n }\n\n deleteRange(start, end) {\n if (this._request) throw new Error('Request already in progress')\n\n const promise = new Promise(this._enqueuePromise)\n\n this._operations.push(\n new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end), this._db._columnFamily)\n )\n\n this._resize()\n\n return promise\n }\n\n tryDeleteRange(start, end) {\n if (this._request) throw new Error('Request already in progress')\n\n this._operations.push(\n new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end), this._db._columnFamily)\n )\n\n this._promises.push(null)\n\n this._resize()\n }\n}\n\nclass RocksDBGet {\n constructor(key, columnFamily) {\n this.key = key\n this.columnFamily = columnFamily._handle\n }\n\n get type() {\n return binding.GET\n }\n}\n\nclass RocksDBPut {\n constructor(key, value, columnFamily) {\n this.key = key\n this.value = value\n this.columnFamily = columnFamily._handle\n }\n\n get type() {\n return binding.PUT\n }\n}\n\nclass RocksDBDelete {\n constructor(key, columnFamily) {\n this.key = key\n this.columnFamily = columnFamily._handle\n }\n\n get type() {\n return binding.DELETE\n }\n}\n\nclass RocksDBDeleteRange {\n constructor(start, end, columnFamily) {\n this.start = start\n this.end = end\n this.columnFamily = columnFamily._handle\n }\n\n get type() {\n return binding.DELETE_RANGE\n }\n}\nconst binding = require('../binding')\nconst constants = require('./constants')\nconst { BloomFilterPolicy } = require('./filter-policy')\n\nclass RocksDBColumnFamily {\n constructor(name, opts = {}) {\n const {\n // Blob options\n enableBlobFiles = false,\n minBlobSize = 0,\n blobFileSize = 0,\n enableBlobGarbageCollection = true,\n // Block table options\n tableBlockSize = 8192,\n tableCacheIndexAndFilterBlocks = true,\n tableFormatVersion = 6,\n optimizeFiltersForMemory = false,\n blockCache = true,\n filterPolicy = new BloomFilterPolicy(10),\n topLevelIndexPinningTier = constants.pinningTier.ALL,\n partitionPinningTier = constants.pinningTier.ALL,\n unpartitionedPinningTier = constants.pinningTier.ALL,\n optimizeFiltersForHits = false,\n numLevels = 7,\n maxWriteBufferNumber = 2,\n blobGarbageCollectionAgeCutOff = 0.25,\n blobGarbageCollectionForceThreshold = 1.0\n } = opts\n\n this._name = name\n this._flushing = null\n this._options = {\n enableBlobFiles,\n minBlobSize,\n blobFileSize,\n enableBlobGarbageCollection,\n tableBlockSize,\n tableCacheIndexAndFilterBlocks,\n tableFormatVersion,\n optimizeFiltersForMemory,\n blockCache,\n filterPolicy,\n topLevelIndexPinningTier,\n partitionPinningTier,\n unpartitionedPinningTier,\n optimizeFiltersForHits,\n numLevels,\n maxWriteBufferNumber\n }\n\n const filterPolicyArguments = [0, 0, 0]\n\n if (filterPolicy !== null) {\n filterPolicyArguments[0] = filterPolicy.type\n\n switch (filterPolicy.type) {\n case 1: // Bloom filter policy\n filterPolicyArguments[1] = filterPolicy.bitsPerKey\n break\n case 2: // Ribbon filter policy\n filterPolicyArguments[1] = filterPolicy.bloomEquivalentBitsPerKey\n filterPolicyArguments[2] = filterPolicy.bloomBeforeLevel\n\n break\n }\n }\n\n this._handle = binding.columnFamilyInit(\n name,\n enableBlobFiles,\n minBlobSize,\n blobFileSize,\n enableBlobGarbageCollection,\n tableBlockSize,\n tableCacheIndexAndFilterBlocks,\n tableFormatVersion,\n optimizeFiltersForMemory,\n blockCache === false,\n ...filterPolicyArguments,\n topLevelIndexPinningTier,\n partitionPinningTier,\n unpartitionedPinningTier,\n optimizeFiltersForHits,\n numLevels,\n maxWriteBufferNumber,\n blobGarbageCollectionAgeCutOff,\n blobGarbageCollectionForceThreshold\n )\n }\n\n cloneSettings(name) {\n return new RocksDBColumnFamily(name, this._options)\n }\n\n get name() {\n return this._name\n }\n\n destroy() {\n if (this._handle === null) return\n\n binding.columnFamilyDestroy(this._handle)\n\n this._handle = null\n }\n}\n\nmodule.exports = RocksDBColumnFamily\nmodule.exports = {\n pinningTier: {\n NONE: 0,\n FLUSHED_AND_SIMILAR: 1,\n ALL: 2\n },\n garbageCollectionPolicy: {\n DEFAULT: 0,\n FORCE: 1,\n DISABLE: 2\n },\n bottommostLevelCompaction: {\n NONE: 0,\n SKIP: 1,\n FORCE: 2\n }\n}\nexports.BloomFilterPolicy = class RocksDBBloomFilterPolicy {\n get type() {\n return 1\n }\n\n constructor(bitsPerKey) {\n this.bitsPerKey = bitsPerKey\n }\n}\n\nexports.RibbonFilterPolicy = class RocksDBRibbonFilterPolicy {\n get type() {\n return 2\n }\n\n constructor(bloomEquivalentBitsPerKey, bloomBeforeLevel = 0) {\n this.bloomEquivalentBitsPerKey = bloomEquivalentBitsPerKey\n this.bloomBeforeLevel = bloomBeforeLevel\n }\n}\nconst { Readable } = require('streamx')\nconst c = require('compact-encoding')\nconst binding = require('../binding')\n\nconst empty = Buffer.alloc(0)\n\nmodule.exports = class RocksDBIterator extends Readable {\n constructor(db, opts = {}) {\n const {\n gt = null,\n gte = null,\n lt = null,\n lte = null,\n reverse = false,\n values = true,\n limit = Infinity,\n capacity = 8\n } = opts\n\n super()\n\n db._ref()\n\n this._db = db\n\n this._gt = gt ? this._encodeKey(gt) : empty\n this._gte = gte ? this._encodeKey(gte) : empty\n this._lt = lt ? this._encodeKey(lt) : empty\n this._lte = lte ? this._encodeKey(lte) : empty\n\n this._reverse = reverse\n this._values = values\n this._limit = limit < 0 ? Infinity : limit\n this._capacity = capacity\n this._opened = false\n\n this._pendingOpen = null\n this._pendingRead = null\n this._pendingDestroy = null\n\n this._buffer = null\n this._handle = null\n\n if (this._db._state.opened === true) this.ready()\n }\n\n _onopen(err) {\n const cb = this._pendingOpen\n this._pendingOpen = null\n this._opened = true\n this._db._state.io.dec()\n cb(err)\n }\n\n _onread(err, keys, values) {\n const cb = this._pendingRead\n this._pendingRead = null\n this._db._state.io.dec()\n if (err) return cb(err)\n\n const n = keys.length\n\n this._limit -= n\n\n for (let i = 0; i < n; i++) {\n this.push({\n key: this._decodeKey(Buffer.from(keys[i])),\n value: this._values ? this._decodeValue(Buffer.from(values[i])) : null\n })\n }\n\n if (n < this._capacity) this.push(null)\n\n cb(null)\n }\n\n _onclose(err) {\n const cb = this._pendingDestroy\n this._pendingDestroy = null\n this._db._state.io.dec()\n this._db._unref()\n cb(err)\n }\n\n _resize() {\n if (this._handle !== null) {\n this._buffer = binding.iteratorBuffer(this._handle, this._capacity)\n }\n }\n\n async ready() {\n if (this._handle !== null) return\n\n if (this._db._state.opened === false) await this._db._state.ready()\n\n this._init()\n }\n\n _init() {\n this._handle = binding.iteratorInit()\n this._buffer = binding.iteratorBuffer(this._handle, this._capacity)\n }\n\n async _open(cb) {\n await this.ready()\n\n this._db._state.io.inc()\n\n if (this._db._state.resumed !== null) {\n const resumed = await this._db._state.resumed.promise\n\n if (!resumed) {\n this._db._state.io.dec()\n\n return cb(new Error('RocksDB session is closed'))\n }\n }\n\n this._pendingOpen = cb\n\n try {\n binding.iteratorOpen(\n this._db._state._handle,\n this._handle,\n this._db._columnFamily._handle,\n this._gt,\n this._gte,\n this._lt,\n this._lte,\n this._reverse,\n !this._values, // Keys only\n this._db._snapshot ? this._db._snapshot._handle : undefined,\n this,\n this._onopen,\n this._onclose,\n this._onread\n )\n } catch (err) {\n this._db._state.io.dec()\n\n cb(err)\n }\n }\n\n async _read(cb) {\n this._db._state.io.inc()\n\n if (this._db._state.resumed !== null) {\n const resumed = await this._db._state.resumed.promise\n\n if (!resumed) {\n this._db._state.io.dec()\n\n return cb(new Error('RocksDB session is closed'))\n }\n }\n\n this._pendingRead = cb\n\n try {\n binding.iteratorRead(this._handle, Math.min(this._capacity, this._limit))\n } catch (err) {\n this._db._state.io.dec()\n\n cb(err)\n }\n }\n\n async _destroy(cb) {\n await this.ready()\n\n this._db._state.io.inc()\n\n if (this._opened === false) {\n this._db._state.io.dec()\n this._db._unref()\n\n return cb(null)\n }\n\n this._pendingDestroy = cb\n\n try {\n binding.iteratorClose(this._handle)\n } catch (err) {\n this._db._state.io.dec()\n this._db._unref()\n\n cb(err)\n }\n }\n\n _encodeKey(k) {\n if (this._db._keyEncoding !== null) return c.encode(this._db._keyEncoding, k)\n if (typeof k === 'string') return Buffer.from(k)\n return k\n }\n\n _decodeKey(b) {\n if (this._db._keyEncoding !== null) return c.decode(this._db._keyEncoding, b)\n return b\n }\n\n _decodeValue(b) {\n if (this._db._valueEncoding !== null) return c.decode(this._db._valueEncoding, b)\n return b\n }\n}\nconst binding = require('../binding')\n\nmodule.exports = class RocksDBSnapshot {\n constructor(state) {\n this._state = state\n\n this._handle = null\n this._refs = 0\n\n if (state.deferSnapshotInit === false) this._init()\n }\n\n _init() {\n this._handle = binding.snapshotCreate(this._state._handle)\n }\n\n ref() {\n this._refs++\n }\n\n unref() {\n if (--this._refs > 0) return\n\n if (this._handle === null) return\n\n binding.snapshotDestroy(this._handle)\n\n this._handle = null\n }\n}\nconst ReadyResource = require('ready-resource')\nconst RefCounter = require('refcounter')\nconst rrp = require('resolve-reject-promise')\nconst SignalPromise = require('signal-promise')\nconst c = require('compact-encoding')\nconst { ReadBatch, WriteBatch } = require('./batch')\nconst ColumnFamily = require('./column-family')\nconst binding = require('../binding')\nconst constants = require('./constants')\n\nconst MAX_BATCH_REUSE = 64\nconst empty = Buffer.alloc(0)\n\nmodule.exports = class RocksDBState extends ReadyResource {\n constructor(db, path, opts) {\n super()\n\n const {\n columnFamily = new ColumnFamily('default', opts),\n columnFamilies = [],\n readOnly = false,\n createIfMissing = true,\n createMissingColumnFamilies = true,\n maxBackgroundJobs = 6,\n bytesPerSync = 1048576,\n maxOpenFiles = -1,\n useDirectReads = false,\n avoidUnnecessaryBlockingIO = false,\n skipStatsUpdateOnOpen = false,\n useDirectIOForFlushAndCompaction = false,\n maxFileOpeningThreads = 16,\n lock = null\n } = opts\n\n this.path = path\n this.db = db\n this.handles = new RefCounter()\n this.io = new RefCounter()\n this.sessions = []\n this.columnFamilies = [columnFamily]\n this.deferSnapshotInit = true\n this.resumed = null\n\n this._suspended = false\n this._suspending = false\n this._updating = false\n this._updatingSignal = new SignalPromise()\n this._columnsFlushed = false\n this._lock = lock\n this._readBatches = []\n this._writeBatches = []\n\n for (const columnFamily of columnFamilies) {\n this.columnFamilies.push(\n typeof columnFamily === 'string' ? new ColumnFamily(columnFamily, opts) : columnFamily\n )\n }\n\n this._handle = binding.init(\n readOnly,\n createIfMissing,\n createMissingColumnFamilies,\n maxBackgroundJobs,\n bytesPerSync,\n maxOpenFiles,\n useDirectReads,\n avoidUnnecessaryBlockingIO,\n skipStatsUpdateOnOpen,\n useDirectIOForFlushAndCompaction,\n maxFileOpeningThreads\n )\n }\n\n createReadBatch(db, opts) {\n if (this._readBatches.length === 0) return new ReadBatch(db, opts)\n const batch = this._readBatches.pop()\n batch._reuse(db, opts)\n return batch\n }\n\n createWriteBatch(db, opts) {\n if (this._writeBatches.length === 0) return new WriteBatch(db, opts)\n const batch = this._writeBatches.pop()\n batch._reuse(db, opts)\n return batch\n }\n\n freeBatch(batch, writable) {\n if (batch._capacity > 16) return\n const queue = writable ? this._writeBatches : this._readBatches\n if (queue.length >= MAX_BATCH_REUSE) return\n queue.push(batch)\n }\n\n addSession(db) {\n db._index = this.sessions.push(db) - 1\n if (db._snapshot) db._snapshot.ref()\n }\n\n removeSession(db) {\n const head = this.sessions.pop()\n if (head !== db) this.sessions[(head._index = db._index)] = head\n db._index = -1\n if (db._snapshot) db._snapshot.unref()\n }\n\n upsertColumnFamily(c) {\n if (typeof c === 'string') {\n let col = this.getColumnFamilyByName(c)\n if (col) return col\n col = this.columnFamilies[0].cloneSettings(c)\n this.columnFamilies.push(col)\n return col\n }\n\n if (this.columnFamilies.includes(c)) return c\n this.columnFamilies.push(c)\n return c\n }\n\n getColumnFamily(c) {\n if (!c) return this.columnFamilies[0]\n if (!this._columnsFlushed) return this.upsertColumnFamily(c)\n\n if (typeof c !== 'string') return c\n\n const col = this.getColumnFamilyByName(c)\n if (col === null) throw new Error('Unknown column family')\n return col\n }\n\n getColumnFamilyByName(name) {\n for (const col of this.columnFamilies) {\n if (col.name === name) return col\n }\n return null\n }\n\n async _open() {\n await Promise.resolve() // Allow column families to populate if on-demand\n\n if (this._lock) await this._lock.ready()\n\n const req = { resolve: null, reject: null, handle: null }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n this._columnsFlushed = true\n\n const lock = this._lock === null ? -1 : this._lock.transfer()\n\n req.handle = binding.open(\n this._handle,\n this,\n this.path,\n this.columnFamilies.map((c) => c._handle),\n lock,\n req,\n onopen\n )\n\n await promise\n\n this.deferSnapshotInit = false\n\n for (const session of this.sessions) {\n if (session._snapshot) session._snapshot._init()\n }\n\n function onopen(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n async _close() {\n while (this._updating) await this._updatingSignal.wait()\n if (this.resumed) this.resumed.resolve(false)\n\n while (!this.io.isIdle()) await this.io.idle()\n while (!this.handles.isIdle()) await this.handles.idle()\n\n while (this.sessions.length > 0) {\n await this.sessions[this.sessions.length - 1].close()\n }\n\n for (const columnFamily of this.columnFamilies) columnFamily.destroy()\n\n const req = { resolve: null, reject: null, handle: null }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n req.handle = binding.close(this._handle, req, onclose)\n\n try {\n await promise\n } finally {\n if (this._lock) await this._lock.close()\n }\n\n function onclose(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n async flush(db, opts) {\n if (this.opened === false) await this.ready()\n\n this.io.inc()\n\n if (this.resumed !== null) {\n const resumed = await this.resumed.promise\n\n if (!resumed) {\n this.io.dec()\n\n throw new Error('RocksDB session is closed')\n }\n }\n\n const req = { resolve: null, reject: null, handle: null }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.flush(this._handle, db._columnFamily._handle, req, onflush)\n\n await promise\n } finally {\n this.io.dec()\n }\n\n function onflush(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n suspend() {\n this._suspending = true\n return this.update()\n }\n\n resume() {\n this._suspending = false\n return this.update()\n }\n\n async update() {\n while (this._updating) await this._updatingSignal.wait()\n if (this._suspending === this._suspended || this.closing) return\n this._updating = true\n try {\n if (this._suspending) await this._suspend()\n else await this._resume()\n } finally {\n this._updating = false\n this._updatingSignal.notify()\n }\n }\n\n async _suspend() {\n if (this._suspended === true) return\n\n while (!this.io.isIdle()) await this.io.idle()\n\n this.io.inc()\n this.resumed = rrp()\n\n const req = { resolve: null, reject: null, handle: null }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.suspend(this._handle, req, onsuspend)\n\n await promise\n\n this._suspended = true\n } finally {\n this.io.dec()\n }\n\n function onsuspend(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n async _resume() {\n if (this._suspended === false) return\n\n const req = { resolve: null, reject: null, handle: null }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n req.handle = binding.resume(this._handle, req, onresume)\n\n await promise\n\n this._suspended = false\n\n const resumed = this.resumed\n this.resumed = null\n resumed.resolve(true)\n\n function onresume(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n async compactRange(db, start, end, opts) {\n if (this.opened === false) await this.ready()\n\n this.io.inc()\n\n const {\n exclusive = false,\n blobGarbageCollectionPolicy = constants.garbageCollectionPolicy.DEFAULT,\n blobGarbageCollectionAgeCutoff = 0.25,\n bottommostLevelCompaction = constants.bottommostLevelCompaction.NONE\n } = opts\n\n start = this._encodeKey(start)\n end = this._encodeKey(end)\n\n const req = { resolve: null, reject: null, handle: null, start, end }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.compactRange(\n this._handle,\n db._columnFamily._handle,\n start,\n end,\n exclusive,\n blobGarbageCollectionPolicy,\n blobGarbageCollectionAgeCutoff,\n bottommostLevelCompaction,\n req,\n oncompactrange\n )\n\n await promise\n } finally {\n this.io.dec()\n }\n\n function oncompactrange(err) {\n if (err) req.reject(new Error(err))\n else req.resolve()\n }\n }\n\n async approximateSize(db, start, end, opts) {\n if (this.opened === false) await this.ready()\n\n this.io.inc()\n\n const { includeMemtables = false, includeFiles = true, filesSizeErrorMargin = -1 } = opts\n\n start = this._encodeKey(start)\n end = this._encodeKey(end)\n\n const req = { resolve: null, reject: null, handle: null, start, end }\n\n const promise = new Promise((resolve, reject) => {\n req.resolve = resolve\n req.reject = reject\n })\n\n try {\n req.handle = binding.approximateSize(\n this._handle,\n db._columnFamily._handle,\n start,\n end,\n includeMemtables,\n includeFiles,\n filesSizeErrorMargin,\n req,\n onapproximatesize\n )\n\n return await promise\n } finally {\n this.io.dec()\n }\n\n function onapproximatesize(err, result) {\n if (err) req.reject(new Error(err))\n else req.resolve(result)\n }\n }\n\n _encodeKey(k) {\n if (this.db._keyEncoding) return c.encode(this.db._keyEncoding, k)\n if (typeof k === 'string') return Buffer.from(k)\n if (k === null) return empty\n return k\n }\n}\n{\n \"name\": \"rocksdb-native\",\n \"version\": \"3.13.0\",\n \"description\": \"librocksdb bindings for JavaScript\",\n \"exports\": {\n \".\": \"./index.js\",\n \"./package\": \"./package.json\"\n },\n \"imports\": {\n \"crypto\": {\n \"bare\": \"bare-crypto\",\n \"default\": \"crypto\"\n },\n \"fs\": {\n \"bare\": \"bare-fs\",\n \"default\": \"fs\"\n },\n \"path\": {\n \"bare\": \"bare-path\",\n \"default\": \"path\"\n }\n },\n \"files\": [\n \"index.js\",\n \"binding.cc\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"lib\",\n \"prebuilds\",\n \"!prebuilds/android-*/**/*.node\"\n ],\n \"addon\": true,\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:bare\": \"brittle-bare test.js\",\n \"test:node\": \"brittle-node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/rocksdb-native.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/rocksdb-native/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/rocksdb-native\",\n \"engines\": {\n \"bare\": \">=1.16.0\"\n },\n \"dependencies\": {\n \"compact-encoding\": \"^2.15.0\",\n \"ready-resource\": \"^1.0.0\",\n \"refcounter\": \"^1.0.0\",\n \"require-addon\": \"^1.0.2\",\n \"resolve-reject-promise\": \"^1.1.0\",\n \"signal-promise\": \"^1.0.3\",\n \"streamx\": \"^2.16.1\"\n },\n \"devDependencies\": {\n \"bare-compat-napi\": \"^1.3.0\",\n \"bare-crypto\": \"^1.12.0\",\n \"bare-fs\": \"^4.5.0\",\n \"bare-path\": \"^3.0.0\",\n \"brittle\": \"^3.5.0\",\n \"cmake-bare\": \"^1.1.14\",\n \"cmake-fetch\": \"^1.0.1\",\n \"cmake-napi\": \"^1.0.6\",\n \"fd-lock\": \"^2.0.0\",\n \"lunte\": \"^1.3.0\",\n \"paparam\": \"^1.9.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\"\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}\nmodule.exports = sameData\n\nfunction type (o) {\n const t = typeof o\n\n return t === 'object'\n ? Array.isArray(o)\n ? 'array'\n : isTypedArray(o)\n ? (typeof o.equals === 'function') ? 'buffer' : 'array'\n : (o === null ? 'null' : 'object')\n : t\n}\n\nfunction isTypedArray (a) {\n return !!a && typeof a.length === 'number' && ArrayBuffer.isView(a.array)\n}\n\nfunction sameData (a, b) {\n if (a === b) return true\n\n const ta = type(a)\n const tb = type(b)\n\n if (ta !== tb) return false\n\n if (ta === 'buffer') return a.equals(b)\n\n if (ta === 'array') {\n if (a.length !== b.length) return false\n\n for (let i = 0; i < a.length; i++) {\n if (!sameData(a[i], b[i])) return false\n }\n\n return true\n }\n\n if (ta !== 'object') return false\n\n const ea = Object.entries(a)\n const eb = Object.entries(b)\n\n if (ea.length !== eb.length) return false\n\n ea.sort(cmp)\n eb.sort(cmp)\n\n for (let i = 0; i < ea.length; i++) {\n if (ea[i][0] !== eb[i][0] || !sameData(ea[i][1], eb[i][1])) return false\n }\n\n return true\n}\n\nfunction cmp (a, b) {\n return a[0] === b[0] ? 0 : a[0] < b[0] ? -1 : 1\n}\n{\n \"name\": \"same-data\",\n \"version\": \"1.0.0\",\n \"description\": \"Deep equal with no deps and only for 'data' objects, ie basic objects, arrays, primitives and typed arrays\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test.js\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.1\",\n \"standard\": \"^17.0.0\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/mafintosh/same-data.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/same-data/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/same-data\"\n}\nlet tmpResolve = null\n\nmodule.exports = class ScopeLock {\n constructor ({ debounce = false } = {}) {\n this.debounce = debounce\n this.waiting = []\n this.locked = false\n this.skip = 0\n this.destroyed = false\n }\n\n flush () {\n if (this.locked === false && this.waiting.length === 0) return Promise.resolve(this.destroyed === false)\n\n const promise = new Promise(setTmpResolve)\n const resolve = tmpResolve\n\n tmpResolve = null\n this.waiting.push({ lock: false, resolve })\n\n return promise\n }\n\n destroy () {\n this.destroyed = true\n }\n\n lock () {\n const promise = new Promise(setTmpResolve)\n const resolve = tmpResolve\n\n tmpResolve = null\n\n if (this.locked === true) {\n this.waiting.push({ lock: true, resolve })\n return promise\n }\n\n if (this.destroyed === true) {\n resolve(false)\n return promise\n }\n\n this.locked = true\n resolve(true)\n\n return promise\n }\n\n unlock () {\n if (this.destroyed === true) {\n for (let i = 0; i < this.waiting.length; i++) {\n this.waiting[i].resolve(false)\n }\n this.waiting = []\n this.skip = 0\n this.locked = false\n return\n }\n\n if (this.skip !== 0) {\n for (let i = 0; i < this.skip; i++) {\n const { lock, resolve } = this.waiting[i]\n resolve(lock === false)\n }\n\n this.waiting = this.waiting.slice(this.skip)\n this.skip = 0\n }\n\n while (this.waiting.length > 0 && this.waiting[0].lock === false) {\n this.waiting.shift().resolve(true)\n }\n\n if (this.waiting.length === 0) {\n this.locked = false\n return\n }\n\n const { resolve } = this.waiting.shift()\n if (this.debounce === true) this.skip = this.waiting.length\n\n resolve(true)\n }\n}\n\nfunction setTmpResolve (resolve) {\n tmpResolve = resolve\n}\n{\n \"name\": \"scope-lock\",\n \"version\": \"1.2.4\",\n \"description\": \"Some concurrency semantics around entering scopes\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\"\n ],\n \"devDependencies\": {\n \"brittle\": \"^3.7.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/holepunchto/scope-lock.git\"\n },\n \"author\": \"Holepunch Inc.\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/scope-lock/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/scope-lock\"\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}\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}\nvar varint = require('varint')\nexports.encode = function encode (v, b, o) {\n v = v >= 0 ? v*2 : v*-2 - 1\n var r = varint.encode(v, b, o)\n encode.bytes = varint.encode.bytes\n return r\n}\nexports.decode = function decode (b, o) {\n var v = varint.decode(b, o)\n decode.bytes = varint.decode.bytes\n return v & 1 ? (v+1) / -2 : v / 2\n}\n\nexports.encodingLength = function (v) {\n return varint.encodingLength(v >= 0 ? v*2 : v*-2 - 1)\n}\n{\n \"name\": \"signed-varint\",\n \"description\": \"efficiently store signed integers in varint\",\n \"version\": \"2.0.1\",\n \"homepage\": \"https://github.com/dominictarr/signed-varint\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/dominictarr/signed-varint.git\"\n },\n \"dependencies\": {\n \"varint\": \"~5.0.0\"\n },\n \"devDependencies\": {\n \"tape\": \"~2.12.3\"\n },\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"author\": \"Dominic Tarr <dominic.tarr@gmail.com> (http://dominictarr.com)\",\n \"license\": \"MIT\"\n}\nrequire.addon = require('require-addon')\n\nmodule.exports = require.addon('.', __filename)\nconst binding = require('./binding')\nconst b4a = require('b4a')\n\nfunction predicate(u8, u16, u32) {\n return function predicate(buf) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n const n = buf.BYTES_PER_ELEMENT\n\n if (n === 1) return u8(buf)\n if (n === 2) return u16(buf)\n return u32(buf)\n }\n}\n\nfunction unary(u8, u16, u32) {\n return function unary(buf, result = b4a.allocUnsafe(buf.byteLength)) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n if (buf.byteLength !== result.byteLength) {\n throw new Error('Length of result buffer is insufficient')\n }\n\n const n = buf.BYTES_PER_ELEMENT\n\n if (n === 1) u8(buf, result)\n else if (n === 2) u16(buf, result)\n else u32(buf, result)\n\n return result\n }\n}\n\nfunction binary(u8, u16, u32) {\n return function binary(a, b, result = b4a.allocUnsafe(a.byteLength)) {\n if (a.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n if (a.byteLength !== b.byteLength || a.byteLength !== result.byteLength) {\n throw new Error('Buffers must be the same length')\n }\n\n const n = a.BYTES_PER_ELEMENT\n\n if (n === 1) u8(a, b, result)\n else if (n === 2) u16(a, b, result)\n else u32(a, b, result)\n\n return result\n }\n}\n\nfunction reduce(u8, u16, u32) {\n return function reduce(buf) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n const n = buf.BYTES_PER_ELEMENT\n\n if (n === 1) return u8(buf)\n if (n === 2) return u16(buf)\n return u32(buf)\n }\n}\n\nexports.allo = predicate(\n binding.simdle_native_allo_v128_u8,\n binding.simdle_native_allo_v128_u16,\n binding.simdle_native_allo_v128_u32\n)\n\nexports.allz = predicate(\n binding.simdle_native_allz_v128_u8,\n binding.simdle_native_allz_v128_u16,\n binding.simdle_native_allz_v128_u32\n)\n\nexports.and = binary(\n binding.simdle_native_and_v128_u8,\n binding.simdle_native_and_v128_u16,\n binding.simdle_native_and_v128_u32\n)\n\nexports.clear = binary(\n binding.simdle_native_clear_v128_u8,\n binding.simdle_native_clear_v128_u16,\n binding.simdle_native_clear_v128_u32\n)\n\nexports.clo = unary(\n binding.simdle_native_clo_v128_u8,\n binding.simdle_native_clo_v128_u16,\n binding.simdle_native_clo_v128_u32\n)\n\nexports.clz = unary(\n binding.simdle_native_clz_v128_u8,\n binding.simdle_native_clz_v128_u16,\n binding.simdle_native_clz_v128_u32\n)\n\nexports.cnt = unary(\n binding.simdle_native_cnt_v128_u8,\n binding.simdle_native_cnt_v128_u16,\n binding.simdle_native_cnt_v128_u32\n)\n\nexports.cto = unary(\n binding.simdle_native_cto_v128_u8,\n binding.simdle_native_cto_v128_u16,\n binding.simdle_native_cto_v128_u32\n)\n\nexports.ctz = unary(\n binding.simdle_native_ctz_v128_u8,\n binding.simdle_native_ctz_v128_u16,\n binding.simdle_native_ctz_v128_u32\n)\n\nexports.not = unary(\n binding.simdle_native_not_v128_u8,\n binding.simdle_native_not_v128_u16,\n binding.simdle_native_not_v128_u32\n)\n\nexports.or = binary(\n binding.simdle_native_or_v128_u8,\n binding.simdle_native_or_v128_u16,\n binding.simdle_native_or_v128_u32\n)\n\nexports.sum = reduce(\n binding.simdle_native_sum_v128_u8,\n binding.simdle_native_sum_v128_u16,\n binding.simdle_native_sum_v128_u32\n)\n\nexports.xor = binary(\n binding.simdle_native_xor_v128_u8,\n binding.simdle_native_xor_v128_u16,\n binding.simdle_native_xor_v128_u32\n)\n{\n \"name\": \"simdle-native\",\n \"version\": \"1.3.9\",\n \"description\": \"libsimdle JavaScript bindings for Node.js\",\n \"main\": \"index.js\",\n \"files\": [\n \"index.js\",\n \"binding.cc\",\n \"binding.js\",\n \"CMakeLists.txt\",\n \"prebuilds\"\n ],\n \"addon\": true,\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check .\",\n \"test\": \"npm run lint && npm run test:bare && npm run test:node\",\n \"test:bare\": \"bare test.mjs\",\n \"test:node\": \"node test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/simdle-native.git\"\n },\n \"author\": \"Holepunch\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/simdle-native/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/simdle-native#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.6.0\",\n \"require-addon\": \"^1.1.0\"\n },\n \"devDependencies\": {\n \"bare-compat-napi\": \"^1.3.2\",\n \"brittle\": \"^3.1.0\",\n \"cmake-bare\": \"^1.1.7\",\n \"cmake-fetch\": \"^1.1.0\",\n \"cmake-napi\": \"^1.0.2\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\n }\n}\nconst b4a = require('b4a')\nconst scalar = require('./scalar')\n\nfunction view (buf, n) {\n if (n === buf.BYTES_PER_ELEMENT) return buf\n\n let TypedArray\n\n if (n === 1) TypedArray = Uint8Array\n else if (n === 2) TypedArray = Uint16Array\n else TypedArray = Uint32Array\n\n return new TypedArray(buf.buffer, buf.byteOffset, buf.byteLength / n)\n}\n\nfunction unary (u8, u16 = u8, u32 = u16) {\n return function unary (buf, result = b4a.allocUnsafe(buf.byteLength)) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n if (buf.byteLength !== result.byteLength) {\n throw new Error('Length of result buffer is insufficient')\n }\n\n const n = buf.BYTES_PER_ELEMENT\n\n if (n === 1) u8(buf, view(result, n))\n else if (n === 2) u16(buf, view(result, n))\n else u32(buf, view(result, n))\n\n return result\n }\n}\n\nfunction binary (u8, u16 = u8, u32 = u16) {\n return function binary (a, b, result = b4a.allocUnsafe(a.byteLength)) {\n if (a.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n if (a.byteLength !== b.byteLength || a.byteLength !== result.byteLength) {\n throw new Error('Buffers must be the same length')\n }\n\n const n = a.BYTES_PER_ELEMENT\n\n if (n === 1) u8(a, b, view(result, n))\n else if (n === 2) u16(a, b, view(result, n))\n else u32(a, b, view(result, n))\n\n return result\n }\n}\n\nfunction reduce (u8, u16 = u8, u32 = u16) {\n return function reduce (buf) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n const n = buf.BYTES_PER_ELEMENT\n\n if (n === 1) return u8(buf)\n if (n === 2) return u16(buf)\n return u32(buf)\n }\n}\n\nexports.allo = function allo (buf) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n const m = 2 ** (buf.BYTES_PER_ELEMENT * 8) - 1\n\n for (let i = 0, n = buf.length; i < n; i++) {\n if (buf[i] !== m) return false\n }\n\n return true\n}\n\nexports.allz = function allz (buf) {\n if (buf.byteLength % 16 !== 0) {\n throw new Error('Buffer length must be a multiple of 16')\n }\n\n for (let i = 0, n = buf.length; i < n; i++) {\n if (buf[i] !== 0) return false\n }\n\n return true\n}\n\nexports.and = binary(\n (a, b, result) => {\n for (let i = 0, n = result.length; i < n; i++) {\n result[i] = a[i] & b[i]\n }\n }\n)\n\nexports.clear = binary(\n (a, b, result) => {\n for (let i = 0, n = result.length; i < n; i++) {\n result[i] = a[i] & ~b[i]\n }\n }\n)\n\nexports.clo = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = 24 - scalar.clo(buf[i])\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = 16 - scalar.clo(buf[i])\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.clo(buf[i])\n }\n }\n)\n\nexports.clz = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = 24 - scalar.clz(buf[i])\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = 16 - scalar.clz(buf[i])\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.clz(buf[i])\n }\n }\n)\n\nexports.cnt = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.cnt(buf[i]) & 0xff\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.cnt(buf[i]) & 0xffff\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.cnt(buf[i])\n }\n }\n)\n\nexports.cto = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = Math.min(scalar.cto(buf[i]), 8)\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = Math.min(scalar.cto(buf[i]), 16)\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.cto(buf[i])\n }\n }\n)\n\nexports.ctz = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = Math.min(scalar.ctz(buf[i]), 8)\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = Math.min(scalar.ctz(buf[i]), 16)\n }\n },\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = scalar.ctz(buf[i])\n }\n }\n)\n\nexports.not = unary(\n (buf, result) => {\n for (let i = 0, n = buf.length; i < n; i++) {\n result[i] = ~buf[i]\n }\n }\n)\n\nexports.or = binary(\n (a, b, result) => {\n for (let i = 0, n = result.length; i < n; i++) {\n result[i] = a[i] | b[i]\n }\n }\n)\n\nexports.sum = reduce(\n (buf) => {\n let result = 0n\n\n for (let i = 0, n = buf.length; i < n; i++) {\n result += BigInt(buf[i])\n }\n\n return result\n }\n)\n\nexports.xor = binary(\n (a, b, result) => {\n for (let i = 0, n = result.length; i < n; i++) {\n result[i] = a[i] ^ b[i]\n }\n }\n)\ntry {\n module.exports = require('simdle-native')\n} catch {\n module.exports = require('./fallback')\n}\n{\n \"name\": \"simdle-universal\",\n \"version\": \"1.1.2\",\n \"description\": \"Universal wrapper for libsimdle with a JavaScript fallback\",\n \"main\": \"index.js\",\n \"files\": [\n \"fallback.js\",\n \"index.js\",\n \"scalar.js\"\n ],\n \"browser\": {\n \"./index.js\": \"./fallback.js\"\n },\n \"scripts\": {\n \"test\": \"standard && brittle test.mjs\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/simdle-universal.git\"\n },\n \"author\": \"Kasper Isager Dalsgarð <kasper@funktionel.co>\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/simdle-universal/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/simdle-universal#readme\",\n \"dependencies\": {\n \"b4a\": \"^1.6.0\"\n },\n \"optionalDependencies\": {\n \"simdle-native\": \"^1.1.1\"\n },\n \"devDependencies\": {\n \"brittle\": \"^3.1.0\",\n \"standard\": \"^17.0.0\"\n }\n}\nconst clz = exports.clz = function clz (n) {\n return Math.clz32(n)\n}\n\nexports.clo = function clo (n) {\n return clz(~n)\n}\n\nconst ctz = exports.ctz = function ctz (n) {\n return 32 - (n === 0 ? 0 : (clz(n & -n) + 1))\n}\n\nexports.cto = function cto (n) {\n return ctz(~n)\n}\n\nexports.cnt = function cnt (n) {\n n = n - ((n >>> 1) & 0x55555555)\n n = (n & 0x33333333) + ((n >>> 2) & 0x33333333)\n n = (n + (n >>> 4)) & 0x0f0f0f0f\n n = (n * 0x01010101) >>> 24\n return 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(buffer.buffer, buffer.byteOffset, buffer.byteLength)\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)\n 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)\n 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)\n throw new Error('buffers must be of same length\"')\n binding.sodium_sub(a, b)\n}\n\nexports.sodium_compare = function (a, b) {\n if (a?.byteLength !== b?.byteLength)\n throw new Error('buffers must be of same length\"')\n return binding.sodium_compare(a, b)\n}\n\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)\n throw new Error('invalid length')\n\n return binding.sodium_is_zero(buffer, length)\n}\n\nexports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) {\n if (unpaddedBuflen > buffer.byteLength)\n throw new Error('unpadded length cannot exceed buffer length')\n if (blockSize > buffer.byteLength)\n 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 (\n buffer?.byteLength <\n unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize))\n )\n throw new Error('buf not long enough')\n\n return binding.sodium_pad(buffer, unpaddedBuflen, blockSize)\n}\n\nexports.sodium_unpad = function (buffer, paddedBuflen, blockSize) {\n if (paddedBuflen > buffer.byteLength)\n throw new Error('unpadded length cannot exceed buffer length')\n if (blockSize > buffer.byteLength)\n 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)\n 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)\n 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)\n throw new Error('sm must be \"m.byteLength + crypto_sign_BYTES\" bytes')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES)\n 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)\n throw new Error('m must be \"sm.byteLength - crypto_sign_BYTES\" bytes')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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\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)\n throw new Error('m must be \"sm.byteLength - crypto_sign_BYTES\" bytes')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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)\n 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\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)\n throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES)\n 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)\n throw new Error('x25519_pk')\n if (ed25519pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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)\n throw new Error('x25519_sk')\n\n const edLen = ed25519sk.byteLength\n\n if (\n edLen !== binding.crypto_sign_SECRETKEYBYTES &&\n edLen !== binding.crypto_box_SECRETKEYBYTES\n ) {\n throw new Error(\n \"ed25519_sk should either be 'crypto_sign_SECRETKEYBYTES' bytes or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes\"\n )\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)\n throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_box_SECRETKEYBYTES)\n 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)\n throw new Error('pk')\n if (sk?.byteLength !== binding.crypto_box_SECRETKEYBYTES)\n 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\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)\n throw new Error(\n 'c must be \"m.byteLength + crypto_secretbox_MACBYTES\" bytes'\n )\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES)\n 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\nexports.crypto_secretbox_open_easy = function (m, c, n, k) {\n if (m?.byteLength !== c.byteLength - binding.crypto_secretbox_MACBYTES)\n 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)\n 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)\n throw new Error('c must \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_secretbox_MACBYTES)\n throw new Error('mac')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES)\n 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\nexports.crypto_secretbox_open_detached = function (m, c, mac, n, k) {\n if (m?.byteLength !== c.byteLength)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_secretbox_MACBYTES)\n throw new Error('mac')\n if (n?.byteLength !== binding.crypto_secretbox_NONCEBYTES)\n 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 const res = binding.crypto_generichash_batch(\n output,\n batch,\n !!key,\n key || OPTIONAL\n )\n if (res !== 0) throw new Error('status: ' + res)\n } else {\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,\n key.byteOffset,\n 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(\n k.buffer,\n k.byteOffset,\n k.byteLength\n )\n}\n\nexports.crypto_secretstream_xchacha20poly1305_init_push = function (\n state,\n header,\n k\n) {\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 (\n state,\n header,\n k\n) {\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\nexports.crypto_secretstream_xchacha20poly1305_push = function (\n state,\n c,\n m,\n ad,\n tag\n) {\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\nexports.crypto_secretstream_xchacha20poly1305_pull = function (\n state,\n m,\n tag,\n c,\n ad\n) {\n ad ||= OPTIONAL\n\n if (c?.byteLength < binding.crypto_secretstream_xchacha20poly1305_ABYTES)\n throw new Error('invalid cipher length')\n if (\n m?.byteLength !==\n c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES\n )\n 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(\n state.buffer,\n state.byteOffset,\n state.byteLength\n )\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)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES)\n 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)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES)\n 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)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES)\n 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)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES)\n 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)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES)\n 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\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)\n throw new Error('out')\n if (k?.byteLength !== binding.crypto_onetimeauth_KEYBYTES)\n 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)\n throw new Error(\"state must be 'crypto_onetimeauth_STATEBYTES' bytes\")\n if (k?.byteLength !== binding.crypto_onetimeauth_KEYBYTES)\n 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)\n 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)\n throw new Error(\"state must be 'crypto_onetimeauth_STATEBYTES' bytes\")\n if (out?.byteLength !== binding.crypto_onetimeauth_BYTES)\n 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\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)\n 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)\n 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)\n 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\nexports.crypto_pwhash_async = function (\n out,\n passwd,\n salt,\n opslimit,\n memlimit,\n alg,\n callback = undefined\n) {\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)\n 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)\n 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\nexports.crypto_pwhash_str_async = function (\n out,\n passwd,\n opslimit,\n memlimit,\n callback = undefined\n) {\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\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\nexports.crypto_pwhash_str_verify_async = function (\n str,\n passwd,\n callback = undefined\n) {\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\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 (\n out,\n passwd,\n salt,\n opslimit,\n memlimit\n) {\n if (out?.byteLength < binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN)\n throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX)\n throw new Error('out')\n if (salt?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES)\n throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN)\n throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX)\n throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN)\n throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX)\n throw new Error('memlimit')\n\n const res = binding.crypto_pwhash_scryptsalsa208sha256(\n out,\n passwd,\n salt,\n opslimit,\n memlimit\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_pwhash_scryptsalsa208sha256_async = function (\n out,\n passwd,\n salt,\n opslimit,\n memlimit,\n callback = undefined\n) {\n if (out?.byteLength < binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN)\n throw new Error('out')\n if (out?.byteLength > binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX)\n throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (salt?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES)\n throw new Error('salt')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN)\n throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX)\n throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN)\n throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX)\n 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\nexports.crypto_pwhash_scryptsalsa208sha256_str_async = function (\n out,\n passwd,\n opslimit,\n memlimit,\n callback = undefined\n) {\n if (out?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES)\n throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN)\n throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX)\n throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN)\n throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX)\n 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 (\n out,\n passwd,\n opslimit,\n memlimit\n) {\n if (out?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES)\n throw new Error('out')\n if (!passwd?.byteLength) throw new Error('passwd')\n if (opslimit < binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN)\n throw new Error('opslimit')\n if (opslimit > binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX)\n throw new Error('opslimit')\n if (memlimit < binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN)\n throw new Error('memlimit')\n if (memlimit > binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX)\n throw new Error('memlimit')\n\n const res = binding.crypto_pwhash_scryptsalsa208sha256_str(\n out,\n passwd,\n opslimit,\n memlimit\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function (\n str,\n passwd,\n callback = undefined\n) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES)\n 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\nexports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES)\n 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\nexports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (\n str,\n opslimit,\n memlimit\n) {\n if (str?.byteLength !== binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES)\n 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(\n str,\n opslimit,\n memlimit\n )\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 (\n rx,\n tx,\n clientPk,\n clientSk,\n serverPk\n) {\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)\n throw new Error(\n 'receiving key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null'\n )\n } else {\n if (tx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES)\n throw new Error(\n 'transmitting key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null'\n )\n }\n\n if (clientPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES)\n throw new Error('client_pk')\n if (clientSk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES)\n throw new Error('client_sk')\n if (serverPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES)\n throw new Error('server_pk')\n\n const res = binding.crypto_kx_client_session_keys(\n rx,\n tx,\n clientPk,\n clientSk,\n serverPk\n )\n\n if (res !== 0) throw new Error('status: ' + res)\n}\n\nexports.crypto_kx_server_session_keys = function (\n rx,\n tx,\n serverPk,\n serverSk,\n clientPk\n) {\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)\n throw new Error(\n 'receiving key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null'\n )\n } else {\n if (tx?.byteLength !== binding.crypto_kx_SESSIONKEYBYTES)\n throw new Error(\n 'transmitting key buffer must be \"crypto_kx_SESSIONKEYBYTES\" bytes or null'\n )\n }\n\n if (serverPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES)\n throw new Error('server_pk')\n if (serverSk?.byteLength !== binding.crypto_kx_SECRETKEYBYTES)\n throw new Error('server_sk')\n if (clientPk?.byteLength !== binding.crypto_kx_PUBLICKEYBYTES)\n throw new Error('client_pk')\n\n const res = binding.crypto_kx_server_session_keys(\n rx,\n tx,\n serverPk,\n serverSk,\n clientPk\n )\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)\n 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)\n 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)\n throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES)\n 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)\n throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES)\n throw new Error('n')\n if (p?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES)\n 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\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)\n 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)\n throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES)\n 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)\n throw new Error('q')\n if (n?.byteLength !== binding.crypto_scalarmult_ed25519_SCALARBYTES)\n throw new Error('n')\n if (p?.byteLength !== binding.crypto_scalarmult_ed25519_BYTES)\n 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)\n 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)\n throw new Error('r')\n if (s?.byteLength !== binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES)\n 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)\n throw new Error('recip')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n 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)\n throw new Error('neg')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n 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)\n throw new Error('comp')\n if (s?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n 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)\n throw new Error('z')\n if (x?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n throw new Error('x')\n if (y?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n 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)\n throw new Error('z')\n if (x?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n throw new Error('x')\n if (y?.byteLength !== binding.crypto_core_ed25519_SCALARBYTES)\n 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)\n throw new Error('subkey')\n if (subkey?.byteLength > binding.crypto_kdf_BYTES_MAX)\n throw new Error('subkey')\n if (ctx?.byteLength !== binding.crypto_kdf_CONTEXTBYTES)\n 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)\n 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)\n 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)\n 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)\n 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)\n throw new Error('k')\n\n binding.crypto_aead_xchacha20poly1305_ietf_keygen(k)\n}\n\nexports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (\n c,\n m,\n ad,\n nsec = null,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (\n c?.byteLength !==\n m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES\n )\n throw new Error(\n 'c must \"m.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES\" bytes'\n )\n if (c?.byteLength > 0xffffffff)\n throw new Error('c.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(\n c,\n m,\n ad,\n npub,\n k\n )\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\nexports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (\n m,\n nsec = null,\n c,\n ad,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (\n m?.byteLength !==\n c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES\n )\n throw new Error(\n 'm must \"c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES\" bytes'\n )\n if (m?.byteLength > 0xffffffff)\n throw new Error('m.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(\n m,\n c,\n ad,\n npub,\n k\n )\n if (res < 0) throw new Error('could not verify data')\n\n return res\n}\n\nexports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function (\n c,\n mac,\n m,\n ad,\n nsec = null,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength)\n throw new Error('c must be \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_ABYTES)\n throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(\n c,\n mac,\n m,\n ad,\n npub,\n k\n )\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 (\n m,\n nsec = null,\n c,\n mac,\n ad,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_ABYTES)\n throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(\n m,\n c,\n mac,\n ad,\n npub,\n k\n )\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)\n throw new Error('k')\n\n binding.crypto_aead_chacha20poly1305_ietf_keygen(k)\n}\n\nexports.crypto_aead_chacha20poly1305_ietf_encrypt = function (\n c,\n m,\n ad,\n nsec = null,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (\n c?.byteLength !==\n m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES\n )\n throw new Error(\n 'c must \"m.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES\" bytes'\n )\n if (c?.byteLength > 0xffffffff)\n throw new Error('c.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(\n c,\n m,\n ad,\n npub,\n k\n )\n if (res < 0) throw new Error('could not encrypt data')\n\n return res\n}\n\nexports.crypto_aead_chacha20poly1305_ietf_decrypt = function (\n m,\n nsec = null,\n c,\n ad,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (\n m?.byteLength !==\n c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES\n )\n throw new Error(\n 'm must \"c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES\" bytes'\n )\n if (m?.byteLength > 0xffffffff)\n throw new Error('m.byteLength must be a 32bit integer')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(\n m,\n c,\n ad,\n npub,\n k\n )\n if (res < 0) throw new Error('could not verify data')\n\n return res\n}\n\nexports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function (\n c,\n mac,\n m,\n ad,\n nsec = null,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (c?.byteLength !== m.byteLength)\n throw new Error('c must be \"m.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_ABYTES)\n throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(\n c,\n mac,\n m,\n ad,\n npub,\n k\n )\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 (\n m,\n nsec = null,\n c,\n mac,\n ad,\n npub,\n k\n) {\n ad ??= undefined\n if (nsec !== null) throw new Error('nsec must always be set to null')\n if (m?.byteLength !== c.byteLength)\n throw new Error('m must be \"c.byteLength\" bytes')\n if (mac?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_ABYTES)\n throw new Error('mac')\n if (npub?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES)\n throw new Error('npub')\n if (k?.byteLength !== binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES)\n throw new Error('k')\n\n const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(\n m,\n c,\n mac,\n ad,\n npub,\n k\n )\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)\n 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(\n \"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\"\n )\n }\n if (n?.byteLength !== binding.crypto_stream_chacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_KEYBYTES)\n 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(\n \"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\"\n )\n }\n if (c?.byteLength !== m.byteLength)\n 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(\n \"state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes\"\n )\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 (\n state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES\n ) {\n throw new Error(\n \"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\"\n )\n }\n if (n?.byteLength !== binding.crypto_stream_chacha20_ietf_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_chacha20_ietf_KEYBYTES)\n 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 (\n state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES\n ) {\n throw new Error(\n \"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\"\n )\n }\n if (c?.byteLength !== m.byteLength)\n 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 (\n state?.byteLength !== binding.crypto_stream_chacha20_ietf_xor_STATEBYTES\n ) {\n throw new Error(\n \"state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes\"\n )\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(\n \"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\"\n )\n }\n if (n?.byteLength !== binding.crypto_stream_xchacha20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_xchacha20_KEYBYTES)\n 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(\n \"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\"\n )\n }\n if (c?.byteLength !== m.byteLength)\n 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(\n \"state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes\"\n )\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(\n \"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\"\n )\n }\n if (n?.byteLength !== binding.crypto_stream_salsa20_NONCEBYTES)\n throw new Error('n')\n if (k?.byteLength !== binding.crypto_stream_salsa20_KEYBYTES)\n 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(\n \"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\"\n )\n }\n if (c?.byteLength !== m.byteLength)\n 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(\n \"state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes\"\n )\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)\n throw new Error('n')\n if (p?.byteLength !== binding.extension_tweak_ed25519_BYTES)\n 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)\n throw new Error('scalar')\n if (pk && pk.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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)\n throw new Error('n')\n if (sk?.byteLength !== binding.crypto_sign_SECRETKEYBYTES)\n 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)\n throw new Error('scalar_out')\n if (scalar?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n 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)\n throw new Error('tpk')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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 (\n pk,\n scalarOut,\n scalarIn,\n ns\n) {\n if (pk?.byteLength !== binding.extension_tweak_ed25519_BYTES)\n throw new Error('pk')\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n throw new Error('scalar_out')\n if (scalarIn?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n 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)\n throw new Error('scalar_out')\n if (scalar?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n throw new Error('scalar')\n if (n?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n 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)\n throw new Error('tpk')\n if (pk?.byteLength !== binding.crypto_sign_PUBLICKEYBYTES)\n 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 (\n pk,\n scalarOut,\n scalarIn,\n tweak\n) {\n if (pk?.byteLength !== binding.extension_tweak_ed25519_BYTES)\n throw new Error('pk')\n if (scalarOut?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n throw new Error('scalar_out')\n if (scalarIn?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n throw new Error('scalar_in')\n if (tweak?.byteLength !== binding.extension_tweak_ed25519_SCALARBYTES)\n throw new Error('tweak')\n\n const res = binding.extension_tweak_ed25519_keypair_add(\n pk,\n scalarOut,\n scalarIn,\n tweak\n )\n if (res !== 0) throw new Error('failed to add tweak to keypair')\n}\n\nexports.extension_pbkdf2_sha512_async = function (\n out,\n passwd,\n salt,\n iter,\n outlen,\n callback = undefined\n) {\n if (iter < binding.extension_pbkdf2_sha512_ITERATIONS_MIN)\n throw new Error('iterations')\n if (outlen > binding.extension_pbkdf2_sha512_BYTES_MAX)\n 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)\n throw new Error('iterations')\n if (outlen > binding.extension_pbkdf2_sha512_BYTES_MAX)\n 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\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.10\",\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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.0.0\"\n },\n \"scripts\": {\n \"test\": \"prettier . --check && 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 <github@tixz.dk> (http://bayes.dk)\",\n \"Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)\",\n \"Christophe Diederichs <chm-diederichs@hyperdivision.dk>\"\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}\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}\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 },\n \"browserify\": {\n \"transform\": [\n \"./build-scripts/transform.js\"\n ]\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 <github@tixz.dk> (http://bayes.dk)\",\n \"Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)\",\n \"Christophe Diederichs <chm-diederichs@hyperdivision.dk>\"\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}\nvar tick = 1\nvar maxTick = 65535\nvar resolution = 4\nvar timer\nvar inc = function () {\n tick = (tick + 1) & maxTick\n}\n\n\nmodule.exports = function (seconds) {\n if (!timer) {\n timer = setInterval(inc, (1000 / resolution) | 0)\n if (timer.unref) timer.unref()\n }\n\n var size = resolution * (seconds || 5)\n var buffer = [0]\n var pointer = 1\n var last = (tick - 1) & maxTick\n\n return function (delta) {\n var dist = (tick - last) & maxTick\n if (dist > size) dist = size\n last = tick\n\n while (dist--) {\n if (pointer === size) pointer = 0\n buffer[pointer] = buffer[pointer === 0 ? size - 1 : pointer - 1]\n pointer++\n }\n\n if (delta) buffer[pointer - 1] += delta\n\n var top = buffer[pointer - 1]\n var btm = buffer.length < size ? 0 : buffer[pointer === size ? 0 : pointer]\n\n return buffer.length < resolution ? top : (top - btm) * resolution / buffer.length\n }\n}\n{\n \"name\": \"speedometer\",\n \"version\": \"1.1.0\",\n \"repository\": \"git://github.com/mafintosh/speedometer\",\n \"description\": \"simple speed measurement in javascript\",\n \"license\": \"MIT\",\n \"keywords\": [\n \"speed\",\n \"bytes\",\n \"per\",\n \"second\",\n \"transfer\"\n ],\n \"author\": \"Mathias Buus Madsen <mathiasbuus@gmail.com>\"\n}\nconst { EventEmitter } = require('events-universal')\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.23.0\",\n \"description\": \"An iteration of the Node.js core streams with a series of improvements\",\n \"main\": \"index.js\",\n \"dependencies\": {\n \"events-universal\": \"^1.0.0\",\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 \"files\": [\n \"index.js\"\n ],\n \"scripts\": {\n \"test\": \"standard && node test/all.js\",\n \"test:bare\": \"standard && bare test/all.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}\nconst codecs = require('codecs')\nconst b = require('b4a')\n\nconst SEP = b.alloc(1)\nconst SEP_BUMPED = b.from([0x1])\nconst EMPTY = b.alloc(0)\n\nmodule.exports = class SubEncoder {\n constructor (prefix, encoding, parent = null) {\n this.userEncoding = codecs(encoding)\n this.prefix = prefix != null ? createPrefix(prefix, parent) : null\n this.lt = this.prefix && b.concat([this.prefix.subarray(0, this.prefix.byteLength - 1), SEP_BUMPED])\n }\n\n _encodeRangeUser (r) {\n if (this.userEncoding.encodeRange) return this.userEncoding.encodeRange(r)\n\n const res = {}\n if (r.gt != null) res.gt = this.userEncoding.encode(r.gt)\n if (r.gte != null) res.gte = this.userEncoding.encode(r.gte)\n if (r.lte != null) res.lte = this.userEncoding.encode(r.lte)\n if (r.lt != null) res.lt = this.userEncoding.encode(r.lt)\n\n return res\n }\n\n _addPrefix (key) {\n return this.prefix ? b.concat([this.prefix, key]) : key\n }\n\n encode (key) {\n return this._addPrefix(this.userEncoding.encode(key))\n }\n\n encodeRange (range) {\n const r = this._encodeRangeUser(range)\n\n if (r.gt) r.gt = this._addPrefix(r.gt)\n else if (r.gte) r.gte = this._addPrefix(r.gte)\n else if (this.prefix) r.gte = this.prefix\n\n if (r.lt) r.lt = this._addPrefix(r.lt)\n else if (r.lte) r.lte = this._addPrefix(r.lte)\n else if (this.prefix) r.lt = this.lt\n\n return r\n }\n\n decode (key) {\n return this.userEncoding.decode(this.prefix ? key.subarray(this.prefix.byteLength) : key)\n }\n\n sub (prefix, encoding) {\n return new SubEncoder(prefix || EMPTY, compat(encoding), this.prefix)\n }\n}\n\nfunction createPrefix (prefix, parent) {\n prefix = typeof prefix === 'string' ? b.from(prefix) : prefix\n\n if (prefix && parent) return b.concat([parent, prefix, SEP])\n if (prefix) return b.concat([prefix, SEP])\n if (parent) return b.concat([parent, SEP])\n return SEP\n}\n\nfunction compat (enc) {\n if (enc && enc.keyEncoding) return enc.keyEncoding\n return enc\n}\n{\n \"name\": \"sub-encoder\",\n \"version\": \"2.1.3\",\n \"description\": \"Generate sub encodings for key/value stores\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"standard && brittle test/*.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/holepunchto/sub-encoder.git\"\n },\n \"keywords\": [\n \"kv-store\",\n \"encoding\",\n \"hyperbee\"\n ],\n \"author\": \"Andrew Osheroff <andrewosh@gmail.com>\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/sub-encoder/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/sub-encoder#readme\",\n \"devDependencies\": {\n \"brittle\": \"^3.1.1\",\n \"compact-encoding\": \"^2.12.0\",\n \"hyperbee\": \"^2.11.0\",\n \"hypercore\": \"^10.3.2\",\n \"index-encoder\": \"^3.0.0\",\n \"random-access-memory\": \"^6.0.0\",\n \"standard\": \"^17.0.0\"\n },\n \"dependencies\": {\n \"b4a\": \"^1.6.0\",\n \"codecs\": \"^3.1.0\"\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 (\n let i = Math.max(0, data.byteLength - 4), n = data.byteLength;\n i < n && isBoundary;\n i++\n ) {\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 this.bytesSeen = 0\n\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 i--\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.6\",\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/browser-decoder.js\",\n \"./lib/utf8-decoder.js\": \"./lib/browser-decoder.js\"\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 },\n \"scripts\": {\n \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check . && lunte\",\n \"test\": \"npm run test:node && npm run test:bare\",\n \"test:node\": \"brittle-node test.js\",\n \"test:bare\": \"brittle-bare 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 \"lunte\": \"^1.3.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^1.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}\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\": \"./browser.js\",\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}\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 '^(' +\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)\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.alloc(binding.sizeof_udx_napi_interface_event_t)\n this._watching = false\n this._destroying = null\n\n binding.udx_napi_interface_event_init(\n udx._handle,\n this._handle,\n 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.alloc(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(\n this._handle.buffer,\n this._handle.byteOffset,\n this._handle.byteLength >> 3\n )\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(\n this.udx._handle,\n this._handle,\n 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(\n this._handle,\n req.handle,\n id,\n buffer,\n port,\n host,\n family,\n ttl || 0\n )\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(\n this._handle,\n req.handle,\n id,\n buffer,\n port,\n host,\n family,\n ttl || 0\n )\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.alloc(binding.sizeof_udx_napi_stream_t)\n this._view = new Uint32Array(\n this._handle.buffer,\n this._handle.byteOffset,\n this._handle.byteLength >> 2\n )\n this._view16 = new Uint16Array(\n this._handle.buffer,\n this._handle.byteOffset,\n this._handle.byteLength >> 1\n )\n this._view64 = new BigUint64Array(\n this._handle.buffer,\n this._handle.byteOffset,\n this._handle.byteLength >> 3\n )\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(\n this.udx._handle,\n this._handle,\n id,\n opts.framed ? 1 : 0,\n 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.once('remote-changed', onchanged).once('close', onclose)\n })\n\n binding.udx_napi_stream_change_remote(\n this._handle,\n socket._handle,\n remoteId,\n port,\n host,\n family\n )\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)\n 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 =\n 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 (\n this._wreqs.push({\n handle,\n size,\n buffers: null,\n buffer: null,\n flush: 0\n }) - 1\n )\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) {\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.alloc(binding.sizeof_udx_napi_t)\n this._watchers = new Set()\n this._view64 = new BigUint64Array(\n this._handle.buffer,\n this._handle.byteOffset,\n this._handle.byteLength >> 3\n )\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 { family = 0 } = opts\n\n const req = b4a.alloc(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.19.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 \"format\": \"prettier --write .\",\n \"lint\": \"prettier --check .\",\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 },\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 \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"tiny-byte-size\": \"^1.1.0\"\n }\n}\nmodule.exports = resolve\n\nfunction parse (addr) {\n const names = addr.split(/[/\\\\]/)\n\n const r = {\n isAbsolute: false,\n names\n }\n\n // don't think this ever happens, but whatevs\n if (names.length === 0) return r\n\n if (names.length > 1 && names[0].endsWith(':')) {\n r.isAbsolute = true\n\n if (names[0].length === 2) { // windows\n r.names = names.slice(1)\n return r\n }\n\n if (names[0] === 'file:') {\n r.names = names.slice(1)\n return r\n }\n\n r.names = names.slice(3)\n return r\n }\n\n r.isAbsolute = addr.startsWith('/') || addr.startsWith('\\\\')\n\n return r\n}\n\nfunction resolve (a, b = '') {\n const ap = parse(a)\n const bp = parse(b)\n\n if (bp.isAbsolute) {\n return resolveNames([], bp.names)\n }\n\n if (!ap.isAbsolute) {\n throw new Error('One of the two paths must be absolute')\n }\n\n return resolveNames(ap.names, bp.names)\n}\n\nfunction toString (p, names) {\n for (let i = 0; i < names.length; i++) {\n if (names[i] === '') continue\n if (names[i] === '.') continue\n if (names[i] === '..') {\n if (p.length === 1) throw new Error('Path cannot be resolved, too many \\'..\\'')\n p = p.slice(0, p.lastIndexOf('/')) || '/'\n continue\n }\n p += (p.length === 1 ? names[i] : '/' + names[i])\n }\n\n return p\n}\n\nfunction resolveNames (a, b) {\n return toString(toString('/', a), b)\n}\n{\n \"name\": \"unix-path-resolve\",\n \"version\": \"1.0.2\",\n \"description\": \"Cross platform resolve that always returns a UNIX style `/` seperated path\",\n \"main\": \"index.js\",\n \"dependencies\": {},\n \"devDependencies\": {\n \"brittle\": \"^2.0.1\",\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/unix-path-resolve.git\"\n },\n \"author\": \"Mathias Buus (@mafintosh)\",\n \"license\": \"MIT\",\n \"bugs\": {\n \"url\": \"https://github.com/mafintosh/unix-path-resolve/issues\"\n },\n \"homepage\": \"https://github.com/mafintosh/unix-path-resolve\"\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}\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}\nmodule.exports = read\n\nvar MSB = 0x80\n , REST = 0x7F\n\nfunction read(buf, offset) {\n var res = 0\n , offset = offset || 0\n , shift = 0\n , counter = offset\n , b\n , l = buf.length\n\n do {\n if (counter >= l) {\n read.bytes = 0\n throw new RangeError('Could not decode varint')\n }\n b = buf[counter++]\n res += shift < 28\n ? (b & REST) << shift\n : (b & REST) * Math.pow(2, shift)\n shift += 7\n } while (b >= MSB)\n\n read.bytes = counter - offset\n\n return res\n}\nmodule.exports = encode\n\nvar MSB = 0x80\n , REST = 0x7F\n , MSBALL = ~REST\n , INT = Math.pow(2, 31)\n\nfunction encode(num, out, offset) {\n out = out || []\n offset = offset || 0\n var oldOffset = offset\n\n while(num >= INT) {\n out[offset++] = (num & 0xFF) | MSB\n num /= 128\n }\n while(num & MSBALL) {\n out[offset++] = (num & 0xFF) | MSB\n num >>>= 7\n }\n out[offset] = num | 0\n \n encode.bytes = offset - oldOffset + 1\n \n return out\n}\nmodule.exports = {\n encode: require('./encode.js')\n , decode: require('./decode.js')\n , encodingLength: require('./length.js')\n}\n\nvar N1 = Math.pow(2, 7)\nvar N2 = Math.pow(2, 14)\nvar N3 = Math.pow(2, 21)\nvar N4 = Math.pow(2, 28)\nvar N5 = Math.pow(2, 35)\nvar N6 = Math.pow(2, 42)\nvar N7 = Math.pow(2, 49)\nvar N8 = Math.pow(2, 56)\nvar N9 = Math.pow(2, 63)\n\nmodule.exports = function (value) {\n return (\n value < N1 ? 1\n : value < N2 ? 2\n : value < N3 ? 3\n : value < N4 ? 4\n : value < N5 ? 5\n : value < N6 ? 6\n : value < N7 ? 7\n : value < N8 ? 8\n : value < N9 ? 9\n : 10\n )\n}\n{\n \"name\": \"varint\",\n \"version\": \"5.0.0\",\n \"description\": \"protobuf-style varint bytes - use msb to create integer values of varying sizes\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/chrisdickinson/varint.git\"\n },\n \"keywords\": [\n \"varint\",\n \"protobuf\",\n \"encode\",\n \"decode\"\n ],\n \"author\": \"Chris Dickinson <chris@neversaw.us>\",\n \"license\": \"MIT\",\n \"devDependencies\": {\n \"tape\": \"~2.12.3\"\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 = 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.2\",\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}\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}\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}\n{\n \"name\": \"pear-mobile\",\n \"version\": \"0.0.1\",\n \"description\": \"Embeddable Pear runtime for mobile applications\",\n \"main\": \"index.js\",\n \"exports\": {\n \"./package\": \"./package.json\",\n \".\": {\n \"types\": \"./index.d.ts\",\n \"default\": \"./index.js\"\n }\n },\n \"files\": [\n \"package.json\",\n \"index.js\",\n \"index.d.ts\",\n \"lib/pear.bundle.js\"\n ],\n \"scripts\": {\n \"build\": \"npx bare-pack --host ios --host android --linked --out ./lib/pear.bundle.js ./lib/pear.js\",\n \"format\": \"prettier . --write\",\n \"test\": \"prettier . --check && lunte && brittle-bare test/index.js\",\n \"lint\": \"lunte\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/holepunchto/pear-mobile.git\"\n },\n \"author\": \"Holepunch Inc\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/holepunchto/pear-mobile/issues\"\n },\n \"homepage\": \"https://github.com/holepunchto/pear-mobile\",\n \"devDependencies\": {\n \"brittle\": \"^3.19.0\",\n \"lunte\": \"^1.2.0\",\n \"prettier\": \"^3.6.2\",\n \"prettier-config-holepunch\": \"^2.0.0\",\n \"hyperdrive\": \"^13.2.1\",\n \"hyperswarm\": \"^4.16.0\",\n \"localdrive\": \"^2.2.0\",\n \"pear-link\": \"^4.2.1\",\n \"bare-bundle-id\": \"^1.0.2\",\n \"bare-module-traverse\": \"^2.0.1\",\n \"bare-pack\": \"^2.0.0\",\n \"bare-path\": \"^3.0.0\",\n \"corestore\": \"^7.8.0\",\n \"hypercore-id-encoding\": \"^1.3.0\",\n \"ready-resource\": \"^1.2.0\"\n },\n \"dependencies\": {\n \"b4a\": \"^1.7.4\",\n \"bare-rpc\": \"^1.1.0\",\n \"react-native-fs\": \"^2.20.0\"\n },\n \"peerDependencies\": {\n \"react-native-bare-kit\": \"*\",\n \"@react-native-async-storage/async-storage\": \"*\",\n \"react-native\": \"*\"\n }\n}\n"