Full Code of datproject/sdk for AI

master b5980211c7bb cached
20 files
61.4 KB
16.6k tokens
109 symbols
1 requests
Download .txt
Repository: datproject/sdk
Branch: master
Commit: b5980211c7bb
Files: 20
Total size: 61.4 KB

Directory structure:
gitextract_wwgwrlvq/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── dist/
│   ├── index.d.ts
│   └── test.d.ts
├── index.js
├── package.json
├── test.js
├── tsconfig.json
└── types/
    ├── corestore.d.ts
    ├── hyperbee.d.ts
    ├── hypercore-crypto.d.ts
    ├── hypercore.d.ts
    ├── hyperdrive.d.ts
    ├── hyperswarm.d.ts
    ├── rocksdb-native.d.ts
    ├── test-tmp.d.ts
    └── z32.d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: Testing

on: [ push, pull_request ]

jobs:
  build:
    strategy:
      matrix:
        node: [ '24' ]
        os: [macos-latest, ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}

    name: Unit tests ${{ matrix.node }} ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node }}
      - run: npm install
      - run: npm run lint
      - run: npm run test:node


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next

package-lock.json
test-bundle.js
bundle.js
dat-sdk-bundle.js

# IDE
.idea

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
 advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
 address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
 professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at hi@datproject.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Dat Project

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# hyper-sdk

