[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Testing\n\non: [ push, pull_request ]\n\njobs:\n  build:\n    strategy:\n      matrix:\n        node: [ '24' ]\n        os: [macos-latest, ubuntu-latest, windows-latest]\n    runs-on: ${{ matrix.os }}\n\n    name: Unit tests ${{ matrix.node }} ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: ${{ matrix.node }}\n      - run: npm install\n      - run: npm run lint\n      - run: npm run test:node\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\npackage-lock.json\ntest-bundle.js\nbundle.js\ndat-sdk-bundle.js\n\n# IDE\n.idea"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at hi@datproject.org. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Dat Project\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# hyper-sdk\n\nA Software Development Kit for the [hypercore-protocol](https://hypercore-protocol.org/)\n\n## Why use this?\n\nHypercore-protocol and it's ecosystem consists of a bunch of low level building blocks for working with data in distributed applications. Although this modularity makes it easy to mix and match pieces, it adds complexity when it comes to actually building something.\n\nThe Hyper SDK combines the lower level pieces of the Hyper stack into high level APIs that you can use across platforms so that you can focus on your application rather than the gritty details of how it works.\n\n## Goals\n\n- High level API\n- Cross-platform with same codebase\n  - ✔ [Node.js](https://nodejs.org/en)\n  - ✔ [Electron](https://www.electronjs.org/)\n  - ✔ [Pear](https://docs.pears.com/)\n  - 🏗️ Web (PRs welcome)\n\n## Installation\n\nMake sure you've set up [Node.js](https://nodejs.org/).\n\n```shell\nnpm install --save hyper-sdk\n# or yarn\n```\n\n```js\nimport * as SDK from \"hyper-sdk\"\n```\n\n## API\n\n### SDK.create()\n\n```JavaScript\nconst sdk = await SDK.create({\n  // This argument is mandatory since Hypercore no longer support in-memory\n  // Check out the env-paths module for application specific path storage\n  storage: './hyper-sdk',\n\n  // This controls whether the SDK will automatically start swarming when loading a core via `get`\n  // Set this to false if you want to have more fine control over peer discovery\n  autoJoin: true,\n\n  // Specify options to pass to the Corestore constructor\n  // The storage will get derived from the `storage` parameter\n  // https://github.com/hypercore-protocol/corestore/\n  corestoreOpts: {},\n\n  // Specify options to pass to the hyperswarm constructor\n  // The keypair will get derived automatically from the corestore\n  // https://github.com/hyperswarm/hyperswarm\n  swarmOpts: {},\n})\n```\n\n### sdk.publicKey\n\nThe public key used for identifying this peer in the hyperswarm network.\n\nThis is a 32 byte buffer which can be use in conjunction with `sdk.joinPeer()` to connect two peers directly together.\n\n### sdk.connections\n\nThe list of active connections to other peers, taken from hyperswarm.\n\n### sdk.peers\n\nThe list of active peers.\n\nEach peer has a `publicKey`, and list of `topics`\n\nYou can find more docs in the [hyperswarm](https://github.com/hyperswarm/hyperswarm#peerinfo-api) repo.\n\n### sdk.cores\n\nList of active Hypercores.\n\n### sdk.on('peer-add', peerInfo) / sdk.on('peer-remove', peerInfo)\n\nYou can listen on when a peer gets connected or disconnected with this event.\n\nYou can find more docs in the [hyperswarm](https://github.com/hyperswarm/hyperswarm#peerinfo-api) repo.\n\n```JavaScript\nsdk.on('peer-add', (peerInfo) => {\n  console.log('Connected to', peerInfo.publicKey, 'on', peerInfo.topics)\n})\nsdk.on('peer-add', (peerInfo) => {\n  console.log('Disconnected from')\n})\n```\n\n### sdk.get()\n\nYou can initialize a [Hypercore](https://github.com/hypercore-protocol/hypercore) instance by passing in a key, a name to derive a key from, or a URL containing either a key or a DNS name.\n\nUnlike corestore, you may not initialize a hypercore from a `null` key since everything must be derivable or loadable.\n\nUnless `autoJoin` is set to `false`, the peer discovery will be automatically started for the core.\n\n```JavaScript\n// Derive a key from a \"name\"\nconst core = await sdk.get('example name')\n\n// Resolve DNS to a hypercore\nconst core = await sdk.get('hyper://example.mauve.moe')\n\n// Buffer key, 32 bytes of 0's\nconst core = await sdk.get(b4a.alloc(32, 0))\n\n// Hex key, equivalent to 32 bytes of zeros\nconst core = await sdk.get('hyper://0000000000000000000000000000000000000000000000000000000000000000')\n\n// z32 encoded, equivalent to 32 bytes of zeros\nconst core = await sdk.get('hyper://yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')\n\n// Don't auto-join the swarm for the core on init\nconst core = await sdk.get('example', {autoJoin: false})\n```\n\n### sdk.getDrive()\n\nYou can initialize a [Hyperdrive](https://github.com/holepunchto/hyperdrive-next) instance by passing in the same arguments as in `sdk.get()`.\n\nIn addition to the usual `hyperdrive` properties, there's a new `url` property to get the `hyper://` URL for the drive to used elsewhere.\n\nNote that the drives's metadata DB's discovery key will be used for replicating if `autoJoin` is `true`.\n\nHyperdrive is mostly useful for storing and loading files since it splits the metadata representing the file systema and the blob storage into separate cores.\n\n```JavaScript\nconst drive = await sdk.getDrive('hyper://blob.mauve.moe')\nfor(const path of drive.readdir('/')) {\n  const stat = drive.stat(path)\n}\n```\n\n### sdk.getBee()\n\nYou can initialize a [Hyperbee](https://github.com/holepunchto/hyperbee) instance by passing the same arguments as in `sdk.get()`.\n\nIn addition to the usual `hyperbee` properties, there's a new `url` property to get the `hyper://` URL for the bee to used elsewhere.\n\nAdditionally, you should pass in a `keyEncoding` and a `valueEncoding` in order to control the encoding for data that's being written.\n\nHyperbee is best used when you want to create database indexes.\n\nFor an out of the box database with a proper query language, check out [HyperbeeDeeBee](https://github.com/RangerMauve/hyperbeedeebee/).\n\n```JavaScript\nconst db = await sdk.getBee('example db')\n\nconst db = await sdk.getBee('example db', {keyEncoding: 'utf8', valueEncoding: 'json')\nawait db.put('hello', 'world')\n\nfor(const entry of db.createReadStream()) {\n  console.log(entry)\n}\n```\n\n### sdk.resolveDNSToKey()\n\nYou can manually resolve DNS addresses to hypercore keys on domains using the DNS Link spec with this method.\n\nHowever, it's not mandatory to use DNS since `sdk.get()` will automatically detect and perform resolutions of DNS for `hyper://` URLs.\n\nHyper-SDK currently bypasses the OS DNS resolver and uses DNS Over HTTPS. You can configure your own using the `dnsResolver` config option and any of the options [on this list](https://dnsprivacy.org/public_resolvers/#dns-over-https-doh). By default we use the one provided by [Mozilla](https://developers.cloudflare.com/1.1.1.1/commitment-to-privacy/privacy-policy/firefox/).\n\n```JavaScript\nconst key = await sdk.resolveDNSToKey('example.mauve.moe')\n```\n\n### sdk.namespace()\n\nGet back a namespaced [Corestore](https://github.com/hypercore-protocol/corestore/) instance which can be passed to things like Hyperdrive.\n\nNote that cores initialized with a namespaced corestore will not be auto-joined and you will need to call `sdk.join(core.discoveryKey)` on said cores.\n\n```JavaScript\nimport Hypderdrive from \"hyperdrive\"\n\nconst drive = new Hyperdrive(sdk.namespace('example'))\n\n// Wait for the drive to initiailize\nawait drive.ready()\n\n// Manually trigger peer lookup for this drive\nsdk.join(drive.publicKey)\n```\n\n### sdk.join() / sdk.leave()\n\nYou can manually trigger peer discovery of hypercores as well as stop peer discovery.\nThis can be done by using the `discoveryKey` of a hypercore, or any 32 byte buffer.\n\nAs well, you can use string names for topics in order to discover peers based on a human readable string.\nWhen using string topics, they are converted to 32 byte buffers using the [Hypercore Crypto namespace algorithm](https://github.com/mafintosh/hypercore-crypto#list--cryptonamespacename-count).\n\n```JavaScript\nconst core = await sdk.get('example', {autoJoin: false})\n\n// Start finding peers without advertising\nsdk.join(core.discoveryKey, {server: false})\n\n// Listen on a human readable topic\nsdk.join(\"cool cat videos\")\n\nsdk.leave(core.discoveryKey)\nsdk.leave(\"cool cat videos\")\n```\n\n### sdk.joinPeer() / sdk.leavePeer()\n\n```JavaScript\nconst sdk1 = await SDK.create({storage: './sdk1'})\nconst sdk2 = await SDK.create({storage: './sdk1'})\n\nsdk1.joinPeer(sdk2.publicKey)\n```\n\n### sdk.close()\n\nThis will gracefully close connections, remove advertisements from the DHT, and close any open file handles.\n\nMake sure you invoke this to keep the network fast and to avoid data corruption!\n\n### sdk.suspend()\n\nThis will pause network and data operations.\nUse it when your app is in the background or the user wants to pause seeding.\n\n### sdk.resume()\n\nUndo the effects of `suspend()`, re-enable network and storage.\n\n## TypeScript Support\n\nThis module comes with TypeScript types, but by default Hypercore and other Holepunch libraries do not.\n\nYou can add these missing types by importing them from the SDK's `types` folder in your `tsconfig.json` file.\n\nYour `tsconfig.json` file should look something like this:\n\n```\n{\n    \"compilerOptions\": {\n        \"module\": \"nodenext\",\n        \"moduleResolution\": \"nodenext\",\n    },\n    \"extends\": \"@tsconfig/node20/tsconfig.json\",\n    \"include\": [\n        \"./test.js\",\n        \"./node_modules/hyper-sdk/types/*.d.ts\"\n    ]\n}\n```"
  },
  {
    "path": "dist/index.d.ts",
    "content": "/**\n *\n * @param {object} options\n * @param {string} [options.storage]\n * @param {CoreStoreOpts} [options.corestoreOpts]\n * @param {SwarmOpts} [options.swarmOpts]\n * @param {typeof globalThis[\"fetch\"]} [options.fetch]\n * @param {HyperSwarm} [options.swarm]\n * @param {CoreStore} [options.corestore]\n * @param {RocksDB} [options.dnsCache]\n * @param {CoreOpts} [options.defaultCoreOpts]\n * @param {JoinOpts} [options.defaultJoinOpts]\n * @param {string} [options.dnsResolver]\n * @param {boolean} [options.autoJoin=true]\n * @param {boolean} [options.doReplicate=true]\n * @returns {Promise<SDK>}\n */\nexport function create({ storage, corestoreOpts, swarmOpts, fetch, ...opts }?: {\n    storage?: string | undefined;\n    corestoreOpts?: CoreStoreOpts | undefined;\n    swarmOpts?: SwarmOpts | undefined;\n    fetch?: typeof globalThis.fetch | undefined;\n    swarm?: HyperSwarm | undefined;\n    corestore?: CoreStore | undefined;\n    dnsCache?: RocksDB | undefined;\n    defaultCoreOpts?: CoreOpts | undefined;\n    defaultJoinOpts?: JoinOpts | undefined;\n    dnsResolver?: string | undefined;\n    autoJoin?: boolean | undefined;\n    doReplicate?: boolean | undefined;\n}): Promise<SDK>;\n/** @import {JoinOpts,SwarmOpts,PeerDiscovery,Connection} from \"hyperswarm\" */\n/** @import {BeeOpts} from \"hyperbee\" */\n/** @import {CoreOpts} from \"hypercore\" */\n/** @import {DriveOpts} from \"hyperdrive\" */\n/** @import {CoreStoreOpts} from \"corestore\" */\n/** @typedef {Buffer|Uint8Array} Key */\n/** @typedef {string|Key} NameOrKeyOrURL */\n/** @typedef {{key?:Key|null, name?:string}} ResolvedKeyOrName */\n/**\n * @typedef {object} DNSResponse\n * @property {{name: string, data: string}} DNSResponse.Answer\n */\nexport const HYPER_PROTOCOL_SCHEME: \"hyper://\";\nexport const DEFAULT_CORE_OPTS: {};\nexport namespace DEFAULT_JOIN_OPTS {\n    let server: boolean;\n    let client: boolean;\n}\nexport const DEFAULT_CORESTORE_OPTS: {};\nexport const DEFAULT_SWARM_OPTS: {};\nexport class SDK extends EventEmitter<any> {\n    /**\n     * @param {object} [options]\n     * @param {typeof globalThis[\"fetch\"]} [options.fetch]\n     * @param {HyperSwarm} [options.swarm]\n     * @param {CoreStore} [options.corestore]\n     * @param {CoreOpts} [options.defaultCoreOpts]\n     * @param {JoinOpts} [options.defaultJoinOpts]\n     * @param {string} [options.dnsResolver]\n     * @param {boolean} [options.autoJoin=true]\n     * @param {boolean} [options.doReplicate=true]\n     * @param {RocksDB} [options.dnsCache]\n     */\n    constructor({ swarm, corestore, dnsCache, fetch, defaultCoreOpts, defaultJoinOpts, dnsResolver, autoJoin, doReplicate }?: {\n        fetch?: typeof globalThis.fetch | undefined;\n        swarm?: HyperSwarm | undefined;\n        corestore?: CoreStore | undefined;\n        defaultCoreOpts?: CoreOpts | undefined;\n        defaultJoinOpts?: JoinOpts | undefined;\n        dnsResolver?: string | undefined;\n        autoJoin?: boolean | undefined;\n        doReplicate?: boolean | undefined;\n        dnsCache?: RocksDB | undefined;\n    });\n    autoJoin: boolean;\n    get swarm(): HyperSwarm;\n    get corestore(): CoreStore;\n    get publicKey(): import(\"hyperswarm\").Key;\n    get connections(): Connection[];\n    get peers(): Map<string, import(\"hyperswarm\").PeerInfo>;\n    /**\n     * @type {Hypercore[]}\n     */\n    get cores(): Hypercore[];\n    /**\n     * @type {Hyperdrive[]}\n     */\n    get drives(): Hyperdrive[];\n    /** @type {Hyperbee[]} */\n    get bees(): Hyperbee[];\n    /**\n     * Resolve DNS names to a hypercore key using the DNSLink spec\n     * @param {string} hostname Hostname to resolve, e,g, `agregore.mauve.moe`\n     * @returns {Promise<string>}\n     */\n    resolveDNSToKey(hostname: string): Promise<string>;\n    /**\n     * Resolves a string to be a key or opts and resolves DNS\n     * Useful for hypercore opts or Hyperdrive\n     * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or URL to resolve\n     * @returns {Promise<ResolvedKeyOrName>}\n     */\n    resolveNameOrKeyToOpts(nameOrKeyOrURL: NameOrKeyOrURL): Promise<ResolvedKeyOrName>;\n    /**\n     *\n     * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or hyper URL for the bee\n     * @param {BeeOpts & CoreOpts & JoinOpts} opts Options for configuring Hyperbee\n     * @returns {Promise<Hyperbee>}\n     */\n    getBee(nameOrKeyOrURL: NameOrKeyOrURL, opts?: BeeOpts & CoreOpts & JoinOpts): Promise<Hyperbee>;\n    /**\n     *\n     * @param {NameOrKeyOrURL} nameOrKeyOrURL\n     * @param {DriveOpts&JoinOpts} opts\n     * @returns {Promise<Hyperdrive>}\n     */\n    getDrive(nameOrKeyOrURL: NameOrKeyOrURL, opts?: DriveOpts & JoinOpts): Promise<Hyperdrive>;\n    /**\n     * @template DataType\n     * Get a HyperCore by its name or key or URL\n     * @param {NameOrKeyOrURL} nameOrKeyOrURL\n     * @param {CoreOpts&JoinOpts} [opts]\n     * @returns {Promise<Hypercore<DataType>>}\n     */\n    get<DataType>(nameOrKeyOrURL: NameOrKeyOrURL, opts?: CoreOpts & JoinOpts): Promise<Hypercore<DataType>>;\n    /**\n     * Get a sub CoreStore for a given namespace. Use this to derive core names for a particular group\n     * @param {string} namespace Namespace to store cores under\n     * @returns {CoreStore}\n     */\n    namespace(namespace: string): CoreStore;\n    /**\n     * Derive a topic key (for hypercores) from a namespace.\n     * @param {string} name Name of the namespace to derive\n     * @returns {Key}\n     */\n    makeTopicKey(name: string): Key;\n    /**\n     * Start peer discovery on a core. Use this if you created a core on a namespaced CoreStore\n     * @param {Hypercore} core\n     * @param {JoinOpts} opts\n     * @returns {Promise<void>}\n     */\n    joinCore(core: Hypercore, opts?: JoinOpts): Promise<void>;\n    /**\n     *\n     * @param {string|Key} topic\n     * @param {JoinOpts} opts\n     * @returns {PeerDiscovery}\n     */\n    join(topic: string | Key, opts?: JoinOpts): PeerDiscovery;\n    /**\n     *\n     * @param {string|Key} topic\n     * @returns {Promise<void>}\n     */\n    leave(topic: string | Key): Promise<void>;\n    /**\n     * @param {Key} id\n     */\n    joinPeer(id: Key): void;\n    /**\n     * @param {Key} id\n     */\n    leavePeer(id: Key): void;\n    ready(): Promise<void>;\n    close(): Promise<void>;\n    /**\n     * Replicate a connection from hyperswarm manually\n     * @param {Connection} connection\n     */\n    replicate(connection: Connection): void;\n    #private;\n}\nexport type Key = Buffer | Uint8Array;\nexport type NameOrKeyOrURL = string | Key;\nexport type ResolvedKeyOrName = {\n    key?: Key | null;\n    name?: string;\n};\nexport type DNSResponse = {\n    Answer: {\n        name: string;\n        data: string;\n    };\n};\nimport type { CoreStoreOpts } from \"corestore\";\nimport type { SwarmOpts } from \"hyperswarm\";\nimport HyperSwarm from 'hyperswarm';\nimport CoreStore from 'corestore';\nimport RocksDB from 'rocksdb-native';\nimport type { CoreOpts } from \"hypercore\";\nimport type { JoinOpts } from \"hyperswarm\";\nimport { EventEmitter } from 'events';\nimport type { Connection } from \"hyperswarm\";\nimport Hypercore from 'hypercore';\nimport Hyperdrive from 'hyperdrive';\nimport Hyperbee from 'hyperbee';\nimport type { BeeOpts } from \"hyperbee\";\nimport type { DriveOpts } from \"hyperdrive\";\nimport type { PeerDiscovery } from \"hyperswarm\";\n"
  },
  {
    "path": "dist/test.d.ts",
    "content": "export {};\n"
  },
  {
    "path": "index.js",
    "content": "import HyperSwarm from 'hyperswarm'\nimport CoreStore from 'corestore'\nimport Hypercore from 'hypercore'\nimport Hyperdrive from 'hyperdrive'\nimport Hyperbee from 'hyperbee'\nimport crypto from 'hypercore-crypto'\nimport z32 from 'z32'\nimport b4a from 'b4a'\nimport { EventEmitter } from 'events'\nimport { join } from 'path'\nimport RocksDB from 'rocksdb-native'\n\n/** @import {JoinOpts,SwarmOpts,PeerDiscovery,Connection} from \"hyperswarm\" */\n/** @import {BeeOpts} from \"hyperbee\" */\n/** @import {CoreOpts} from \"hypercore\" */\n/** @import {DriveOpts} from \"hyperdrive\" */\n/** @import {CoreStoreOpts} from \"corestore\" */\n\n/** @typedef {Buffer|Uint8Array} Key */\n/** @typedef {string|Key} NameOrKeyOrURL */\n/** @typedef {{key?:Key|null, name?:string}} ResolvedKeyOrName */\n/**\n * @typedef {object} DNSResponse\n * @property {{name: string, data: string}} DNSResponse.Answer\n */\n\n// TODO: Base36 encoding/decoding for URLs instead of hex\n\nexport const HYPER_PROTOCOL_SCHEME = 'hyper://'\nexport const DEFAULT_CORE_OPTS = {}\nexport const DEFAULT_JOIN_OPTS = {\n  server: true,\n  client: true\n}\nexport const DEFAULT_CORESTORE_OPTS = {}\nexport const DEFAULT_SWARM_OPTS = {}\n\n// Monkey-patching with first class URL support\nObject.defineProperty(Hypercore.prototype, 'url', {\n  get: function () {\n    return `${HYPER_PROTOCOL_SCHEME}${this.id}/`\n  }\n})\nObject.defineProperty(Hyperdrive.prototype, 'url', {\n  get: function () {\n    return `${HYPER_PROTOCOL_SCHEME}${this.core.id}/`\n  }\n})\nObject.defineProperty(Hyperbee.prototype, 'url', {\n  get: function () {\n    return `${HYPER_PROTOCOL_SCHEME}${this.feed.id}/`\n  }\n})\n\nconst DEFAULT_DNS_RESOLVER = 'https://mozilla.cloudflare-dns.com/dns-query'\n\nconst DNSLINK_PREFIX = 'dnslink=/hyper/'\n\nexport class SDK extends EventEmitter {\n  #fetch\n  #dnsCache\n  #dnsMemoryCache\n  #defaultCoreOpts\n  #defaultJoinOpts\n  #dnsResolver\n  #swarm\n  #corestore\n  #coreCache\n  #beeCache\n  #driveCache\n\n  /**\n   * @param {object} [options]\n   * @param {typeof globalThis[\"fetch\"]} [options.fetch]\n   * @param {HyperSwarm} [options.swarm]\n   * @param {CoreStore} [options.corestore]\n   * @param {CoreOpts} [options.defaultCoreOpts]\n   * @param {JoinOpts} [options.defaultJoinOpts]\n   * @param {string} [options.dnsResolver]\n   * @param {boolean} [options.autoJoin=true]\n   * @param {boolean} [options.doReplicate=true]\n   * @param {RocksDB} [options.dnsCache]\n   */\n  constructor ({\n    swarm,\n    corestore,\n    dnsCache,\n    fetch = globalThis.fetch,\n    defaultCoreOpts = DEFAULT_CORE_OPTS,\n    defaultJoinOpts = DEFAULT_JOIN_OPTS,\n    dnsResolver = DEFAULT_DNS_RESOLVER,\n    autoJoin = true,\n    doReplicate = true\n  } = {}) {\n    super()\n    if (!swarm) throw new TypeError('Missing parameter swarm')\n    if (!corestore) throw new TypeError('Missing parameter corestore')\n    if (!dnsCache) throw new TypeError('Missing parameter dnsCache')\n    this.#swarm = swarm\n    this.#corestore = corestore\n    this.#dnsCache = dnsCache\n    this.#fetch = fetch\n\n    // These probably shouldn't be accessed\n    this.#dnsMemoryCache = new Map()\n    this.#coreCache = new Map()\n    this.#beeCache = new Map()\n    this.#driveCache = new Map()\n\n    this.#defaultCoreOpts = defaultCoreOpts\n    this.#defaultJoinOpts = defaultJoinOpts\n    this.#dnsResolver = dnsResolver\n\n    this.autoJoin = autoJoin\n\n    if (doReplicate) {\n      swarm.on('connection', (connection, peerInfo) => {\n        this.emit('peer-add', peerInfo)\n        connection.once('close', () => this.emit('peer-remove', peerInfo))\n        this.replicate(connection)\n      })\n    }\n  }\n\n  get swarm () {\n    return this.#swarm\n  }\n\n  get corestore () {\n    return this.#corestore\n  }\n\n  get publicKey () {\n    return this.#swarm.keyPair.publicKey\n  }\n\n  get connections () {\n    return this.#swarm.connections\n  }\n\n  get peers () {\n    return this.#swarm.peers\n  }\n\n  /**\n   * @type {Hypercore[]}\n   */\n  get cores () {\n    return [...this.#coreCache.values()]\n  }\n\n  /**\n   * @type {Hyperdrive[]}\n   */\n  get drives () {\n    return [...this.#driveCache.values()]\n  }\n\n  /** @type {Hyperbee[]} */\n  get bees () {\n    return [...this.#beeCache.values()]\n  }\n\n  /**\n   * Resolve DNS names to a hypercore key using the DNSLink spec\n   * @param {string} hostname Hostname to resolve, e,g, `agregore.mauve.moe`\n   * @returns {Promise<string>}\n   */\n  async resolveDNSToKey (hostname) {\n    // TODO: Check for TTL?\n    if (this.#dnsMemoryCache.has(hostname)) {\n      return this.#dnsMemoryCache.get(hostname)\n    }\n\n    const fetch = this.#fetch\n\n    const subdomained = `_dnslink.${hostname}`\n\n    const url = `${this.#dnsResolver}?name=${subdomained}&type=TXT`\n\n    let answers = null\n    try {\n      const response = await fetch(url, {\n        headers: { accept: 'application/dns-json' }\n      })\n\n      if (!response.ok) {\n        throw new Error(\n          `Unable to resolve DoH for ${hostname} ${await response.text()}`\n        )\n      }\n\n      const dnsResults = /** @type {DNSResponse} */ (await response.json())\n      answers = dnsResults.Answer\n      await this.#dnsCache.put(hostname, JSON.stringify(dnsResults))\n    } catch (e) {\n      const cached = await this.#dnsCache.get(hostname)\n      if (cached) {\n        answers = JSON.parse(cached).Answer\n      }\n    }\n\n    for (let { name, data } of answers) {\n      if (name !== subdomained || !data) {\n        continue\n      }\n      if (data.startsWith('\"')) {\n        data = data.slice(1, -1)\n      }\n      if (!data.startsWith(DNSLINK_PREFIX)) {\n        continue\n      }\n      const key = data.split('/')[2]\n      this.#dnsMemoryCache.set(hostname, key)\n      return key\n    }\n\n    throw new Error(`DNS-Link Record not found for TXT ${subdomained}`)\n  }\n\n  /**\n   * Resolves a string to be a key or opts and resolves DNS\n   * Useful for hypercore opts or Hyperdrive\n   * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or URL to resolve\n   * @returns {Promise<ResolvedKeyOrName>}\n   */\n  async resolveNameOrKeyToOpts (nameOrKeyOrURL) {\n    // If a URL, use the hostname as either a key or a DNS to resolve\n    // If not a URL, try to decode to a key\n    // if not a key, use as name to generate a hypercore\n    // Else it's an errorW\n\n    const isKeyString = typeof nameOrKeyOrURL === 'string'\n    if (!isKeyString) {\n      // If a 32 byte buffer, use it as the key\n      if (nameOrKeyOrURL && nameOrKeyOrURL.length === 32) {\n        return { key: nameOrKeyOrURL }\n      } else {\n        throw new Error(\n          'Must specify a name, url, or a 32 byte buffer with a key'\n        )\n      }\n    }\n\n    if (nameOrKeyOrURL.startsWith(HYPER_PROTOCOL_SCHEME)) {\n      const url = new URL(nameOrKeyOrURL)\n      // probably a domain\n      if (url.hostname.includes('.')) {\n        const key = await this.resolveDNSToKey(url.hostname)\n\n        return { key: stringToKey(key) }\n      } else {\n        // Try to parse the hostname to a key\n        const key = stringToKey(url.hostname)\n        if (!key) {\n          // If not a key or a domain, throw an error\n          throw new Error(\n            'URLs must have either an encoded key or a valid DNSlink domain'\n          )\n        }\n        return { key }\n      }\n    } else {\n      const parsed = stringToKey(nameOrKeyOrURL)\n      if (parsed) {\n        return { key: parsed }\n      } else {\n        return { name: nameOrKeyOrURL }\n      }\n    }\n  }\n\n  /**\n   *\n   * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or hyper URL for the bee\n   * @param {BeeOpts & CoreOpts & JoinOpts} opts Options for configuring Hyperbee\n   * @returns {Promise<Hyperbee>}\n   */\n  async getBee (nameOrKeyOrURL, opts = {}) {\n    const core = await this.get(nameOrKeyOrURL, opts)\n\n    if (this.#beeCache.has(core.url)) {\n      return this.#beeCache.get(core.url)\n    }\n\n    const bee = new Hyperbee(core, opts)\n\n    core.once('close', () => {\n      this.#beeCache.delete(core.url)\n    })\n\n    this.#beeCache.set(core.url, bee)\n\n    await bee.ready()\n\n    return bee\n  }\n\n  /**\n   *\n   * @param {NameOrKeyOrURL} nameOrKeyOrURL\n   * @param {DriveOpts&JoinOpts} opts\n   * @returns {Promise<Hyperdrive>}\n   */\n  async getDrive (nameOrKeyOrURL, opts = {}) {\n    const coreOpts = {\n      ...this.#defaultCoreOpts,\n      autoJoin: this.autoJoin,\n      ...opts\n    }\n\n    const resolvedOpts = await this.resolveNameOrKeyToOpts(nameOrKeyOrURL)\n\n    const { key, name } = resolvedOpts\n    let stringKey = key && key.toString('hex')\n\n    if (this.#driveCache.has(name)) {\n      return this.#driveCache.get(name)\n    } else if (this.#driveCache.has(stringKey)) {\n      return this.#driveCache.get(stringKey)\n    }\n\n    Object.assign(coreOpts, resolvedOpts)\n\n    let corestore = this.corestore\n\n    if (stringKey) {\n      corestore = this.namespace(stringKey)\n    } else if (name) {\n      corestore = this.namespace(name)\n    } else {\n      throw new Error('Unable to parse')\n    }\n\n    const drive = new Hyperdrive(corestore, key || null)\n\n    await drive.ready()\n\n    const core = drive.core\n    stringKey = core.key.toString('hex')\n\n    drive.once('close', () => {\n      this.#driveCache.delete(stringKey)\n      this.#driveCache.delete(name)\n    })\n\n    this.#driveCache.set(stringKey, drive)\n    if (name) this.#driveCache.set(name, drive)\n\n    if (coreOpts.autoJoin && !core.discovery) {\n      await this.joinCore(core, opts)\n    }\n\n    return drive\n  }\n\n  /**\n   * @template DataType\n   * Get a HyperCore by its name or key or URL\n   * @param {NameOrKeyOrURL} nameOrKeyOrURL\n   * @param {CoreOpts&JoinOpts} [opts]\n   * @returns {Promise<Hypercore<DataType>>}\n   */\n  async get (nameOrKeyOrURL, opts = {}) {\n    const coreOpts = {\n      ...this.#defaultCoreOpts,\n      autoJoin: this.autoJoin,\n      ...opts\n    }\n\n    const resolvedOpts = await this.resolveNameOrKeyToOpts(nameOrKeyOrURL)\n\n    const { key, name } = resolvedOpts\n    let stringKey = key && key.toString('hex')\n\n    if (this.#coreCache.has(name)) {\n      return this.#coreCache.get(name)\n    } else if (this.#coreCache.has(stringKey)) {\n      return this.#coreCache.get(stringKey)\n    }\n\n    Object.assign(coreOpts, resolvedOpts)\n\n    // There shouldn't be a way to pass null for the key\n    const core = this.corestore.get(coreOpts)\n\n    // Await for core to be ready\n    await core.ready()\n\n    core.once('close', () => {\n      this.#coreCache.delete(stringKey)\n      this.#coreCache.delete(name)\n    })\n\n    stringKey = core.key.toString('hex')\n\n    this.#coreCache.set(stringKey, core)\n    if (name) this.#coreCache.set(name, core)\n\n    if (coreOpts.autoJoin && !core.discovery) {\n      await this.joinCore(core, opts)\n    }\n\n    return core\n  }\n\n  /**\n   * Get a sub CoreStore for a given namespace. Use this to derive core names for a particular group\n   * @param {string} namespace Namespace to store cores under\n   * @returns {CoreStore}\n   */\n  namespace (namespace) {\n    return this.corestore.namespace(namespace)\n  }\n\n  /**\n   * Derive a topic key (for hypercores) from a namespace.\n   * @param {string} name Name of the namespace to derive\n   * @returns {Key}\n   */\n  makeTopicKey (name) {\n    const [key] = crypto.namespace(name, 1)\n    return key\n  }\n\n  /**\n   * Start peer discovery on a core. Use this if you created a core on a namespaced CoreStore\n   * @param {Hypercore} core\n   * @param {JoinOpts} opts\n   * @returns {Promise<void>}\n   */\n  async joinCore (core, opts = {}) {\n    if (core.discovery) return\n    const discovery = this.join(core.discoveryKey, opts)\n    core.discovery = discovery\n\n    // If we're the owner, then we wait until is fully announced\n    if (core.writable) {\n      await discovery.flushed()\n    }\n\n    // Await for initial peer for new readable cores\n    if (!core.writable && !core.length) {\n      const done = core.findingPeers()\n      this.swarm.flush().then(done)\n      await core.update()\n    }\n\n    core.once('close', () => {\n      discovery.destroy()\n    })\n  }\n\n  /**\n   *\n   * @param {string|Key} topic\n   * @param {JoinOpts} opts\n   * @returns {PeerDiscovery}\n   */\n  join (topic, opts = {}) {\n    if (typeof topic === 'string') {\n      return this.join(this.makeTopicKey(topic), opts)\n    }\n    const joinOpts = { ...this.#defaultJoinOpts, ...opts }\n    return this.swarm.join(topic, joinOpts)\n  }\n\n  /**\n   *\n   * @param {string|Key} topic\n   * @returns {Promise<void>}\n   */\n  leave (topic) {\n    if (typeof topic === 'string') {\n      return this.leave(this.makeTopicKey(topic))\n    }\n    return this.swarm.leave(topic)\n  }\n\n  /**\n   * @param {Key} id\n   */\n  joinPeer (id) {\n    this.swarm.joinPeer(id)\n  }\n\n  /**\n   * @param {Key} id\n   */\n  leavePeer (id) {\n    this.swarm.leavePeer(id)\n  }\n\n  async ready () {\n    // Wait for the network to be configured?\n    await this.corestore.ready()\n    await this.swarm.listen()\n  }\n\n  async close () {\n    await this.#dnsCache.flush()\n    // Close corestore, close hyperswarm\n    await Promise.all([\n      this.corestore.close(),\n      this.swarm.destroy(),\n      this.#dnsCache.close()\n    ])\n  }\n\n  /**\n   * Persist any pending transactions to disk and pause network activity.\n   * Use this when the app goes in the background or you want to pause seeding.\n   */\n  async suspend() {\n    await this.swarm.suspend()\n    await this.corestore.suspend()\n  }\n\n  /**\n   * Resume network interactions and re-enable storage after suspending.\n   */\n  async resume() {\n    await this.corestore.resume()\n    await this.swarm.resume()\n  }\n\n  /**\n   * Replicate a connection from hyperswarm manually\n   * @param {Connection} connection\n   */\n  replicate (connection) {\n    this.corestore.replicate(connection)\n  }\n}\n\n/**\n * Initialize the SDK\n * @param {object} options\n * @param {string} options.storage\n * @param {CoreStoreOpts} [options.corestoreOpts]\n * @param {SwarmOpts} [options.swarmOpts]\n * @param {typeof globalThis[\"fetch\"]} [options.fetch]\n * @param {HyperSwarm} [options.swarm]\n * @param {CoreStore} [options.corestore]\n * @param {RocksDB} [options.dnsCache]\n * @param {CoreOpts} [options.defaultCoreOpts]\n * @param {JoinOpts} [options.defaultJoinOpts]\n * @param {string} [options.dnsResolver]\n * @param {boolean} [options.autoJoin=true]\n * @param {boolean} [options.doReplicate=true]\n * @returns {Promise<SDK>}\n */\nexport async function create ({\n  storage,\n  corestoreOpts = DEFAULT_CORESTORE_OPTS,\n  swarmOpts = DEFAULT_SWARM_OPTS,\n  fetch = globalThis.fetch,\n  ...opts\n} = {storage: ''}) {\n  if (!storage) {\n    throw new Error('Storage parameter is required to be a valid file path')\n  }\n  const corestore =\n    opts.corestore || new CoreStore(storage, { ...corestoreOpts })\n\n  const dnsCache = opts.dnsCache || new RocksDB(join(storage, 'dnsCache'))\n\n  const networkKeypair = await corestore.createKeyPair('noise')\n\n  const swarm =\n    opts.swarm ||\n    new HyperSwarm({\n      keyPair: networkKeypair,\n      ...swarmOpts\n    })\n\n  const sdk = new SDK({\n    ...opts,\n    fetch: fetch || (await import('bare-fetch')).default,\n    corestore,\n    swarm,\n    dnsCache\n  })\n\n  await sdk.ready()\n\n  return sdk\n}\n\n/**\n * @param {string} string\n * @returns {Key|null}\n */\nfunction stringToKey (string) {\n  if (string.length === 52) {\n    try {\n      return z32.decode(string)\n    } catch {\n      // Not formatted properly, probs a name?\n    }\n  } else if (string.length === 64) {\n    // Parse as hex key\n    try {\n      return b4a.from(string, 'hex')\n    } catch {\n      // Not formatted properly, probs a name?\n    }\n  }\n  return null\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"hyper-sdk\",\n  \"version\": \"6.2.2\",\n  \"description\": \"A Software Development Kit for the Hypercore-Protocol\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"default\": \"./index.js\",\n      \"types\": \"./dist/index.d.ts\"\n    }\n  },\n  \"imports\": {\n    \"events\": {\n      \"default\": \"bare-events\"\n    },\n    \"stream\": {\n      \"default\": \"bare-stream\"\n    },\n    \"path\": {\n      \"default\": \"bare-path\"\n    }\n  },\n  \"types\": \"dist\",\n  \"scripts\": {\n    \"test\": \"npm run test:node && npm run test:bare\",\n    \"test:bare\": \"bare test.js\",\n    \"test:node\": \"node test.js\",\n    \"lint\": \"standard --fix && tsc\",\n    \"upgrade-hyper\": \"npm i --save corestore@latest hypercore@latest hyperswarm@latest hyperdrive@latest hyperbee@latest z32@latest b4a@latest bare-events@latest bare-fetch@latest bare-os@latest bare-path@latest bare-stream@latest\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/rangermauve/hyper-sdk.git\"\n  },\n  \"keywords\": [\n    \"dat\",\n    \"sdk\",\n    \"hyperdrive\",\n    \"hypercore\",\n    \"hypercore-protocol\",\n    \"p2p\"\n  ],\n  \"author\": \"RangerMauve\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/rangermauve/hyper-sdk/issues\"\n  },\n  \"homepage\": \"https://github.com/rangermauve/hyper-sdk#readme\",\n  \"dependencies\": {\n    \"b4a\": \"^1.7.3\",\n    \"bare-events\": \"^2.8.2\",\n    \"bare-fetch\": \"^2.5.1\",\n    \"bare-os\": \"^3.6.2\",\n    \"bare-path\": \"^3.0.0\",\n    \"bare-stream\": \"^2.7.0\",\n    \"corestore\": \"^7.6.1\",\n    \"dns-query\": \"^0.11.2\",\n    \"hyperbee\": \"^2.26.5\",\n    \"hypercore\": \"^11.20.1\",\n    \"hyperdrive\": \"^13.0.2\",\n    \"hyperswarm\": \"^4.16.0\",\n    \"rocksdb-native\": \"^3.5.10\",\n    \"test-tmp\": \"^1.4.0\",\n    \"z32\": \"^1.1.0\"\n  },\n  \"devDependencies\": {\n    \"@tsconfig/node20\": \"^20.1.8\",\n    \"@types/b4a\": \"^1.6.5\",\n    \"@types/brittle\": \"^3.5.0\",\n    \"@types/compact-encoding\": \"^2.15.0\",\n    \"@types/node\": \"^25.0.3\",\n    \"@types/streamx\": \"^2.9.5\",\n    \"bare\": \"^1.22.0\",\n    \"brittle\": \"^3.7.0\",\n    \"standard\": \"^17.0.0\",\n    \"tape\": \"^5.6.1\",\n    \"tmp-promise\": \"^3.0.3\",\n    \"typescript\": \"^5.9.3\"\n  }\n}\n"
  },
  {
    "path": "test.js",
    "content": "import { test, configure } from 'brittle'\nimport { once } from 'events'\nimport { create } from './index.js'\nimport b4a from 'b4a'\nimport tmp from 'test-tmp'\n\n/** @import Hyperbee from 'hyperbee' */\n\nconst NULL_KEY = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'\nconst NULL_BUFFER = b4a.alloc(32, 0)\nconst NULL_HEX_KEY = NULL_BUFFER.toString('hex')\nconst NULL_URL = `hyper://${NULL_KEY}/`\n\n// Close can take a while\nconst timeout = 120_000\n\nconfigure({ timeout })\n\ntest('Specify storage for sdk', async (t) => {\n  const storage = await tmp()\n  const name = 'example'\n  const data = 'Hello World!'\n  let sdk = await create({ storage })\n  let sdk2 = null\n\n  try {\n    try {\n      sdk2 = await create({ storage })\n      t.fail('Should not be able to load SDK over existing dir')\n    } catch {\n      t.pass('Threw error when opening same storage path twice')\n    } finally {\n      if (sdk2) await sdk2.close()\n    }\n\n    const core1 = await sdk.get(name)\n    const url1 = core1.url\n    await core1.append(data)\n\n    await sdk.close()\n\n    sdk = await create({ storage })\n\n    const core2 = await sdk.get(name)\n    const url2 = core2.url\n\n    t.is(url1, url2, 'Loaded core has same key')\n\n    const contents = await core2.get(0)\n\n    t.alike(contents.toString('utf8'), data, 'Got data back from disk')\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Support storage reuse by default', async (t) => {\n  const storage = await tmp()\n\n  const sdk = await create({ storage })\n  const core = await sdk.get('persist in memory')\n  const key = core.key\n\n  const data = b4a.from('beep')\n  await core.append(data)\n  await core.close()\n  t.ok(core.closed, 'initial core was closed')\n\n  const coreAgain = await sdk.get(key)\n  t.alike(await coreAgain.get(0, { wait: false }), data, 'found persisted data')\n\n  await sdk.close()\n})\n\ntest('Load hypercores by names and urls', async (t) => {\n  const storage = await tmp()\n\n  const sdk = await create({ storage })\n  const name = 'example'\n\n  try {\n    const core = await sdk.get(name)\n\n    t.ok(core, 'Got core for name')\n\n    const toTry = [\n      NULL_KEY,\n      NULL_BUFFER,\n      NULL_HEX_KEY,\n      `hyper://${NULL_KEY}`,\n      `hyper://${NULL_HEX_KEY}`\n    ]\n\n    for (const key of toTry) {\n      // @ts-ignore\n      const core = await sdk.get(key)\n\n      t.ok(core, `Got core for ${key}`)\n      t.is(core.url, NULL_URL, 'Correct URL got loaded')\n    }\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Loading same key twice results in same core', async (t) => {\n  const storage = await tmp()\n\n  const sdk = await create({ storage })\n  const name = 'example'\n\n  try {\n    const core1 = await sdk.get(name)\n    const core2 = await sdk.get(core1.key)\n    const core3 = await sdk.get(core1.url)\n    t.is(core1, core2, 'Key loaded same core from memory')\n    t.is(core1, core3, 'URL loaded same core from memory')\n\n    const drive1 = await sdk.getDrive(name)\n    const drive2 = await sdk.getDrive(drive1.key)\n    const drive3 = await sdk.getDrive(drive1.url)\n    t.is(drive1, drive2, 'Key loaded same drive from memory')\n    t.is(drive1, drive3, 'URL loaded same drive from memory')\n\n    const bee1 = await sdk.getBee(name)\n    const bee2 = await sdk.getBee(bee1.key)\n    const bee3 = await sdk.getBee(bee1.url)\n    t.is(bee1, bee2, 'Key loaded same bee from memory')\n    t.is(bee1, bee3, 'URL loaded same bee from memory')\n\n    await core1.close()\n    await drive1.close()\n    const core4 = await sdk.get(name)\n    t.not(core1, core4, 'New core after close')\n    const drive4 = await sdk.getDrive(name)\n    t.not(drive1, drive4, 'New drive after close')\n    const bee4 = await sdk.getBee(name)\n    t.not(bee1, bee4, 'New bee after close')\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Resolve DNS entries to keys', async (t) => {\n  const storage = await tmp()\n\n  const expected = NULL_KEY\n\n  const sdk = await create({ storage })\n\n  try {\n    const resolved = await sdk.resolveDNSToKey('example.mauve.moe')\n\n    t.is(resolved, expected, 'Resolved to correct key')\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Resolve DNS in hyper URLs', async (t) => {\n  const storage = await tmp()\n\n  const expected = NULL_KEY\n\n  const sdk = await create({ storage })\n\n  try {\n    const core = await sdk.get('hyper://example.mauve.moe')\n\n    t.is(core.id, expected, 'Loaded correct core from DNSLink')\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Get hostname from cache when fetch fails', async (t) => {\n  const storage = await tmp()\n\n  const expected = NULL_KEY\n\n  const fetch = globalThis.fetch || (await import('bare-fetch')).default\n\n  let isFirst = true\n  let hasFailed = false\n  /** @type {typeof globalThis['fetch']} */\n  function testFetch (...args) {\n    if (isFirst) {\n      isFirst = false\n      return fetch(...args)\n    }\n    hasFailed = true\n    throw new Error('Simulated Network Fail')\n  }\n\n  let sdk = await create({ fetch: testFetch, storage })\n\n  try {\n    const resolved = await sdk.resolveDNSToKey('example.mauve.moe')\n\n    t.is(resolved, expected, 'Resolved to correct key')\n\n    await sdk.close()\n    console.log('close')\n    sdk = await create({ fetch: testFetch, storage })\n\n    const resolved2 = await sdk.resolveDNSToKey('example.mauve.moe')\n\n    t.is(resolved2, expected, 'Resolved to correct key, without network')\n    t.is(hasFailed, true, 'Fetch was called and failed')\n  } finally {\n    await sdk.close()\n  }\n})\n\ntest('Load a core between two peers', async (t) => {\n  const storage1 = await tmp()\n  const storage2 = await tmp()\n\n  const sdk1 = await create({ storage: storage1 })\n  const sdk2 = await create({ storage: storage2 })\n  try {\n    t.comment('Initializing core on first peer')\n\n    const core1 = await sdk1.get('example')\n    await core1.append('Hello World!')\n\n    t.comment('Loading core on second peer')\n\n    const core2 = await sdk2.get(core1.url)\n\n    t.ok(core2.peers?.length, 'Found peer')\n    t.is(core2.url, core1.url, 'Got expected URL')\n    t.is(core2.length, 1, 'Not empty')\n\n    const data = await core2.get(0)\n    t.alike(data, Buffer.from('Hello World!'), 'Got block back out')\n  } finally {\n    await Promise.all([\n      sdk1.close(),\n      sdk2.close()\n    ])\n  }\n})\n\ntest('Connect directly between two peers', async (t) => {\n  const storage1 = await tmp()\n  const storage2 = await tmp()\n\n  const sdk1 = await create({ storage: storage1 })\n  const sdk2 = await create({ storage: storage2 })\n\n  const onPeer = once(sdk2, 'peer-add')\n  const onPeernt = once(sdk2, 'peer-remove')\n  try {\n    await sdk1.joinPeer(sdk2.publicKey)\n\n    const [peerInfo] = await onPeer\n\n    t.alike(peerInfo.publicKey, sdk1.publicKey, 'Connected to peer')\n  } finally {\n    await Promise.all([\n      sdk1.close(),\n      sdk2.close()\n    ])\n  }\n\n  await onPeernt\n\n  t.pass('Peer remove event detected')\n})\n\ntest('Get a hyperdrive and share a file', async (t) => {\n  const storage1 = await tmp()\n  const storage2 = await tmp()\n\n  const sdk1 = await create({ storage: storage1 })\n  const sdk2 = await create({ storage: storage2 })\n\n  try {\n    const drive1 = await sdk1.getDrive('example')\n\n    const ws = drive1.createWriteStream('/blob.txt')\n    const onWrote = once(ws, 'close')\n\n    ws.write('Hello, ')\n    ws.write('world!')\n    ws.end()\n\n    await onWrote\n\n    const drive2 = await sdk2.getDrive(drive1.url)\n\n    t.is(drive2.url, drive1.url, 'Loaded drive has same URL')\n\n    const rs = drive2.createReadStream('/blob.txt')\n\n    let data = ''\n    for await (const chunk of rs) {\n      data += chunk.toString('utf8')\n    }\n\n    t.is(data, 'Hello, world!', 'Loaded expected data')\n  } finally {\n    await Promise.all([\n      sdk1.close(),\n      sdk2.close()\n    ])\n  }\n})\n\ntest('Get a hyperbee and share a key value pair', async (t) => {\n  const storage1 = await tmp()\n  const storage2 = await tmp()\n\n  const sdk1 = await create({ storage: storage1 })\n  const sdk2 = await create({ storage: storage2 })\n\n  try {\n    const encodingOpts = { keyEncoding: 'utf-8', valueEncoding: 'utf-8' }\n    // @ts-ignore\n    const db1 = /** @type {Hyperbee<string,string>} */ (await sdk1.getBee('example', encodingOpts))\n\n    await db1.put('hello', 'world')\n\n    // @ts-ignore\n    const db2 = /** @type {Hyperbee<string,string>} */ (await sdk2.getBee(db1.url, encodingOpts))\n    t.is(db2.url, db1.url, 'Loaded bee has same URL')\n    t.is(db2.version, db1.version, 'Loaded bee has same version')\n    const gotValue = await db2.get('hello')\n\n    if (!gotValue) return t.fail('unable to get value for key')\n    const { value } = gotValue\n\n    t.is(value, 'world', 'Got value for key')\n  } finally {\n    await Promise.all([\n      sdk1.close(),\n      sdk2.close()\n    ])\n  }\n})\n\n// test('', async (t) => {})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"es2022\",\n        \"module\": \"nodenext\",\n        \"checkJs\": true,\n        \"allowJs\": true,\n        \"declaration\": true,\n        \"emitDeclarationOnly\": true,\n        \"moduleResolution\": \"nodenext\",\n        \"outDir\": \"dist\",\n    },\n    \"extends\": \"@tsconfig/node20/tsconfig.json\",\n    \"files\": [\"./index.js\",\"./test.js\"],\n    \"include\": [\"./index.js\",\"./test.js\", \"types/*.d.ts\"],\n    \"exclude\": [\"node_modules\", \"index.d.ts\"],\n}"
  },
  {
    "path": "types/corestore.d.ts",
    "content": "declare module \"corestore\" {\n  import type { Connection } from \"hyperswarm\";\n  import type { CoreOpts } from \"hypercore\";\n  import type Hypercore, { Key, KeyPair } from \"hypercore\";\n  import type RocksDB from \"rocksdb-native\";\n  import { Readable } from \"streamx\";\n  interface CoreStoreOpts {\n    writable?: boolean;\n    readOnly?: boolean;\n    primaryKey?: Key;\n    unsafe?: boolean;\n  }\n  type GetOpts = CoreOpts | { name: string } | { key: Key };\n  export default class CoreStore {\n    constructor(storage: string | RocksDB, opts: CoreStoreOpts?);\n    get<DataType>(opts: GetOpts): Hypercore<DataType>;\n    ready(): Promise<void>;\n    close(): Promise<void>;\n    namespace(namespace: string): CoreStore;\n    session(): CoreStore;\n    list(namespace?: string): Readable<Key>;\n    watch(cb: (core: Hypercore) => void);\n    unwatch(cb: (core: Hypercore) => void);\n    suspend(): Promise<void>;\n    resume(): Promise<void>;\n    createKeyPair(name: string): KeyPair;\n    replicate(connection: Connection);\n  }\n}\n"
  },
  {
    "path": "types/hyperbee.d.ts",
    "content": "declare module \"hyperbee\" {\n  import type { EncodingType } from \"hypercore\";\n  import type Hypercore, { Key } from \"hypercore\";\n  import { EventEmitter } from \"events\";\n\n  interface BeeOpts<Key = Buffer, Value = Buffer> {\n    keyEncoding?: EncodingType;\n    valueEncoding?: EncodingType;\n  }\n\n  interface Entry<Key = Buffer, Value = Buffer> {\n    readonly seq: number;\n    readonly key: Key;\n    readonly value: Value;\n  }\n\n  type HistoryEntry<Key, Value> = Entry<Key, Value> & {\n    readonly type: \"put\" | \"del\";\n  };\n\n  interface Range<Key> {\n    gt?: Key;\n    gte?: Key;\n    lt?: Key;\n    lte?: Key;\n  }\n\n  interface BatchOptions<Key, Value> {\n    cas?(prev: Entry<Key, Value>, next: Entry<Key, Value>): boolean;\n  }\n\n  type DBWatcher<Key, Value> = AsyncIterable<\n    Hyperbee<Key, Value> & { close(): Never }\n  > & {\n    ready(): Promise<void>;\n    close(): Promise<void>;\n  };\n\n  type KeyWatcher<Key, Value> = EventEmitter<{ update: [] }> & {\n    readonly node: Entry<Key, Value>;\n    close(): Promise<void>;\n  };\n\n  interface SubOpts<Key = Buffer> {\n    sep?: Buffer;\n    valueEncoding?: EncodingType;\n    keyEncoding?: EncodingType;\n  }\n\n  interface ReadStreamOpts {\n    reverse?: boolean;\n    limit?: number;\n  }\n\n  type HistoryOpts = Range<number> &\n    ReadStreamOpts & {\n      live?: boolean;\n    };\n\n  interface Batch<Key, Value> {\n    put(key: Key, [value]?: Value, [options]?: BatchOptions): Promise<boolean>;\n    get(key: Key): Promise<Entry<Key, Value> | null>;\n    del(key: Key, [options]?: BatchOptions<Key, Value>): Promise<boolean>;\n    flush(): Promise<void>;\n    close(): Promise<void>;\n  }\n\n  export default class Hyperbee<\n    Key = Buffer,\n    Value = Buffer\n  > extends EventEmitter {\n    constructor(core: Hypercore, opts?: BeeOpts<Key, Value>);\n\n    readonly url: string;\n    readonly id: string;\n    readonly writable: boolean;\n    readonly readable: boolean;\n    readonly key: Key;\n    readonly discoveryKey: Key;\n    readonly version: number;\n    readonly core: Hypercore;\n\n    ready(): Promise<void>;\n\n    put(key: Key, [value]?: Value, [options]?: BatchOptions): Promise<boolean>;\n    get(key: Key): Promise<Entry<Key, Value> | null>;\n    del(key: Key, [options]?: BatchOptions<Key, Value>): Promise<boolean>;\n    batch(): Batch<Key, Value>;\n    getBySeq(seq: number, options?: {}): Promise<Entry<Key, Value> | null>;\n    createReadStream(\n      range?: Range<Key>,\n      options?: ReadStreamOpts\n    ): AsyncIterable<Entry<Key, Value>>;\n    peek(\n      range?: Range<Key>,\n      options?: ReadStreamOpts\n    ): Promise<Entry<Key, Value>>;\n    createHistoryStream(\n      options?: HistoryOpts\n    ): AsyncIterable<HistoryEntry<Key, Value>>;\n    createDiffStream(\n      otherVersion: Hyperbee,\n      options?: Range<Key>\n    ): AsyncIterable<{\n      left: Entry<Key, Value> | null;\n      right: Entry<Key, Value> | null;\n    }>;\n    getAndWatch(key: Key, options?: {}): Promise<KeyWatcher<Key, Value>>;\n    watch(range?: Range<Key>): DBWatcher<Key, Value>;\n    checkout(version: number): Hyperbee<Key, Value>;\n    snapshot(): Hyperbee<Key, Value>;\n    sub(prefix: string, options?: SubOpts<Key>): Hyperbee<Key, Value>;\n\n    close(): Promise<void>;\n  }\n}\n"
  },
  {
    "path": "types/hypercore-crypto.d.ts",
    "content": "declare module \"hypercore-crypto\" {\n  import type {KeyPair} from \"hypercore-crypto\";\n\n  interface KeyPair {\n    publicKey: Buffer;\n  }\n  export default {\n    namespace(name: string, count: number[] | number) : Buffer[]\n  }   \n}"
  },
  {
    "path": "types/hypercore.d.ts",
    "content": "declare module \"hypercore\" {\n  import { EventEmitter } from \"node:events\";\n  import type RocksDB from \"rocksdb-native\";\n\n  type Key = Buffer | Uint8Array;\n  interface KeyPair {\n    publicKey: Key;\n    secretKey: Key;\n  }\n\n  type EncodingType = \"json\" | \"utf-8\" | \"binary\";\n  type Stringable = string | { toString(): string };\n\n  // Plain JSON object based on this stack overflow answer\n  // https://stackoverflow.com/a/59647842\n  type Primitive = bigint | boolean | null | number | string;\n  type JSONValue = Primitive | JSONObject | JSONArray;\n  interface JSONObject {\n    [key: string]: JSONValue;\n  }\n  interface JSONArray extends Array<JSONValue> {}\n\n  interface CoreOpts {\n    valueEncoding?: EncodingType;\n    keyPair?: KeyPair;\n    encryption?: { key: Key };\n    timeout?: number;\n    writable?: boolean;\n    inflightRange?: [number, number];\n    userData?: { [key: string]: any };\n    key?: Key;\n  }\n  interface GetOpts {\n    wait?: boolean;\n    onwait?: () => void;\n    timeout?: number;\n    activeRequests?: any[];\n    valueEncoding?: EncodingType;\n    decrypt?: boolean;\n    raw?: boolean;\n  }\n\n  interface Peer {\n    remotePublicKey: Key;\n    readonly paused: boolean;\n    readonly removed: boolean;\n    readonly extensions: Map<string, Extension>\n  }\n\n  interface Extension<Encoding = Buffer | Uint16Array> {\n    send(message: Encoding, peer: Peer): void;\n    broadcast(message: Encoding): void;\n    destroy(): void;\n  }\n\n  interface ExtensionOpts<Encoding = Buffer | Uint8Array> {\n    onmessage: (message: Encoding, peer: Peer) => void;\n  }\n\n  interface HypercoreEvents {\n    close: [];\n    ready: [];\n    append: [];\n    \"peer-add\": [peer: Peer];\n    \"peer-remove\": [peer: Peer];\n    upload: [index: number, byteLength: number, peer: Peer];\n    download: [index: number, byteLength: number, peer: Peer];\n  }\n\n  export default class Hypercore<\n    DataType = string | Buffer | Uint8Array\n  > extends EventEmitter<HypercoreEvents> {\n    constructor(storage: string | RocksDB, opts?: CoreOpts);\n    readonly key: Buffer;\n    readonly url: string;\n    readonly id: string;\n    readonly discoveryKey: Buffer;\n    discovery: object;\n    readonly writable: boolean;\n    readonly length: number;\n    readonly closed: boolean;\n    readonly peers: Peer[];\n    readonly extensions: Map<string, Extension>\n\n    findingPeers(): () => void;\n    ready(): Promise<void>;\n    update(options?: {\n      wait?: false;\n      activeRequests?: any[];\n      force?: false;\n    }): Promise<boolean>;\n    close(options?: { error?: Error }): Promise<void>;\n    get(index: number, opts?: GetOpts): Promise<DataType>;\n    append(\n      data: DataType | DataType[],\n      options?: { writable?: boolean }\n    ): Promise<number>;\n    registerExtension(\n      name: string,\n      extensionOpts: ExtensionOpts<string> & { encoding: \"utf-8\" }\n    ): Extension<Stringable>;\n    registerExtension(\n      name: string,\n      extensionOpts: ExtensionOpts<Buffer | Uint8Array> & {\n        encoding?: \"buffer\";\n      }\n    ): Extension<Buffer | Uint8Array>;\n    registerExtension<Encoding = JSONValue>(\n      name: string,\n      extensionOpts: ExtensionOpts<Encoding> & { encoding: \"json\" }\n    ): Extension<Encoding>;\n  }\n}\n"
  },
  {
    "path": "types/hyperdrive.d.ts",
    "content": "declare module \"hyperdrive\" {\n  import { EventEmitter } from \"node:stream\";\n  import type Hypercore from \"hypercore\";\n  import type Hyperbee from \"hyperbee\";\n  import type CoreStore from \"corestore\";\n\n  interface DriveOpts {\n    key?: Buffer | Uint8Array;\n  }\n\n  interface Entry {\n    seq: number;\n    key: string;\n    value: {\n      executable: boolean;\n      linkname: null | string;\n      blob: {\n        blockOffset: number;\n        blockLength: number;\n        byteOffset: number;\n        byteLength: number;\n      };\n      metadata: Metadata | null;\n    };\n  }\n\n  type Metadata = { [key: string]: any };\n\n  interface WriteOptions {\n    executable?: boolean;\n    metadata: Metadata;\n  }\n\n  interface ReadOptions {\n    wait?: boolean;\n    timeout?: number;\n    start?: number;\n    end?: number;\n    length?: number;\n  }\n\n  interface ListOptions {\n    recursive?: boolean;\n    ignore?: string | string[];\n    wait?: boolean;\n  }\n\n  type EntryOptions = ReadOptions & {\n    follow?: boolean;\n  };\n\n  interface Diff {\n    left: Entry;\n    right: Entry;\n  }\n\n  interface Download {\n    done(): Promise<void>;\n    destroy(): void;\n  }\n\n  interface HypedriveEvents {\n    close: [];\n  }\n\n  type UncloseableDrive = Hyperdrive & {\n    close(): never;\n  };\n\n  interface MirrorDriveOptions {\n    prefix?: string;\n    dryRun?: boolean;\n    prune?: boolean;\n    includeEquals?: boolean;\n    filter: (key: string) => boolean;\n    batch?: boolean;\n    ignore?: string | string[];\n  }\n  interface MirrorEvent {\n    op: \"add\";\n    key: string;\n    bytesRemoved: number;\n    bytesAdded: number;\n  }\n  type MirrorDrive = AsyncIterable<MirrorEvent> & {\n    readonly count: number;\n    done: Promise<void>;\n  };\n\n  export default class Hyperdrive extends EventEmitter<HyperdriveEvents> {\n    readonly url: string;\n    readonly id: string;\n    readonly writable: boolean;\n    readonly readable: boolean;\n    readonly key: Key;\n    readonly discoveryKey: Key;\n    readonly version: number;\n    readonly supportsMetadata: true;\n\n    readonly core: Hypercore;\n    readonly corestore: CoreStore;\n    readonly db: Hyperbee<string, Entry>;\n\n    constructor(\n      store: CoreStore,\n      key: Buffer | Uint8Array | null,\n      opts?: DriveOpts\n    );\n\n    ready(): Promise<void>;\n    close(): Promise<void>;\n\n    put(\n      path: string,\n      buffer: Uint8Array,\n      options?: WriteOptions\n    ): Promise<void>;\n    get(path: string, options?: ReadOptions): Promise<Uint8Array | null>;\n    entry(path: string, options?: EntryOptions): Promise<Entry>;\n    exists(path: string): Promise<boolean>;\n    del(path: string): Promise<void>;\n    compare(entryA: Entry, entryB: Entry): number;\n    clear(path: string, options?: { diff?: boolean }): Promise<number | null>;\n    clearAll(options?: { diff?: boolean }): Promise<number | null>;\n    truncate(\n      version: number,\n      options?: { blobs?: number }\n    ): Promise<void | number>;\n    purge(): Promise<void>;\n    symlink(path: string, linkname: string): Promise<void>;\n    batch(): Hyperdrive & { flush(): Promise<void> };\n    list(folder: string, options?: ListOptions): AsyncIterable<Entry>;\n    readdir(\n      folder: string,\n      options?: { wait?: boolean }\n    ): AsyncIterable<string>;\n    has(path: string): Promise<boolean>;\n    entries: Hyperbee<string, Entry>[\"createReadStream\"];\n    mirror(out: Hyperdrive, options?: MirrorDriveOptions): MirrorDrive;\n    watch(folder?: string): AsyncIterable<[UncloseableDrive, UncloseableDrive]>;\n    ready(): Promise<void>;\n    destroy(): void;\n    createReadStream(path: string, options?: ReadOptions): any;\n    createWriteStream(path: string, options?: WriteOptions): any;\n    download(folder: string, options?: ListOptions): Download;\n    checkout(version: number): Hyperdrive;\n    diff(version: number, folder: string, options?: any): AsyncIterable<Diff>;\n    downloadDiff(version: number, folder: string, options?: any): Download;\n    downloadRange(dbRanges: any, blobRanges: any): Download;\n    findingPeers(): () => void;\n    update(options?: { wait: boolean }): Promise<boolean>;\n  }\n}\n"
  },
  {
    "path": "types/hyperswarm.d.ts",
    "content": "declare module \"hyperswarm\" {\n  import { EventEmitter } from \"stream\";\n  import { Duplex } from \"streamx\";\n  type Key = Buffer | Uint8Array;\n  interface KeyPair {\n    publicKey: Key;\n    secretKey: Key;\n  }\n  interface SwarmOpts {\n    keyPair?: KeyPair;\n    seed?: Key;\n    maxKeys?: number;\n    firewall?: (remotePublicKey: Key) => boolean;\n  }\n  type Connection = Duplex;\n  interface PeerInfo {\n    readonly publicKey: Key;\n    readonly topics: Key[];\n    readonly prioritized: boolean;\n    ban(banStatus: boolean): void;\n  }\n  interface JoinOpts {\n    server?: boolean;\n    client?: boolean;\n  }\n  interface PeerDiscovery {\n    flushed(): Promise<void>;\n    destroy(): Promise<void>;\n    refresh(joinOpts: JoinOpts): Promise<void>;\n  }\n  interface SwarmEvents {\n    close: [];\n    connection: [connection: Connection, peer: PeerInfo];\n    update: [];\n    ban: [peerInfo: PeerInfo, err: Error];\n  }\n  export default class Hyperswarm extends EventEmitter<SwarmEvents> {\n    readonly keyPair: KeyPair;\n    readonly connections: Connection[];\n    readonly peers: Map<string, PeerInfo>;\n\n    constructor(opts?: SwarmOpts);\n\n    flush(): Promise<void>;\n    join(topic: Key, joinOpts?: JoinOpts): PeerDiscovery;\n    leave(topic: Key): Promise<void>;\n    joinPeer(topic: Key): void;\n    leavePeer(topic: Key): void;\n    destroy(): Promise<void>;\n    listen(): Promise<void>;\n    suspend(): Promise<void>;\n    resume(): Promise<void>;\n  }\n}\n"
  },
  {
    "path": "types/rocksdb-native.d.ts",
    "content": "declare module \"rocksdb-native\" {\n  import type { Encoder } from \"compact-encoding\";\n\n  interface SessionOpts {\n    valueEncoding?: Encoder;\n    keyEncoding?: Encoder;\n    columnFamily?: string;\n    readOnly?: boolean;\n  }\n\n  interface IteratorOpts<Key> {\n    gt?: Key;\n    gte?: Key;\n    lt?: Key;\n    lte?: Key;\n    reverse?: boolean;\n    limit?: number;\n    values?: boolean;\n    keys?: boolean;\n  }\n\n  interface Entry<Key, Value> {\n    key: Key;\n    value: Value;\n  }\n\n  /**\n   * RocksDB-Native Instance.\n   * **WARNING**: This is a stub used in hyper-sdk.\n   * Most methods are not yet documented\n   */\n  export default class RocksDB<Key = string, Value = string> {\n    constructor(storageLocation: string, options?: SessionOpts);\n    put(key: Key, value: Value): Promise<void>;\n    get(key: Key): Promise<Value?>;\n    delete(key: Key): Promise<void>;\n    flush(): Promise<void>;\n    close(): Promise<void>;\n    session<SubKey = Key, SubValue = Value>(\n      options?: SessionOpts\n    ): RocksDB<SubKey, SubValue>;\n    iterator(\n      range?: IteratorOpts,\n      options?: IteratorOpts\n    ): AsyncIterable<Entry<Key, Value>>;\n  }\n}\n"
  },
  {
    "path": "types/test-tmp.d.ts",
    "content": "declare module \"test-tmp\" {\n    export default function testTMP(): Promise<string>\n}"
  },
  {
    "path": "types/z32.d.ts",
    "content": "declare module \"z32\" {\n    export function decode(encoded: string) : Buffer\n}"
  }
]