A Software Development Kit for the [hypercore-protocol](https://hypercore-protocol.org/)

## Why use this?

Hypercore-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.

The 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.

## Goals

- High level API
- Cross-platform with same codebase
  - ✔ [Node.js](https://nodejs.org/en)
  - ✔ [Electron](https://www.electronjs.org/)
  - ✔ [Pear](https://docs.pears.com/)
  - 🏗️ Web (PRs welcome)

## Installation

Make sure you've set up [Node.js](https://nodejs.org/).

```shell
npm install --save hyper-sdk
# or yarn
```

```js
import * as SDK from "hyper-sdk"
```

## API

### SDK.create()

```JavaScript
const sdk = await SDK.create({
  // This argument is mandatory since Hypercore no longer support in-memory
  // Check out the env-paths module for application specific path storage
  storage: './hyper-sdk',

  // This controls whether the SDK will automatically start swarming when loading a core via `get`
  // Set this to false if you want to have more fine control over peer discovery
  autoJoin: true,

  // Specify options to pass to the Corestore constructor
  // The storage will get derived from the `storage` parameter
  // https://github.com/hypercore-protocol/corestore/
  corestoreOpts: {},

  // Specify options to pass to the hyperswarm constructor
  // The keypair will get derived automatically from the corestore
  // https://github.com/hyperswarm/hyperswarm
  swarmOpts: {},
})
```

### sdk.publicKey

The public key used for identifying this peer in the hyperswarm network.

This is a 32 byte buffer which can be use in conjunction with `sdk.joinPeer()` to connect two peers directly together.

### sdk.connections

The list of active connections to other peers, taken from hyperswarm.

### sdk.peers

The list of active peers.

Each peer has a `publicKey`, and list of `topics`

You can find more docs in the [hyperswarm](https://github.com/hyperswarm/hyperswarm#peerinfo-api) repo.

### sdk.cores

List of active Hypercores.

### sdk.on('peer-add', peerInfo) / sdk.on('peer-remove', peerInfo)

You can listen on when a peer gets connected or disconnected with this event.

You can find more docs in the [hyperswarm](https://github.com/hyperswarm/hyperswarm#peerinfo-api) repo.

```JavaScript
sdk.on('peer-add', (peerInfo) => {
  console.log('Connected to', peerInfo.publicKey, 'on', peerInfo.topics)
})
sdk.on('peer-add', (peerInfo) => {
  console.log('Disconnected from')
})
```

### sdk.get()

You 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.

Unlike corestore, you may not initialize a hypercore from a `null` key since everything must be derivable or loadable.

Unless `autoJoin` is set to `false`, the peer discovery will be automatically started for the core.

```JavaScript
// Derive a key from a "name"
const core = await sdk.get('example name')

// Resolve DNS to a hypercore
const core = await sdk.get('hyper://example.mauve.moe')

// Buffer key, 32 bytes of 0's
const core = await sdk.get(b4a.alloc(32, 0))

// Hex key, equivalent to 32 bytes of zeros
const core = await sdk.get('hyper://0000000000000000000000000000000000000000000000000000000000000000')

// z32 encoded, equivalent to 32 bytes of zeros
const core = await sdk.get('hyper://yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy')

// Don't auto-join the swarm for the core on init
const core = await sdk.get('example', {autoJoin: false})
```

### sdk.getDrive()

You can initialize a [Hyperdrive](https://github.com/holepunchto/hyperdrive-next) instance by passing in the same arguments as in `sdk.get()`.

In addition to the usual `hyperdrive` properties, there's a new `url` property to get the `hyper://` URL for the drive to used elsewhere.

Note that the drives's metadata DB's discovery key will be used for replicating if `autoJoin` is `true`.

Hyperdrive is mostly useful for storing and loading files since it splits the metadata representing the file systema and the blob storage into separate cores.

```JavaScript
const drive = await sdk.getDrive('hyper://blob.mauve.moe')
for(const path of drive.readdir('/')) {
  const stat = drive.stat(path)
}
```

### sdk.getBee()

You can initialize a [Hyperbee](https://github.com/holepunchto/hyperbee) instance by passing the same arguments as in `sdk.get()`.

In addition to the usual `hyperbee` properties, there's a new `url` property to get the `hyper://` URL for the bee to used elsewhere.

Additionally, you should pass in a `keyEncoding` and a `valueEncoding` in order to control the encoding for data that's being written.

Hyperbee is best used when you want to create database indexes.

For an out of the box database with a proper query language, check out [HyperbeeDeeBee](https://github.com/RangerMauve/hyperbeedeebee/).

```JavaScript
const db = await sdk.getBee('example db')

const db = await sdk.getBee('example db', {keyEncoding: 'utf8', valueEncoding: 'json')
await db.put('hello', 'world')

for(const entry of db.createReadStream()) {
  console.log(entry)
}
```

### sdk.resolveDNSToKey()

You can manually resolve DNS addresses to hypercore keys on domains using the DNS Link spec with this method.

However, it's not mandatory to use DNS since `sdk.get()` will automatically detect and perform resolutions of DNS for `hyper://` URLs.

Hyper-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/).

```JavaScript
const key = await sdk.resolveDNSToKey('example.mauve.moe')
```

### sdk.namespace()

Get back a namespaced [Corestore](https://github.com/hypercore-protocol/corestore/) instance which can be passed to things like Hyperdrive.

Note 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.

```JavaScript
import Hypderdrive from "hyperdrive"

const drive = new Hyperdrive(sdk.namespace('example'))

// Wait for the drive to initiailize
await drive.ready()

// Manually trigger peer lookup for this drive
sdk.join(drive.publicKey)
```

### sdk.join() / sdk.leave()

You can manually trigger peer discovery of hypercores as well as stop peer discovery.
This can be done by using the `discoveryKey` of a hypercore, or any 32 byte buffer.

As well, you can use string names for topics in order to discover peers based on a human readable string.
When 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).

```JavaScript
const core = await sdk.get('example', {autoJoin: false})

// Start finding peers without advertising
sdk.join(core.discoveryKey, {server: false})

// Listen on a human readable topic
sdk.join("cool cat videos")

sdk.leave(core.discoveryKey)
sdk.leave("cool cat videos")
```

### sdk.joinPeer() / sdk.leavePeer()

```JavaScript
const sdk1 = await SDK.create({storage: './sdk1'})
const sdk2 = await SDK.create({storage: './sdk1'})

sdk1.joinPeer(sdk2.publicKey)
```

### sdk.close()

This will gracefully close connections, remove advertisements from the DHT, and close any open file handles.

Make sure you invoke this to keep the network fast and to avoid data corruption!

### sdk.suspend()

This will pause network and data operations.
Use it when your app is in the background or the user wants to pause seeding.

### sdk.resume()

Undo the effects of `suspend()`, re-enable network and storage.

## TypeScript Support

This module comes with TypeScript types, but by default Hypercore and other Holepunch libraries do not.

You can add these missing types by importing them from the SDK's `types` folder in your `tsconfig.json` file.

Your `tsconfig.json` file should look something like this:

```
{
    "compilerOptions": {
        "module": "nodenext",
        "moduleResolution": "nodenext",
    },
    "extends": "@tsconfig/node20/tsconfig.json",
    "include": [
        "./test.js",
        "./node_modules/hyper-sdk/types/*.d.ts"
    ]
}
```

================================================
FILE: dist/index.d.ts
================================================
/**
 *
 * @param {object} options
 * @param {string} [options.storage]
 * @param {CoreStoreOpts} [options.corestoreOpts]
 * @param {SwarmOpts} [options.swarmOpts]
 * @param {typeof globalThis["fetch"]} [options.fetch]
 * @param {HyperSwarm} [options.swarm]
 * @param {CoreStore} [options.corestore]
 * @param {RocksDB} [options.dnsCache]
 * @param {CoreOpts} [options.defaultCoreOpts]
 * @param {JoinOpts} [options.defaultJoinOpts]
 * @param {string} [options.dnsResolver]
 * @param {boolean} [options.autoJoin=true]
 * @param {boolean} [options.doReplicate=true]
 * @returns {Promise<SDK>}
 */
export function create({ storage, corestoreOpts, swarmOpts, fetch, ...opts }?: {
    storage?: string | undefined;
    corestoreOpts?: CoreStoreOpts | undefined;
    swarmOpts?: SwarmOpts | undefined;
    fetch?: typeof globalThis.fetch | undefined;
    swarm?: HyperSwarm | undefined;
    corestore?: CoreStore | undefined;
    dnsCache?: RocksDB | undefined;
    defaultCoreOpts?: CoreOpts | undefined;
    defaultJoinOpts?: JoinOpts | undefined;
    dnsResolver?: string | undefined;
    autoJoin?: boolean | undefined;
    doReplicate?: boolean | undefined;
}): Promise<SDK>;
/** @import {JoinOpts,SwarmOpts,PeerDiscovery,Connection} from "hyperswarm" */
/** @import {BeeOpts} from "hyperbee" */
/** @import {CoreOpts} from "hypercore" */
/** @import {DriveOpts} from "hyperdrive" */
/** @import {CoreStoreOpts} from "corestore" */
/** @typedef {Buffer|Uint8Array} Key */
/** @typedef {string|Key} NameOrKeyOrURL */
/** @typedef {{key?:Key|null, name?:string}} ResolvedKeyOrName */
/**
 * @typedef {object} DNSResponse
 * @property {{name: string, data: string}} DNSResponse.Answer
 */
export const HYPER_PROTOCOL_SCHEME: "hyper://";
export const DEFAULT_CORE_OPTS: {};
export namespace DEFAULT_JOIN_OPTS {
    let server: boolean;
    let client: boolean;
}
export const DEFAULT_CORESTORE_OPTS: {};
export const DEFAULT_SWARM_OPTS: {};
export class SDK extends EventEmitter<any> {
    /**
     * @param {object} [options]
     * @param {typeof globalThis["fetch"]} [options.fetch]
     * @param {HyperSwarm} [options.swarm]
     * @param {CoreStore} [options.corestore]
     * @param {CoreOpts} [options.defaultCoreOpts]
     * @param {JoinOpts} [options.defaultJoinOpts]
     * @param {string} [options.dnsResolver]
     * @param {boolean} [options.autoJoin=true]
     * @param {boolean} [options.doReplicate=true]
     * @param {RocksDB} [options.dnsCache]
     */
    constructor({ swarm, corestore, dnsCache, fetch, defaultCoreOpts, defaultJoinOpts, dnsResolver, autoJoin, doReplicate }?: {
        fetch?: typeof globalThis.fetch | undefined;
        swarm?: HyperSwarm | undefined;
        corestore?: CoreStore | undefined;
        defaultCoreOpts?: CoreOpts | undefined;
        defaultJoinOpts?: JoinOpts | undefined;
        dnsResolver?: string | undefined;
        autoJoin?: boolean | undefined;
        doReplicate?: boolean | undefined;
        dnsCache?: RocksDB | undefined;
    });
    autoJoin: boolean;
    get swarm(): HyperSwarm;
    get corestore(): CoreStore;
    get publicKey(): import("hyperswarm").Key;
    get connections(): Connection[];
    get peers(): Map<string, import("hyperswarm").PeerInfo>;
    /**
     * @type {Hypercore[]}
     */
    get cores(): Hypercore[];
    /**
     * @type {Hyperdrive[]}
     */
    get drives(): Hyperdrive[];
    /** @type {Hyperbee[]} */
    get bees(): Hyperbee[];
    /**
     * Resolve DNS names to a hypercore key using the DNSLink spec
     * @param {string} hostname Hostname to resolve, e,g, `agregore.mauve.moe`
     * @returns {Promise<string>}
     */
    resolveDNSToKey(hostname: string): Promise<string>;
    /**
     * Resolves a string to be a key or opts and resolves DNS
     * Useful for hypercore opts or Hyperdrive
     * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or URL to resolve
     * @returns {Promise<ResolvedKeyOrName>}
     */
    resolveNameOrKeyToOpts(nameOrKeyOrURL: NameOrKeyOrURL): Promise<ResolvedKeyOrName>;
    /**
     *
     * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or hyper URL for the bee
     * @param {BeeOpts & CoreOpts & JoinOpts} opts Options for configuring Hyperbee
     * @returns {Promise<Hyperbee>}
     */
    getBee(nameOrKeyOrURL: NameOrKeyOrURL, opts?: BeeOpts & CoreOpts & JoinOpts): Promise<Hyperbee>;
    /**
     *
     * @param {NameOrKeyOrURL} nameOrKeyOrURL
     * @param {DriveOpts&JoinOpts} opts
     * @returns {Promise<Hyperdrive>}
     */
    getDrive(nameOrKeyOrURL: NameOrKeyOrURL, opts?: DriveOpts & JoinOpts): Promise<Hyperdrive>;
    /**
     * @template DataType
     * Get a HyperCore by its name or key or URL
     * @param {NameOrKeyOrURL} nameOrKeyOrURL
     * @param {CoreOpts&JoinOpts} [opts]
     * @returns {Promise<Hypercore<DataType>>}
     */
    get<DataType>(nameOrKeyOrURL: NameOrKeyOrURL, opts?: CoreOpts & JoinOpts): Promise<Hypercore<DataType>>;
    /**
     * Get a sub CoreStore for a given namespace. Use this to derive core names for a particular group
     * @param {string} namespace Namespace to store cores under
     * @returns {CoreStore}
     */
    namespace(namespace: string): CoreStore;
    /**
     * Derive a topic key (for hypercores) from a namespace.
     * @param {string} name Name of the namespace to derive
     * @returns {Key}
     */
    makeTopicKey(name: string): Key;
    /**
     * Start peer discovery on a core. Use this if you created a core on a namespaced CoreStore
     * @param {Hypercore} core
     * @param {JoinOpts} opts
     * @returns {Promise<void>}
     */
    joinCore(core: Hypercore, opts?: JoinOpts): Promise<void>;
    /**
     *
     * @param {string|Key} topic
     * @param {JoinOpts} opts
     * @returns {PeerDiscovery}
     */
    join(topic: string | Key, opts?: JoinOpts): PeerDiscovery;
    /**
     *
     * @param {string|Key} topic
     * @returns {Promise<void>}
     */
    leave(topic: string | Key): Promise<void>;
    /**
     * @param {Key} id
     */
    joinPeer(id: Key): void;
    /**
     * @param {Key} id
     */
    leavePeer(id: Key): void;
    ready(): Promise<void>;
    close(): Promise<void>;
    /**
     * Replicate a connection from hyperswarm manually
     * @param {Connection} connection
     */
    replicate(connection: Connection): void;
    #private;
}
export type Key = Buffer | Uint8Array;
export type NameOrKeyOrURL = string | Key;
export type ResolvedKeyOrName = {
    key?: Key | null;
    name?: string;
};
export type DNSResponse = {
    Answer: {
        name: string;
        data: string;
    };
};
import type { CoreStoreOpts } from "corestore";
import type { SwarmOpts } from "hyperswarm";
import HyperSwarm from 'hyperswarm';
import CoreStore from 'corestore';
import RocksDB from 'rocksdb-native';
import type { CoreOpts } from "hypercore";
import type { JoinOpts } from "hyperswarm";
import { EventEmitter } from 'events';
import type { Connection } from "hyperswarm";
import Hypercore from 'hypercore';
import Hyperdrive from 'hyperdrive';
import Hyperbee from 'hyperbee';
import type { BeeOpts } from "hyperbee";
import type { DriveOpts } from "hyperdrive";
import type { PeerDiscovery } from "hyperswarm";


================================================
FILE: dist/test.d.ts
================================================
export {};


================================================
FILE: index.js
================================================
import HyperSwarm from 'hyperswarm'
import CoreStore from 'corestore'
import Hypercore from 'hypercore'
import Hyperdrive from 'hyperdrive'
import Hyperbee from 'hyperbee'
import crypto from 'hypercore-crypto'
import z32 from 'z32'
import b4a from 'b4a'
import { EventEmitter } from 'events'
import { join } from 'path'
import RocksDB from 'rocksdb-native'

/** @import {JoinOpts,SwarmOpts,PeerDiscovery,Connection} from "hyperswarm" */
/** @import {BeeOpts} from "hyperbee" */
/** @import {CoreOpts} from "hypercore" */
/** @import {DriveOpts} from "hyperdrive" */
/** @import {CoreStoreOpts} from "corestore" */

/** @typedef {Buffer|Uint8Array} Key */
/** @typedef {string|Key} NameOrKeyOrURL */
/** @typedef {{key?:Key|null, name?:string}} ResolvedKeyOrName */
/**
 * @typedef {object} DNSResponse
 * @property {{name: string, data: string}} DNSResponse.Answer
 */

// TODO: Base36 encoding/decoding for URLs instead of hex

export const HYPER_PROTOCOL_SCHEME = 'hyper://'
export const DEFAULT_CORE_OPTS = {}
export const DEFAULT_JOIN_OPTS = {
  server: true,
  client: true
}
export const DEFAULT_CORESTORE_OPTS = {}
export const DEFAULT_SWARM_OPTS = {}

// Monkey-patching with first class URL support
Object.defineProperty(Hypercore.prototype, 'url', {
  get: function () {
    return `${HYPER_PROTOCOL_SCHEME}${this.id}/`
  }
})
Object.defineProperty(Hyperdrive.prototype, 'url', {
  get: function () {
    return `${HYPER_PROTOCOL_SCHEME}${this.core.id}/`
  }
})
Object.defineProperty(Hyperbee.prototype, 'url', {
  get: function () {
    return `${HYPER_PROTOCOL_SCHEME}${this.feed.id}/`
  }
})

const DEFAULT_DNS_RESOLVER = 'https://mozilla.cloudflare-dns.com/dns-query'

const DNSLINK_PREFIX = 'dnslink=/hyper/'

export class SDK extends EventEmitter {
  #fetch
  #dnsCache
  #dnsMemoryCache
  #defaultCoreOpts
  #defaultJoinOpts
  #dnsResolver
  #swarm
  #corestore
  #coreCache
  #beeCache
  #driveCache

  /**
   * @param {object} [options]
   * @param {typeof globalThis["fetch"]} [options.fetch]
   * @param {HyperSwarm} [options.swarm]
   * @param {CoreStore} [options.corestore]
   * @param {CoreOpts} [options.defaultCoreOpts]
   * @param {JoinOpts} [options.defaultJoinOpts]
   * @param {string} [options.dnsResolver]
   * @param {boolean} [options.autoJoin=true]
   * @param {boolean} [options.doReplicate=true]
   * @param {RocksDB} [options.dnsCache]
   */
  constructor ({
    swarm,
    corestore,
    dnsCache,
    fetch = globalThis.fetch,
    defaultCoreOpts = DEFAULT_CORE_OPTS,
    defaultJoinOpts = DEFAULT_JOIN_OPTS,
    dnsResolver = DEFAULT_DNS_RESOLVER,
    autoJoin = true,
    doReplicate = true
  } = {}) {
    super()
    if (!swarm) throw new TypeError('Missing parameter swarm')
    if (!corestore) throw new TypeError('Missing parameter corestore')
    if (!dnsCache) throw new TypeError('Missing parameter dnsCache')
    this.#swarm = swarm
    this.#corestore = corestore
    this.#dnsCache = dnsCache
    this.#fetch = fetch

    // These probably shouldn't be accessed
    this.#dnsMemoryCache = new Map()
    this.#coreCache = new Map()
    this.#beeCache = new Map()
    this.#driveCache = new Map()

    this.#defaultCoreOpts = defaultCoreOpts
    this.#defaultJoinOpts = defaultJoinOpts
    this.#dnsResolver = dnsResolver

    this.autoJoin = autoJoin

    if (doReplicate) {
      swarm.on('connection', (connection, peerInfo) => {
        this.emit('peer-add', peerInfo)
        connection.once('close', () => this.emit('peer-remove', peerInfo))
        this.replicate(connection)
      })
    }
  }

  get swarm () {
    return this.#swarm
  }

  get corestore () {
    return this.#corestore
  }

  get publicKey () {
    return this.#swarm.keyPair.publicKey
  }

  get connections () {
    return this.#swarm.connections
  }

  get peers () {
    return this.#swarm.peers
  }

  /**
   * @type {Hypercore[]}
   */
  get cores () {
    return [...this.#coreCache.values()]
  }

  /**
   * @type {Hyperdrive[]}
   */
  get drives () {
    return [...this.#driveCache.values()]
  }

  /** @type {Hyperbee[]} */
  get bees () {
    return [...this.#beeCache.values()]
  }

  /**
   * Resolve DNS names to a hypercore key using the DNSLink spec
   * @param {string} hostname Hostname to resolve, e,g, `agregore.mauve.moe`
   * @returns {Promise<string>}
   */
  async resolveDNSToKey (hostname) {
    // TODO: Check for TTL?
    if (this.#dnsMemoryCache.has(hostname)) {
      return this.#dnsMemoryCache.get(hostname)
    }

    const fetch = this.#fetch

    const subdomained = `_dnslink.${hostname}`

    const url = `${this.#dnsResolver}?name=${subdomained}&type=TXT`

    let answers = null
    try {
      const response = await fetch(url, {
        headers: { accept: 'application/dns-json' }
      })

      if (!response.ok) {
        throw new Error(
          `Unable to resolve DoH for ${hostname} ${await response.text()}`
        )
      }

      const dnsResults = /** @type {DNSResponse} */ (await response.json())
      answers = dnsResults.Answer
      await this.#dnsCache.put(hostname, JSON.stringify(dnsResults))
    } catch (e) {
      const cached = await this.#dnsCache.get(hostname)
      if (cached) {
        answers = JSON.parse(cached).Answer
      }
    }

    for (let { name, data } of answers) {
      if (name !== subdomained || !data) {
        continue
      }
      if (data.startsWith('"')) {
        data = data.slice(1, -1)
      }
      if (!data.startsWith(DNSLINK_PREFIX)) {
        continue
      }
      const key = data.split('/')[2]
      this.#dnsMemoryCache.set(hostname, key)
      return key
    }

    throw new Error(`DNS-Link Record not found for TXT ${subdomained}`)
  }

  /**
   * Resolves a string to be a key or opts and resolves DNS
   * Useful for hypercore opts or Hyperdrive
   * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or URL to resolve
   * @returns {Promise<ResolvedKeyOrName>}
   */
  async resolveNameOrKeyToOpts (nameOrKeyOrURL) {
    // If a URL, use the hostname as either a key or a DNS to resolve
    // If not a URL, try to decode to a key
    // if not a key, use as name to generate a hypercore
    // Else it's an errorW

    const isKeyString = typeof nameOrKeyOrURL === 'string'
    if (!isKeyString) {
      // If a 32 byte buffer, use it as the key
      if (nameOrKeyOrURL && nameOrKeyOrURL.length === 32) {
        return { key: nameOrKeyOrURL }
      } else {
        throw new Error(
          'Must specify a name, url, or a 32 byte buffer with a key'
        )
      }
    }

    if (nameOrKeyOrURL.startsWith(HYPER_PROTOCOL_SCHEME)) {
      const url = new URL(nameOrKeyOrURL)
      // probably a domain
      if (url.hostname.includes('.')) {
        const key = await this.resolveDNSToKey(url.hostname)

        return { key: stringToKey(key) }
      } else {
        // Try to parse the hostname to a key
        const key = stringToKey(url.hostname)
        if (!key) {
          // If not a key or a domain, throw an error
          throw new Error(
            'URLs must have either an encoded key or a valid DNSlink domain'
          )
        }
        return { key }
      }
    } else {
      const parsed = stringToKey(nameOrKeyOrURL)
      if (parsed) {
        return { key: parsed }
      } else {
        return { name: nameOrKeyOrURL }
      }
    }
  }

  /**
   *
   * @param {NameOrKeyOrURL} nameOrKeyOrURL Name or key or hyper URL for the bee
   * @param {BeeOpts & CoreOpts & JoinOpts} opts Options for configuring Hyperbee
   * @returns {Promise<Hyperbee>}
   */
  async getBee (nameOrKeyOrURL, opts = {}) {
    const core = await this.get(nameOrKeyOrURL, opts)

    if (this.#beeCache.has(core.url)) {
      return this.#beeCache.get(core.url)
    }

    const bee = new Hyperbee(core, opts)

    core.once('close', () => {
      this.#beeCache.delete(core.url)
    })

    this.#beeCache.set(core.url, bee)

    await bee.ready()

    return bee
  }

  /**
   *
   * @param {NameOrKeyOrURL} nameOrKeyOrURL
   * @param {DriveOpts&JoinOpts} opts
   * @returns {Promise<Hyperdrive>}
   */
  async getDrive (nameOrKeyOrURL, opts = {}) {
    const coreOpts = {
      ...this.#defaultCoreOpts,
      autoJoin: this.autoJoin,
      ...opts
    }

    const resolvedOpts = await this.resolveNameOrKeyToOpts(nameOrKeyOrURL)

    const { key, name } = resolvedOpts
    let stringKey = key && key.toString('hex')

    if (this.#driveCache.has(name)) {
      return this.#driveCache.get(name)
    } else if (this.#driveCache.has(stringKey)) {
      return this.#driveCache.get(stringKey)
    }

    Object.assign(coreOpts, resolvedOpts)

    let corestore = this.corestore

    if (stringKey) {
      corestore = this.namespace(stringKey)
    } else if (name) {
      corestore = this.namespace(name)
    } else {
      throw new Error('Unable to parse')
    }

    const drive = new Hyperdrive(corestore, key || null)

    await drive.ready()

    const core = drive.core
    stringKey = core.key.toString('hex')

    drive.once('close', () => {
      this.#driveCache.delete(stringKey)
      this.#driveCache.delete(name)
    })

    this.#driveCache.set(stringKey, drive)
    if (name) this.#driveCache.set(name, drive)

    if (coreOpts.autoJoin && !core.discovery) {
      await this.joinCore(core, opts)
    }

    return drive
  }

  /**
   * @template DataType
   * Get a HyperCore by its name or key or URL
   * @param {NameOrKeyOrURL} nameOrKeyOrURL
   * @param {CoreOpts&JoinOpts} [opts]
   * @returns {Promise<Hypercore<DataType>>}
   */
  async get (nameOrKeyOrURL, opts = {}) {
    const coreOpts = {
      ...this.#defaultCoreOpts,
      autoJoin: this.autoJoin,
      ...opts
    }

    const resolvedOpts = await this.resolveNameOrKeyToOpts(nameOrKeyOrURL)

    const { key, name } = resolvedOpts
    let stringKey = key && key.toString('hex')

    if (this.#coreCache.has(name)) {
      return this.#coreCache.get(name)
    } else if (this.#coreCache.has(stringKey)) {
      return this.#coreCache.get(stringKey)
    }

    Object.assign(coreOpts, resolvedOpts)

    // There shouldn't be a way to pass null for the key
    const core = this.corestore.get(coreOpts)

    // Await for core to be ready
    await core.ready()

    core.once('close', () => {
      this.#coreCache.delete(stringKey)
      this.#coreCache.delete(name)
    })

    stringKey = core.key.toString('hex')

    this.#coreCache.set(stringKey, core)
    if (name) this.#coreCache.set(name, core)

    if (coreOpts.autoJoin && !core.discovery) {
      await this.joinCore(core, opts)
    }

    return core
  }

  /**
   * Get a sub CoreStore for a given namespace. Use this to derive core names for a particular group
   * @param {string} namespace Namespace to store cores under
   * @returns {CoreStore}
   */
  namespace (namespace) {
    return this.corestore.namespace(namespace)
  }

  /**
   * Derive a topic key (for hypercores) from a namespace.
   * @param {string} name Name of the namespace to derive
   * @returns {Key}
   */
  makeTopicKey (name) {
    const [key] = crypto.namespace(name, 1)
    return key
  }

  /**
   * Start peer discovery on a core. Use this if you created a core on a namespaced CoreStore
   * @param {Hypercore} core
   * @param {JoinOpts} opts
   * @returns {Promise<void>}
   */
  async joinCore (core, opts = {}) {
    if (core.discovery) return
    const discovery = this.join(core.discoveryKey, opts)
    core.discovery = discovery

    // If we're the owner, then we wait until is fully announced
    if (core.writable) {
      await discovery.flushed()
    }

    // Await for initial peer for new readable cores
    if (!core.writable && !core.length) {
      const done = core.findingPeers()
      this.swarm.flush().then(done)
      await core.update()
    }

    core.once('close', () => {
      discovery.destroy()
    })
  }

  /**
   *
   * @param {string|Key} topic
   * @param {JoinOpts} opts
   * @returns {PeerDiscovery}
   */
  join (topic, opts = {}) {
    if (typeof topic === 'string') {
      return this.join(this.makeTopicKey(topic), opts)
    }
    const joinOpts = { ...this.#defaultJoinOpts, ...opts }
    return this.swarm.join(topic, joinOpts)
  }

  /**
   *
   * @param {string|Key} topic
   * @returns {Promise<void>}
   */
  leave (topic) {
    if (typeof topic === 'string') {
      return this.leave(this.makeTopicKey(topic))
    }
    return this.swarm.leave(topic)
  }

  /**
   * @param {Key} id
   */
  joinPeer (id) {
    this.swarm.joinPeer(id)
  }

  /**
   * @param {Key} id
   */
  leavePeer (id) {
    this.swarm.leavePeer(id)
  }

  async ready () {
    // Wait for the network to be configured?
    await this.corestore.ready()
    await this.swarm.listen()
  }

  async close () {
    await this.#dnsCache.flush()
    // Close corestore, close hyperswarm
    await Promise.all([
      this.corestore.close(),
      this.swarm.destroy(),
      this.#dnsCache.close()
    ])
  }

  /**
   * Persist any pending transactions to disk and pause network activity.
   * Use this when the app goes in the background or you want to pause seeding.
   */
  async suspend() {
    await this.swarm.suspend()
    await this.corestore.suspend()
  }

  /**
   * Resume network interactions and re-enable storage after suspending.
   */
  async resume() {
    await this.corestore.resume()
    await this.swarm.resume()
  }

  /**
   * Replicate a connection from hyperswarm manually
   * @param {Connection} connection
   */
  replicate (connection) {
    this.corestore.replicate(connection)
  }
}

/**
 * Initialize the SDK
 * @param {object} options
 * @param {string} options.storage
 * @param {CoreStoreOpts} [options.corestoreOpts]
 * @param {SwarmOpts} [options.swarmOpts]
 * @param {typeof globalThis["fetch"]} [options.fetch]
 * @param {HyperSwarm} [options.swarm]
 * @param {CoreStore} [options.corestore]
 * @param {RocksDB} [options.dnsCache]
 * @param {CoreOpts} [options.defaultCoreOpts]
 * @param {JoinOpts} [options.defaultJoinOpts]
 * @param {string} [options.dnsResolver]
 * @param {boolean} [options.autoJoin=true]
 * @param {boolean} [options.doReplicate=true]
 * @returns {Promise<SDK>}
 */
export async function create ({
  storage,
  corestoreOpts = DEFAULT_CORESTORE_OPTS,
  swarmOpts = DEFAULT_SWARM_OPTS,
  fetch = globalThis.fetch,
  ...opts
} = {storage: ''}) {
  if (!storage) {
    throw new Error('Storage parameter is required to be a valid file path')
  }
  const corestore =
    opts.corestore || new CoreStore(storage, { ...corestoreOpts })

  const dnsCache = opts.dnsCache || new RocksDB(join(storage, 'dnsCache'))

  const networkKeypair = await corestore.createKeyPair('noise')

  const swarm =
    opts.swarm ||
    new HyperSwarm({
      keyPair: networkKeypair,
      ...swarmOpts
    })

  const sdk = new SDK({
    ...opts,
    fetch: fetch || (await import('bare-fetch')).default,
    corestore,
    swarm,
    dnsCache
  })

  await sdk.ready()

  return sdk
}

/**
 * @param {string} string
 * @returns {Key|null}
 */
function stringToKey (string) {
  if (string.length === 52) {
    try {
      return z32.decode(string)
    } catch {
      // Not formatted properly, probs a name?
    }
  } else if (string.length === 64) {
    // Parse as hex key
    try {
      return b4a.from(string, 'hex')
    } catch {
      // Not formatted properly, probs a name?
    }
  }
  return null
}


================================================
FILE: package.json
================================================
{
  "name": "hyper-sdk",
  "version": "6.2.2",
  "description": "A Software Development Kit for the Hypercore-Protocol",
  "type": "module",
  "exports": {
    ".": {
      "default": "./index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "imports": {
    "events": {
      "default": "bare-events"
    },
    "stream": {
      "default": "bare-stream"
    },
    "path": {
      "default": "bare-path"
    }
  },
  "types": "dist",
  "scripts": {
    "test": "npm run test:node && npm run test:bare",
    "test:bare": "bare test.js",
    "test:node": "node test.js",
    "lint": "standard --fix && tsc",
    "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"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/rangermauve/hyper-sdk.git"
  },
  "keywords": [
    "dat",
    "sdk",
    "hyperdrive",
    "hypercore",
    "hypercore-protocol",
    "p2p"
  ],
  "author": "RangerMauve",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/rangermauve/hyper-sdk/issues"
  },
  "homepage": "https://github.com/rangermauve/hyper-sdk#readme",
  "dependencies": {
    "b4a": "^1.7.3",
    "bare-events": "^2.8.2",
    "bare-fetch": "^2.5.1",
    "bare-os": "^3.6.2",
    "bare-path": "^3.0.0",
    "bare-stream": "^2.7.0",
    "corestore": "^7.6.1",
    "dns-query": "^0.11.2",
    "hyperbee": "^2.26.5",
    "hypercore": "^11.20.1",
    "hyperdrive": "^13.0.2",
    "hyperswarm": "^4.16.0",
    "rocksdb-native": "^3.5.10",
    "test-tmp": "^1.4.0",
    "z32": "^1.1.0"
  },
  "devDependencies": {
    "@tsconfig/node20": "^20.1.8",
    "@types/b4a": "^1.6.5",
    "@types/brittle": "^3.5.0",
    "@types/compact-encoding": "^2.15.0",
    "@types/node": "^25.0.3",
    "@types/streamx": "^2.9.5",
    "bare": "^1.22.0",
    "brittle": "^3.7.0",
    "standard": "^17.0.0",
    "tape": "^5.6.1",
    "tmp-promise": "^3.0.3",
    "typescript": "^5.9.3"
  }
}


================================================
FILE: test.js
================================================
import { test, configure } from 'brittle'
import { once } from 'events'
import { create } from './index.js'
import b4a from 'b4a'
import tmp from 'test-tmp'

/** @import Hyperbee from 'hyperbee' */

const NULL_KEY = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
const NULL_BUFFER = b4a.alloc(32, 0)
const NULL_HEX_KEY = NULL_BUFFER.toString('hex')
const NULL_URL = `hyper://${NULL_KEY}/`

// Close can take a while
const timeout = 120_000

configure({ timeout })

test('Specify storage for sdk', async (t) => {
  const storage = await tmp()
  const name = 'example'
  const data = 'Hello World!'
  let sdk = await create({ storage })
  let sdk2 = null

  try {
    try {
      sdk2 = await create({ storage })
      t.fail('Should not be able to load SDK over existing dir')
    } catch {
      t.pass('Threw error when opening same storage path twice')
    } finally {
      if (sdk2) await sdk2.close()
    }

    const core1 = await sdk.get(name)
    const url1 = core1.url
    await core1.append(data)

    await sdk.close()

    sdk = await create({ storage })

    const core2 = await sdk.get(name)
    const url2 = core2.url

    t.is(url1, url2, 'Loaded core has same key')

    const contents = await core2.get(0)

    t.alike(contents.toString('utf8'), data, 'Got data back from disk')
  } finally {
    await sdk.close()
  }
})

test('Support storage reuse by default', async (t) => {
  const storage = await tmp()

  const sdk = await create({ storage })
  const core = await sdk.get('persist in memory')
  const key = core.key

  const data = b4a.from('beep')
  await core.append(data)
  await core.close()
  t.ok(core.closed, 'initial core was closed')

  const coreAgain = await sdk.get(key)
  t.alike(await coreAgain.get(0, { wait: false }), data, 'found persisted data')

  await sdk.close()
})

test('Load hypercores by names and urls', async (t) => {
  const storage = await tmp()

  const sdk = await create({ storage })
  const name = 'example'

  try {
    const core = await sdk.get(name)

    t.ok(core, 'Got core for name')

    const toTry = [
      NULL_KEY,
      NULL_BUFFER,
      NULL_HEX_KEY,
      `hyper://${NULL_KEY}`,
      `hyper://${NULL_HEX_KEY}`
    ]

    for (const key of toTry) {
      // @ts-ignore
      const core = await sdk.get(key)

      t.ok(core, `Got core for ${key}`)
      t.is(core.url, NULL_URL, 'Correct URL got loaded')
    }
  } finally {
    await sdk.close()
  }
})

test('Loading same key twice results in same core', async (t) => {
  const storage = await tmp()

  const sdk = await create({ storage })
  const name = 'example'

  try {
    const core1 = await sdk.get(name)
    const core2 = await sdk.get(core1.key)
    const core3 = await sdk.get(core1.url)
    t.is(core1, core2, 'Key loaded same core from memory')
    t.is(core1, core3, 'URL loaded same core from memory')

    const drive1 = await sdk.getDrive(name)
    const drive2 = await sdk.getDrive(drive1.key)
    const drive3 = await sdk.getDrive(drive1.url)
    t.is(drive1, drive2, 'Key loaded same drive from memory')
    t.is(drive1, drive3, 'URL loaded same drive from memory')

    const bee1 = await sdk.getBee(name)
    const bee2 = await sdk.getBee(bee1.key)
    const bee3 = await sdk.getBee(bee1.url)
    t.is(bee1, bee2, 'Key loaded same bee from memory')
    t.is(bee1, bee3, 'URL loaded same bee from memory')

    await core1.close()
    await drive1.close()
    const core4 = await sdk.get(name)
    t.not(core1, core4, 'New core after close')
    const drive4 = await sdk.getDrive(name)
    t.not(drive1, drive4, 'New drive after close')
    const bee4 = await sdk.getBee(name)
    t.not(bee1, bee4, 'New bee after close')
  } finally {
    await sdk.close()
  }
})

test('Resolve DNS entries to keys', async (t) => {
  const storage = await tmp()

  const expected = NULL_KEY

  const sdk = await create({ storage })

  try {
    const resolved = await sdk.resolveDNSToKey('example.mauve.moe')

    t.is(resolved, expected, 'Resolved to correct key')
  } finally {
    await sdk.close()
  }
})

test('Resolve DNS in hyper URLs', async (t) => {
  const storage = await tmp()

  const expected = NULL_KEY

  const sdk = await create({ storage })

  try {
    const core = await sdk.get('hyper://example.mauve.moe')

    t.is(core.id, expected, 'Loaded correct core from DNSLink')
  } finally {
    await sdk.close()
  }
})

test('Get hostname from cache when fetch fails', async (t) => {
  const storage = await tmp()

  const expected = NULL_KEY

  const fetch = globalThis.fetch || (await import('bare-fetch')).default

  let isFirst = true
  let hasFailed = false
  /** @type {typeof globalThis['fetch']} */
  function testFetch (...args) {
    if (isFirst) {
      isFirst = false
      return fetch(...args)
    }
    hasFailed = true
    throw new Error('Simulated Network Fail')
  }

  let sdk = await create({ fetch: testFetch, storage })

  try {
    const resolved = await sdk.resolveDNSToKey('example.mauve.moe')

    t.is(resolved, expected, 'Resolved to correct key')

    await sdk.close()
    console.log('close')
    sdk = await create({ fetch: testFetch, storage })

    const resolved2 = await sdk.resolveDNSToKey('example.mauve.moe')

    t.is(resolved2, expected, 'Resolved to correct key, without network')
    t.is(hasFailed, true, 'Fetch was called and failed')
  } finally {
    await sdk.close()
  }
})

test('Load a core between two peers', async (t) => {
  const storage1 = await tmp()
  const storage2 = await tmp()

  const sdk1 = await create({ storage: storage1 })
  const sdk2 = await create({ storage: storage2 })
  try {
    t.comment('Initializing core on first peer')

    const core1 = await sdk1.get('example')
    await core1.append('Hello World!')

    t.comment('Loading core on second peer')

    const core2 = await sdk2.get(core1.url)

    t.ok(core2.peers?.length, 'Found peer')
    t.is(core2.url, core1.url, 'Got expected URL')
    t.is(core2.length, 1, 'Not empty')

    const data = await core2.get(0)
    t.alike(data, Buffer.from('Hello World!'), 'Got block back out')
  } finally {
    await Promise.all([
      sdk1.close(),
      sdk2.close()
    ])
  }
})

test('Connect directly between two peers', async (t) => {
  const storage1 = await tmp()
  const storage2 = await tmp()

  const sdk1 = await create({ storage: storage1 })
  const sdk2 = await create({ storage: storage2 })

  const onPeer = once(sdk2, 'peer-add')
  const onPeernt = once(sdk2, 'peer-remove')
  try {
    await sdk1.joinPeer(sdk2.publicKey)

    const [peerInfo] = await onPeer

    t.alike(peerInfo.publicKey, sdk1.publicKey, 'Connected to peer')
  } finally {
    await Promise.all([
      sdk1.close(),
      sdk2.close()
    ])
  }

  await onPeernt

  t.pass('Peer remove event detected')
})

test('Get a hyperdrive and share a file', async (t) => {
  const storage1 = await tmp()
  const storage2 = await tmp()

  const sdk1 = await create({ storage: storage1 })
  const sdk2 = await create({ storage: storage2 })

  try {
    const drive1 = await sdk1.getDrive('example')

    const ws = drive1.createWriteStream('/blob.txt')
    const onWrote = once(ws, 'close')

    ws.write('Hello, ')
    ws.write('world!')
    ws.end()

    await onWrote

    const drive2 = await sdk2.getDrive(drive1.url)

    t.is(drive2.url, drive1.url, 'Loaded drive has same URL')

    const rs = drive2.createReadStream('/blob.txt')

    let data = ''
    for await (const chunk of rs) {
      data += chunk.toString('utf8')
    }

    t.is(data, 'Hello, world!', 'Loaded expected data')
  } finally {
    await Promise.all([
      sdk1.close(),
      sdk2.close()
    ])
  }
})

test('Get a hyperbee and share a key value pair', async (t) => {
  const storage1 = await tmp()
  const storage2 = await tmp()

  const sdk1 = await create({ storage: storage1 })
  const sdk2 = await create({ storage: storage2 })

  try {
    const encodingOpts = { keyEncoding: 'utf-8', valueEncoding: 'utf-8' }
    // @ts-ignore
    const db1 = /** @type {Hyperbee<string,string>} */ (await sdk1.getBee('example', encodingOpts))

    await db1.put('hello', 'world')

    // @ts-ignore
    const db2 = /** @type {Hyperbee<string,string>} */ (await sdk2.getBee(db1.url, encodingOpts))
    t.is(db2.url, db1.url, 'Loaded bee has same URL')
    t.is(db2.version, db1.version, 'Loaded bee has same version')
    const gotValue = await db2.get('hello')

    if (!gotValue) return t.fail('unable to get value for key')
    const { value } = gotValue

    t.is(value, 'world', 'Got value for key')
  } finally {
    await Promise.all([
      sdk1.close(),
      sdk2.close()
    ])
  }
})

// test('', async (t) => {})


================================================
FILE: tsconfig.json
================================================
{
    "compilerOptions": {
        "target": "es2022",
        "module": "nodenext",
        "checkJs": true,
        "allowJs": true,
        "declaration": true,
        "emitDeclarationOnly": true,
        "moduleResolution": "nodenext",
        "outDir": "dist",
    },
    "extends": "@tsconfig/node20/tsconfig.json",
    "files": ["./index.js","./test.js"],
    "include": ["./index.js","./test.js", "types/*.d.ts"],
    "exclude": ["node_modules", "index.d.ts"],
}

================================================
FILE: types/corestore.d.ts
================================================
declare module "corestore" {
  import type { Connection } from "hyperswarm";
  import type { CoreOpts } from "hypercore";
  import type Hypercore, { Key, KeyPair } from "hypercore";
  import type RocksDB from "rocksdb-native";
  import { Readable } from "streamx";
  interface CoreStoreOpts {
    writable?: boolean;
    readOnly?: boolean;
    primaryKey?: Key;
    unsafe?: boolean;
  }
  type GetOpts = CoreOpts | { name: string } | { key: Key };
  export default class CoreStore {
    constructor(storage: string | RocksDB, opts: CoreStoreOpts?);
    get<DataType>(opts: GetOpts): Hypercore<DataType>;
    ready(): Promise<void>;
    close(): Promise<void>;
    namespace(namespace: string): CoreStore;
    session(): CoreStore;
    list(namespace?: string): Readable<Key>;
    watch(cb: (core: Hypercore) => void);
    unwatch(cb: (core: Hypercore) => void);
    suspend(): Promise<void>;
    resume(): Promise<void>;
    createKeyPair(name: string): KeyPair;
    replicate(connection: Connection);
  }
}


================================================
FILE: types/hyperbee.d.ts
================================================
declare module "hyperbee" {
  import type { EncodingType } from "hypercore";
  import type Hypercore, { Key } from "hypercore";
  import { EventEmitter } from "events";

  interface BeeOpts<Key = Buffer, Value = Buffer> {
    keyEncoding?: EncodingType;
    valueEncoding?: EncodingType;
  }

  interface Entry<Key = Buffer, Value = Buffer> {
    readonly seq: number;
    readonly key: Key;
    readonly value: Value;
  }

  type HistoryEntry<Key, Value> = Entry<Key, Value> & {
    readonly type: "put" | "del";
  };

  interface Range<Key> {
    gt?: Key;
    gte?: Key;
    lt?: Key;
    lte?: Key;
  }

  interface BatchOptions<Key, Value> {
    cas?(prev: Entry<Key, Value>, next: Entry<Key, Value>): boolean;
  }

  type DBWatcher<Key, Value> = AsyncIterable<
    Hyperbee<Key, Value> & { close(): Never }
  > & {
    ready(): Promise<void>;
    close(): Promise<void>;
  };

  type KeyWatcher<Key, Value> = EventEmitter<{ update: [] }> & {
    readonly node: Entry<Key, Value>;
    close(): Promise<void>;
  };

  interface SubOpts<Key = Buffer> {
    sep?: Buffer;
    valueEncoding?: EncodingType;
    keyEncoding?: EncodingType;
  }

  interface ReadStreamOpts {
    reverse?: boolean;
    limit?: number;
  }

  type HistoryOpts = Range<number> &
    ReadStreamOpts & {
      live?: boolean;
    };

  interface Batch<Key, Value> {
    put(key: Key, [value]?: Value, [options]?: BatchOptions): Promise<boolean>;
    get(key: Key): Promise<Entry<Key, Value> | null>;
    del(key: Key, [options]?: BatchOptions<Key, Value>): Promise<boolean>;
    flush(): Promise<void>;
    close(): Promise<void>;
  }

  export default class Hyperbee<
    Key = Buffer,
    Value = Buffer
  > extends EventEmitter {
    constructor(core: Hypercore, opts?: BeeOpts<Key, Value>);

    readonly url: string;
    readonly id: string;
    readonly writable: boolean;
    readonly readable: boolean;
    readonly key: Key;
    readonly discoveryKey: Key;
    readonly version: number;
    readonly core: Hypercore;

    ready(): Promise<void>;

    put(key: Key, [value]?: Value, [options]?: BatchOptions): Promise<boolean>;
    get(key: Key): Promise<Entry<Key, Value> | null>;
    del(key: Key, [options]?: BatchOptions<Key, Value>): Promise<boolean>;
    batch(): Batch<Key, Value>;
    getBySeq(seq: number, options?: {}): Promise<Entry<Key, Value> | null>;
    createReadStream(
      range?: Range<Key>,
      options?: ReadStreamOpts
    ): AsyncIterable<Entry<Key, Value>>;
    peek(
      range?: Range<Key>,
      options?: ReadStreamOpts
    ): Promise<Entry<Key, Value>>;
    createHistoryStream(
      options?: HistoryOpts
    ): AsyncIterable<HistoryEntry<Key, Value>>;
    createDiffStream(
      otherVersion: Hyperbee,
      options?: Range<Key>
    ): AsyncIterable<{
      left: Entry<Key, Value> | null;
      right: Entry<Key, Value> | null;
    }>;
    getAndWatch(key: Key, options?: {}): Promise<KeyWatcher<Key, Value>>;
    watch(range?: Range<Key>): DBWatcher<Key, Value>;
    checkout(version: number): Hyperbee<Key, Value>;
    snapshot(): Hyperbee<Key, Value>;
    sub(prefix: string, options?: SubOpts<Key>): Hyperbee<Key, Value>;

    close(): Promise<void>;
  }
}


================================================
FILE: types/hypercore-crypto.d.ts
================================================
declare module "hypercore-crypto" {
  import type {KeyPair} from "hypercore-crypto";

  interface KeyPair {
    publicKey: Buffer;
  }
  export default {
    namespace(name: string, count: number[] | number) : Buffer[]
  }   
}

================================================
FILE: types/hypercore.d.ts
================================================
declare module "hypercore" {
  import { EventEmitter } from "node:events";
  import type RocksDB from "rocksdb-native";

  type Key = Buffer | Uint8Array;
  interface KeyPair {
    publicKey: Key;
    secretKey: Key;
  }

  type EncodingType = "json" | "utf-8" | "binary";
  type Stringable = string | { toString(): string };

  // Plain JSON object based on this stack overflow answer
  // https://stackoverflow.com/a/59647842
  type Primitive = bigint | boolean | null | number | string;
  type JSONValue = Primitive | JSONObject | JSONArray;
  interface JSONObject {
    [key: string]: JSONValue;
  }
  interface JSONArray extends Array<JSONValue> {}

  interface CoreOpts {
    valueEncoding?: EncodingType;
    keyPair?: KeyPair;
    encryption?: { key: Key };
    timeout?: number;
    writable?: boolean;
    inflightRange?: [number, number];
    userData?: { [key: string]: any };
    key?: Key;
  }
  interface GetOpts {
    wait?: boolean;
    onwait?: () => void;
    timeout?: number;
    activeRequests?: any[];
    valueEncoding?: EncodingType;
    decrypt?: boolean;
    raw?: boolean;
  }

  interface Peer {
    remotePublicKey: Key;
    readonly paused: boolean;
    readonly removed: boolean;
    readonly extensions: Map<string, Extension>
  }

  interface Extension<Encoding = Buffer | Uint16Array> {
    send(message: Encoding, peer: Peer): void;
    broadcast(message: Encoding): void;
    destroy(): void;
  }

  interface ExtensionOpts<Encoding = Buffer | Uint8Array> {
    onmessage: (message: Encoding, peer: Peer) => void;
  }

  interface HypercoreEvents {
    close: [];
    ready: [];
    append: [];
    "peer-add": [peer: Peer];
    "peer-remove": [peer: Peer];
    upload: [index: number, byteLength: number, peer: Peer];
    download: [index: number, byteLength: number, peer: Peer];
  }

  export default class Hypercore<
    DataType = string | Buffer | Uint8Array
  > extends EventEmitter<HypercoreEvents> {
    constructor(storage: string | RocksDB, opts?: CoreOpts);
    readonly key: Buffer;
    readonly url: string;
    readonly id: string;
    readonly discoveryKey: Buffer;
    discovery: object;
    readonly writable: boolean;
    readonly length: number;
    readonly closed: boolean;
    readonly peers: Peer[];
    readonly extensions: Map<string, Extension>

    findingPeers(): () => void;
    ready(): Promise<void>;
    update(options?: {
      wait?: false;
      activeRequests?: any[];
      force?: false;
    }): Promise<boolean>;
    close(options?: { error?: Error }): Promise<void>;
    get(index: number, opts?: GetOpts): Promise<DataType>;
    append(
      data: DataType | DataType[],
      options?: { writable?: boolean }
    ): Promise<number>;
    registerExtension(
      name: string,
      extensionOpts: ExtensionOpts<string> & { encoding: "utf-8" }
    ): Extension<Stringable>;
    registerExtension(
      name: string,
      extensionOpts: ExtensionOpts<Buffer | Uint8Array> & {
        encoding?: "buffer";
      }
    ): Extension<Buffer | Uint8Array>;
    registerExtension<Encoding = JSONValue>(
      name: string,
      extensionOpts: ExtensionOpts<Encoding> & { encoding: "json" }
    ): Extension<Encoding>;
  }
}


================================================
FILE: types/hyperdrive.d.ts
================================================
declare module "hyperdrive" {
  import { EventEmitter } from "node:stream";
  import type Hypercore from "hypercore";
  import type Hyperbee from "hyperbee";
  import type CoreStore from "corestore";

  interface DriveOpts {
    key?: Buffer | Uint8Array;
  }

  interface Entry {
    seq: number;
    key: string;
    value: {
      executable: boolean;
      linkname: null | string;
      blob: {
        blockOffset: number;
        blockLength: number;
        byteOffset: number;
        byteLength: number;
      };
      metadata: Metadata | null;
    };
  }

  type Metadata = { [key: string]: any };

  interface WriteOptions {
    executable?: boolean;
    metadata: Metadata;
  }

  interface ReadOptions {
    wait?: boolean;
    timeout?: number;
    start?: number;
    end?: number;
    length?: number;
  }

  interface ListOptions {
    recursive?: boolean;
    ignore?: string | string[];
    wait?: boolean;
  }

  type EntryOptions = ReadOptions & {
    follow?: boolean;
  };

  interface Diff {
    left: Entry;
    right: Entry;
  }

  interface Download {
    done(): Promise<void>;
    destroy(): void;
  }

  interface HypedriveEvents {
    close: [];
  }

  type UncloseableDrive = Hyperdrive & {
    close(): never;
  };

  interface MirrorDriveOptions {
    prefix?: string;
    dryRun?: boolean;
    prune?: boolean;
    includeEquals?: boolean;
    filter: (key: string) => boolean;
    batch?: boolean;
    ignore?: string | string[];
  }
  interface MirrorEvent {
    op: "add";
    key: string;
    bytesRemoved: number;
    bytesAdded: number;
  }
  type MirrorDrive = AsyncIterable<MirrorEvent> & {
    readonly count: number;
    done: Promise<void>;
  };

  export default class Hyperdrive extends EventEmitter<HyperdriveEvents> {
    readonly url: string;
    readonly id: string;
    readonly writable: boolean;
    readonly readable: boolean;
    readonly key: Key;
    readonly discoveryKey: Key;
    readonly version: number;
    readonly supportsMetadata: true;

    readonly core: Hypercore;
    readonly corestore: CoreStore;
    readonly db: Hyperbee<string, Entry>;

    constructor(
      store: CoreStore,
      key: Buffer | Uint8Array | null,
      opts?: DriveOpts
    );

    ready(): Promise<void>;
    close(): Promise<void>;

    put(
      path: string,
      buffer: Uint8Array,
      options?: WriteOptions
    ): Promise<void>;
    get(path: string, options?: ReadOptions): Promise<Uint8Array | null>;
    entry(path: string, options?: EntryOptions): Promise<Entry>;
    exists(path: string): Promise<boolean>;
    del(path: string): Promise<void>;
    compare(entryA: Entry, entryB: Entry): number;
    clear(path: string, options?: { diff?: boolean }): Promise<number | null>;
    clearAll(options?: { diff?: boolean }): Promise<number | null>;
    truncate(
      version: number,
      options?: { blobs?: number }
    ): Promise<void | number>;
    purge(): Promise<void>;
    symlink(path: string, linkname: string): Promise<void>;
    batch(): Hyperdrive & { flush(): Promise<void> };
    list(folder: string, options?: ListOptions): AsyncIterable<Entry>;
    readdir(
      folder: string,
      options?: { wait?: boolean }
    ): AsyncIterable<string>;
    has(path: string): Promise<boolean>;
    entries: Hyperbee<string, Entry>["createReadStream"];
    mirror(out: Hyperdrive, options?: MirrorDriveOptions): MirrorDrive;
    watch(folder?: string): AsyncIterable<[UncloseableDrive, UncloseableDrive]>;
    ready(): Promise<void>;
    destroy(): void;
    createReadStream(path: string, options?: ReadOptions): any;
    createWriteStream(path: string, options?: WriteOptions): any;
    download(folder: string, options?: ListOptions): Download;
    checkout(version: number): Hyperdrive;
    diff(version: number, folder: string, options?: any): AsyncIterable<Diff>;
    downloadDiff(version: number, folder: string, options?: any): Download;
    downloadRange(dbRanges: any, blobRanges: any): Download;
    findingPeers(): () => void;
    update(options?: { wait: boolean }): Promise<boolean>;
  }
}


================================================
FILE: types/hyperswarm.d.ts
================================================
declare module "hyperswarm" {
  import { EventEmitter } from "stream";
  import { Duplex } from "streamx";
  type Key = Buffer | Uint8Array;
  interface KeyPair {
    publicKey: Key;
    secretKey: Key;
  }
  interface SwarmOpts {
    keyPair?: KeyPair;
    seed?: Key;
    maxKeys?: number;
    firewall?: (remotePublicKey: Key) => boolean;
  }
  type Connection = Duplex;
  interface PeerInfo {
    readonly publicKey: Key;
    readonly topics: Key[];
    readonly prioritized: boolean;
    ban(banStatus: boolean): void;
  }
  interface JoinOpts {
    server?: boolean;
    client?: boolean;
  }
  interface PeerDiscovery {
    flushed(): Promise<void>;
    destroy(): Promise<void>;
    refresh(joinOpts: JoinOpts): Promise<void>;
  }
  interface SwarmEvents {
    close: [];
    connection: [connection: Connection, peer: PeerInfo];
    update: [];
    ban: [peerInfo: PeerInfo, err: Error];
  }
  export default class Hyperswarm extends EventEmitter<SwarmEvents> {
    readonly keyPair: KeyPair;
    readonly connections: Connection[];
    readonly peers: Map<string, PeerInfo>;

    constructor(opts?: SwarmOpts);

    flush(): Promise<void>;
    join(topic: Key, joinOpts?: JoinOpts): PeerDiscovery;
    leave(topic: Key): Promise<void>;
    joinPeer(topic: Key): void;
    leavePeer(topic: Key): void;
    destroy(): Promise<void>;
    listen(): Promise<void>;
    suspend(): Promise<void>;
    resume(): Promise<void>;
  }
}


================================================
FILE: types/rocksdb-native.d.ts
================================================
declare module "rocksdb-native" {
  import type { Encoder } from "compact-encoding";

  interface SessionOpts {
    valueEncoding?: Encoder;
    keyEncoding?: Encoder;
    columnFamily?: string;
    readOnly?: boolean;
  }

  interface IteratorOpts<Key> {
    gt?: Key;
    gte?: Key;
    lt?: Key;
    lte?: Key;
    reverse?: boolean;
    limit?: number;
    values?: boolean;
    keys?: boolean;
  }

  interface Entry<Key, Value> {
    key: Key;
    value: Value;
  }

  /**
   * RocksDB-Native Instance.
   * **WARNING**: This is a stub used in hyper-sdk.
   * Most methods are not yet documented
   */
  export default class RocksDB<Key = string, Value = string> {
    constructor(storageLocation: string, options?: SessionOpts);
    put(key: Key, value: Value): Promise<void>;
    get(key: Key): Promise<Value?>;
    delete(key: Key): Promise<void>;
    flush(): Promise<void>;
    close(): Promise<void>;
    session<SubKey = Key, SubValue = Value>(
      options?: SessionOpts
    ): RocksDB<SubKey, SubValue>;
    iterator(
      range?: IteratorOpts,
      options?: IteratorOpts
    ): AsyncIterable<Entry<Key, Value>>;
  }
}


================================================
FILE: types/test-tmp.d.ts
================================================
declare module "test-tmp" {
    export default function testTMP(): Promise<string>
}

================================================
FILE: types/z32.d.ts
================================================
declare module "z32" {
    export function decode(encoded: string) : Buffer
}
Download .txt
gitextract_wwgwrlvq/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── dist/
│   ├── index.d.ts
│   └── test.d.ts
├── index.js
├── package.json
├── test.js
├── tsconfig.json
└── types/
    ├── corestore.d.ts
    ├── hyperbee.d.ts
    ├── hypercore-crypto.d.ts
    ├── hypercore.d.ts
    ├── hyperdrive.d.ts
    ├── hyperswarm.d.ts
    ├── rocksdb-native.d.ts
    ├── test-tmp.d.ts
    └── z32.d.ts
Download .txt
SYMBOL INDEX (109 symbols across 10 files)

FILE: dist/index.d.ts
  constant HYPER_PROTOCOL_SCHEME (line 44) | const HYPER_PROTOCOL_SCHEME: "hyper://";
  constant DEFAULT_CORE_OPTS (line 45) | const DEFAULT_CORE_OPTS: {};
  constant DEFAULT_CORESTORE_OPTS (line 50) | const DEFAULT_CORESTORE_OPTS: {};
  constant DEFAULT_SWARM_OPTS (line 51) | const DEFAULT_SWARM_OPTS: {};
  class SDK (line 52) | class SDK extends EventEmitter<any> {
  type Key (line 176) | type Key = Buffer | Uint8Array;
  type NameOrKeyOrURL (line 177) | type NameOrKeyOrURL = string | Key;
  type ResolvedKeyOrName (line 178) | type ResolvedKeyOrName = {
  type DNSResponse (line 182) | type DNSResponse = {

FILE: index.js
  constant HYPER_PROTOCOL_SCHEME (line 29) | const HYPER_PROTOCOL_SCHEME = 'hyper://'
  constant DEFAULT_CORE_OPTS (line 30) | const DEFAULT_CORE_OPTS = {}
  constant DEFAULT_JOIN_OPTS (line 31) | const DEFAULT_JOIN_OPTS = {
  constant DEFAULT_CORESTORE_OPTS (line 35) | const DEFAULT_CORESTORE_OPTS = {}
  constant DEFAULT_SWARM_OPTS (line 36) | const DEFAULT_SWARM_OPTS = {}
  constant DEFAULT_DNS_RESOLVER (line 55) | const DEFAULT_DNS_RESOLVER = 'https://mozilla.cloudflare-dns.com/dns-query'
  constant DNSLINK_PREFIX (line 57) | const DNSLINK_PREFIX = 'dnslink=/hyper/'
  class SDK (line 59) | class SDK extends EventEmitter {
    method constructor (line 84) | constructor ({
    method swarm (line 125) | get swarm () {
    method corestore (line 129) | get corestore () {
    method publicKey (line 133) | get publicKey () {
    method connections (line 137) | get connections () {
    method peers (line 141) | get peers () {
    method cores (line 148) | get cores () {
    method drives (line 155) | get drives () {
    method bees (line 160) | get bees () {
    method resolveDNSToKey (line 169) | async resolveDNSToKey (hostname) {
    method resolveNameOrKeyToOpts (line 227) | async resolveNameOrKeyToOpts (nameOrKeyOrURL) {
    method getBee (line 279) | async getBee (nameOrKeyOrURL, opts = {}) {
    method getDrive (line 305) | async getDrive (nameOrKeyOrURL, opts = {}) {
    method get (line 364) | async get (nameOrKeyOrURL, opts = {}) {
    method namespace (line 412) | namespace (namespace) {
    method makeTopicKey (line 421) | makeTopicKey (name) {
    method joinCore (line 432) | async joinCore (core, opts = {}) {
    method join (line 460) | join (topic, opts = {}) {
    method leave (line 473) | leave (topic) {
    method joinPeer (line 483) | joinPeer (id) {
    method leavePeer (line 490) | leavePeer (id) {
    method ready (line 494) | async ready () {
    method close (line 500) | async close () {
    method suspend (line 514) | async suspend() {
    method resume (line 522) | async resume() {
    method replicate (line 531) | replicate (connection) {
  function create (line 553) | async function create ({
  function stringToKey (line 594) | function stringToKey (string) {

FILE: test.js
  constant NULL_KEY (line 9) | const NULL_KEY = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
  constant NULL_BUFFER (line 10) | const NULL_BUFFER = b4a.alloc(32, 0)
  constant NULL_HEX_KEY (line 11) | const NULL_HEX_KEY = NULL_BUFFER.toString('hex')
  constant NULL_URL (line 12) | const NULL_URL = `hyper://${NULL_KEY}/`
  function testFetch (line 186) | function testFetch (...args) {

FILE: types/corestore.d.ts
  type CoreStoreOpts (line 7) | interface CoreStoreOpts {
  type GetOpts (line 13) | type GetOpts = CoreOpts | { name: string } | { key: Key };
  class CoreStore (line 14) | class CoreStore {

FILE: types/hyperbee.d.ts
  type BeeOpts (line 6) | interface BeeOpts<Key = Buffer, Value = Buffer> {
  type Entry (line 11) | interface Entry<Key = Buffer, Value = Buffer> {
  type HistoryEntry (line 17) | type HistoryEntry<Key, Value> = Entry<Key, Value> & {
  type Range (line 21) | interface Range<Key> {
  type BatchOptions (line 28) | interface BatchOptions<Key, Value> {
  type DBWatcher (line 32) | type DBWatcher<Key, Value> = AsyncIterable<
  type KeyWatcher (line 39) | type KeyWatcher<Key, Value> = EventEmitter<{ update: [] }> & {
  type SubOpts (line 44) | interface SubOpts<Key = Buffer> {
  type ReadStreamOpts (line 50) | interface ReadStreamOpts {
  type HistoryOpts (line 55) | type HistoryOpts = Range<number> &
  type Batch (line 60) | interface Batch<Key, Value> {
  class Hyperbee (line 68) | class Hyperbee<

FILE: types/hypercore-crypto.d.ts
  type KeyPair (line 4) | interface KeyPair {

FILE: types/hypercore.d.ts
  type Key (line 5) | type Key = Buffer | Uint8Array;
  type KeyPair (line 6) | interface KeyPair {
  type EncodingType (line 11) | type EncodingType = "json" | "utf-8" | "binary";
  type Stringable (line 12) | type Stringable = string | { toString(): string };
  type Primitive (line 16) | type Primitive = bigint | boolean | null | number | string;
  type JSONValue (line 17) | type JSONValue = Primitive | JSONObject | JSONArray;
  type JSONObject (line 18) | interface JSONObject {
  type JSONArray (line 21) | interface JSONArray extends Array<JSONValue> {}
  type CoreOpts (line 23) | interface CoreOpts {
  type GetOpts (line 33) | interface GetOpts {
  type Peer (line 43) | interface Peer {
  type Extension (line 50) | interface Extension<Encoding = Buffer | Uint16Array> {
  type ExtensionOpts (line 56) | interface ExtensionOpts<Encoding = Buffer | Uint8Array> {
  type HypercoreEvents (line 60) | interface HypercoreEvents {
  class Hypercore (line 70) | class Hypercore<

FILE: types/hyperdrive.d.ts
  type DriveOpts (line 7) | interface DriveOpts {
  type Entry (line 11) | interface Entry {
  type Metadata (line 27) | type Metadata = { [key: string]: any };
  type WriteOptions (line 29) | interface WriteOptions {
  type ReadOptions (line 34) | interface ReadOptions {
  type ListOptions (line 42) | interface ListOptions {
  type EntryOptions (line 48) | type EntryOptions = ReadOptions & {
  type Diff (line 52) | interface Diff {
  type Download (line 57) | interface Download {
  type HypedriveEvents (line 62) | interface HypedriveEvents {
  type UncloseableDrive (line 66) | type UncloseableDrive = Hyperdrive & {
  type MirrorDriveOptions (line 70) | interface MirrorDriveOptions {
  type MirrorEvent (line 79) | interface MirrorEvent {
  type MirrorDrive (line 85) | type MirrorDrive = AsyncIterable<MirrorEvent> & {
  class Hyperdrive (line 90) | class Hyperdrive extends EventEmitter<HyperdriveEvents> {

FILE: types/hyperswarm.d.ts
  type Key (line 4) | type Key = Buffer | Uint8Array;
  type KeyPair (line 5) | interface KeyPair {
  type SwarmOpts (line 9) | interface SwarmOpts {
  type Connection (line 15) | type Connection = Duplex;
  type PeerInfo (line 16) | interface PeerInfo {
  type JoinOpts (line 22) | interface JoinOpts {
  type PeerDiscovery (line 26) | interface PeerDiscovery {
  type SwarmEvents (line 31) | interface SwarmEvents {
  class Hyperswarm (line 37) | class Hyperswarm extends EventEmitter<SwarmEvents> {

FILE: types/rocksdb-native.d.ts
  type SessionOpts (line 4) | interface SessionOpts {
  type IteratorOpts (line 11) | interface IteratorOpts<Key> {
  type Entry (line 22) | interface Entry<Key, Value> {
  class RocksDB (line 32) | class RocksDB<Key = string, Value = string> {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (66K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 475,
    "preview": "name: Testing\n\non: [ push, pull_request ]\n\njobs:\n  build:\n    strategy:\n      matrix:\n        node: [ '24' ]\n        os:"
  },
  {
    "path": ".gitignore",
    "chars": 988,
    "preview": "# 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# Directo"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3349,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2019 Dat Project\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 8752,
    "preview": "# hyper-sdk\n\nA Software Development Kit for the [hypercore-protocol](https://hypercore-protocol.org/)\n\n## Why use this?\n"
  },
  {
    "path": "dist/index.d.ts",
    "chars": 7196,
    "preview": "/**\n *\n * @param {object} options\n * @param {string} [options.storage]\n * @param {CoreStoreOpts} [options.corestoreOpts]"
  },
  {
    "path": "dist/test.d.ts",
    "chars": 11,
    "preview": "export {};\n"
  },
  {
    "path": "index.js",
    "chars": 15358,
    "preview": "import HyperSwarm from 'hyperswarm'\nimport CoreStore from 'corestore'\nimport Hypercore from 'hypercore'\nimport Hyperdriv"
  },
  {
    "path": "package.json",
    "chars": 2070,
    "preview": "{\n  \"name\": \"hyper-sdk\",\n  \"version\": \"6.2.2\",\n  \"description\": \"A Software Development Kit for the Hypercore-Protocol\","
  },
  {
    "path": "test.js",
    "chars": 8662,
    "preview": "import { test, configure } from 'brittle'\nimport { once } from 'events'\nimport { create } from './index.js'\nimport b4a f"
  },
  {
    "path": "tsconfig.json",
    "chars": 471,
    "preview": "{\n    \"compilerOptions\": {\n        \"target\": \"es2022\",\n        \"module\": \"nodenext\",\n        \"checkJs\": true,\n        \"a"
  },
  {
    "path": "types/corestore.d.ts",
    "chars": 1010,
    "preview": "declare module \"corestore\" {\n  import type { Connection } from \"hyperswarm\";\n  import type { CoreOpts } from \"hypercore\""
  },
  {
    "path": "types/hyperbee.d.ts",
    "chars": 3186,
    "preview": "declare module \"hyperbee\" {\n  import type { EncodingType } from \"hypercore\";\n  import type Hypercore, { Key } from \"hype"
  },
  {
    "path": "types/hypercore-crypto.d.ts",
    "chars": 227,
    "preview": "declare module \"hypercore-crypto\" {\n  import type {KeyPair} from \"hypercore-crypto\";\n\n  interface KeyPair {\n    publicKe"
  },
  {
    "path": "types/hypercore.d.ts",
    "chars": 3200,
    "preview": "declare module \"hypercore\" {\n  import { EventEmitter } from \"node:events\";\n  import type RocksDB from \"rocksdb-native\";\n"
  },
  {
    "path": "types/hyperdrive.d.ts",
    "chars": 4075,
    "preview": "declare module \"hyperdrive\" {\n  import { EventEmitter } from \"node:stream\";\n  import type Hypercore from \"hypercore\";\n  "
  },
  {
    "path": "types/hyperswarm.d.ts",
    "chars": 1435,
    "preview": "declare module \"hyperswarm\" {\n  import { EventEmitter } from \"stream\";\n  import { Duplex } from \"streamx\";\n  type Key = "
  },
  {
    "path": "types/rocksdb-native.d.ts",
    "chars": 1138,
    "preview": "declare module \"rocksdb-native\" {\n  import type { Encoder } from \"compact-encoding\";\n\n  interface SessionOpts {\n    valu"
  },
  {
    "path": "types/test-tmp.d.ts",
    "chars": 84,
    "preview": "declare module \"test-tmp\" {\n    export default function testTMP(): Promise<string>\n}"
  },
  {
    "path": "types/z32.d.ts",
    "chars": 77,
    "preview": "declare module \"z32\" {\n    export function decode(encoded: string) : Buffer\n}"
  }
]

About this extraction

This page contains the full source code of the datproject/sdk GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (61.4 KB), approximately 16.6k tokens, and a symbol index with 109 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!