An open API service indexing awesome lists of open source software.

https://github.com/ethersphere/bee-js

Javascript client library for connecting to Bee decentralised storage
https://github.com/ethersphere/bee-js

bee decentralized javascript javascript-library swarm typescript

Last synced: 13 days ago
JSON representation

Javascript client library for connecting to Bee decentralised storage

Awesome Lists containing this project

README

        

# Bee-JS

[![](https://img.shields.io/badge/made%20by-Swarm-blue.svg?style=flat-square)](https://swarm.ethereum.org/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fethersphere%2Fbee-js.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fethersphere%2Fbee-js?ref=badge_shield)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
![](https://img.shields.io/badge/Node.js-%3E%3D18.0.0-orange.svg?style=flat-square)
![](https://img.shields.io/badge/runs%20in-browser%20%7C%20node%20%7C%20webworker%20%7C%20electron-orange)

> JavaScript SDK for the Swarm decentralised storage.

> Supports Node.js 18+, Vite and Webpack.

> Write your code in CJS, MJS or TypeScript.

> Intended to be used with Bee version 2.5.0.

## Quick start

Start a Swarm project using TypeScript:

```sh
npm init swarm-app@latest my-dapp node-ts
```

or using Vite and TypeScript:

```sh
npm init swarm-app@latest my-dapp vite-tsx
```

Supported types are `node`, `node-esm`, `node-ts` and `vite-tsx`. Replace `my-dapp` with your project name.

## Install

```sh
npm install @ethersphere/bee-js
```

## Import

### CJS

```js
const { Bee } = require('@ethersphere/bee-js')
```

### MJS and TypeScript

```ts
import { Bee } from '@ethersphere/bee-js'
```

### Script tag

Loading this module through a script tag will make the `BeeJs` object available in the global namespace.

```html

```

## Overview

### Type interfaces

`NumberString` is a branded type for marking strings that represent numbers. It interops with `string` and `bigint`
types. Where `NumberString` is present, `number` is disallowed in order to avoid pitfalls with unsafe large values.

### Byte primitives

All the classes below extend `Bytes`, therefor the following methods are available on all of them: `toUint8Array`,
`toHex`, `toBase64`, `toBase32`, `toUtf8`, `toJSON`, `static keccak256`, `static fromUtf8`.

The `toString` method uses `toHex`.

`Bytes` and its subclasses may be constructed with `new` from `Uint8Array` or hex `string`.

#### Elliptic

| Name | Description | Methods |
| ---------- | ------------------------- | ------------------------------------------------------ |
| PrivateKey | 32 bytes private key | `publicKey`, `sign` |
| PublicKey | 64 bytes public key | `address`, `toCompressedUint8Array`, `toCompressedHex` |
| EthAddress | 20 bytes Ethereum address | `toChecksum` |
| Signature | 65 bytes signature | `recoverPublicKey` |

#### Swarm

| Name | Description | Methods |
| ------------- | ----------------------------------- | ------------------------------- |
| Reference | 32/64 bytes reference (chunk, feed) | `toCid` |
| Identifier | 32 bytes identifier (SOC, Feed) | - |
| TransactionId | 32 bytes transaction ID | - |
| FeedIndex | 8 bytes feed index (BE) | `static fromBigInt`, `toBigInt` |
| Topic | 32 bytes topic | `static fromString` |
| PeerAddress | 32 bytes peer address | - |
| BatchId | 32 bytes batch ID | - |
| Span | 8 bytes span (LE) | `static fromBigInt`, `toBigInt` |

### Tokens

| Name | Description | Methods |
| ---- | --------------------------- | ------------------------------------------------------------------------------------------------ |
| DAI | ERC20 DAI token (18 digits) | `static fromDecimalString`, `static fromWei`, `toWeiString`, `toWeiBigInt`, `toDecimalString` |
| BZZ | ERC20 BZZ token (16 digits) | `static fromDecimalString`, `static fromPLUR`, `toPLURString`, `toPLURBigInt`, `toDecimalString` |

### Swarm chunks

| Name | Description | Creation |
| ---------------- | ----------------------------------------------------------------------------------------------- | --------------------------- |
| Chunk | Span, max. 4096 bytes payload; address dervied from content | `makeContentAddressedChunk` |
| SingleOwnerChunk | Identifier, signature, span, max. 4096 bytes payload; address derived from identifier and owner | `makeSingleOwnerChunk` |

### Swarm primitives

| Name | Description | Methods |
| ------------ | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| MantarayNode | Compact trie with reference values and JSON metadata | `addFork`, `removeFork`, `calculateSelfAddress`, `find`, `findClosest`, `collect`, `marshal`, `unmarshal`, `saveRecursively`, `loadRecursively` |
| MerkleTree | Streaming BMT of chunks | `append`, `finalize`, `static root` |

### Swarm objects

| Name | Description | Creation |
| ---------- | ----------------------- | -------------------- |
| SOCWriter | SingleOwnerChunk writer | `bee.makeSOCWriter` |
| SOCReader | SingleOwnerChunk reader | `bee.makeSOCReader` |
| FeedWriter | Feed writer | `bee.makeFeedWriter` |
| FeedReader | Feed reader | `bee.makeFeedReader` |

### Bee API

- ❌❌✅ - Full node only
- ❌✅✅ - Light node and full node
- ✅✅✅ - Ultra-light node, light node and full node

| JS Call | Bee Endpoint | Bee Mode |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `uploadFile` | `POST /bzz` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz/post) | ❌✅✅ |
| `uploadFilesFromDirectory` _Node.js_ | `POST /bzz` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz/post) | ❌✅✅ |
| `uploadFiles` | `POST /bzz` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz/post) | ❌✅✅ |
| `uploadCollection` | `POST /bzz` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz/post) | ❌✅✅ |
| `uploadData` | `POST /bytes` [🔗](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes/post) | ❌✅✅ |
| `uploadChunk` | `POST /chunks` [🔗](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks/post) | ❌✅✅ |
| `streamDirectory` _Node.js_ | `POST /chunks` [🔗](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks/post) | ❌✅✅ |
| `streamFiles` _Browser_ | `POST /chunks` [🔗](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks/post) | ❌✅✅ |
| `SOCWriter.upload` | `POST /soc/:owner/:identifier` [🔗](https://docs.ethswarm.org/api/#tag/Single-owner-chunk/paths/~1soc~1%7Bowner%7D~1%7Bid%7D/post) | ❌✅✅ |
| `FeedReader.download` | `GET /feeds/:owner/:topic` [🔗](https://docs.ethswarm.org/api/#tag/Feed/paths/~1feeds~1%7Bowner%7D~1%7Btopic%7D/get) | ✅✅✅ |
| `FeedWriter.updateFeed` | `POST /soc/:owner/:identifier` [🔗](https://docs.ethswarm.org/api/#tag/Single-owner-chunk/paths/~1soc~1%7Bowner%7D~1%7Bid%7D/post) | ❌✅✅ |
| `downloadFile` | `GET /bzz/:reference` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz~1%7Breference%7D/get) | ✅✅✅ |
| `downloadFile` | `GET /bzz/:reference/:path` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz~1%7Breference%7D~1%7Bpath%7D/get) | ✅✅✅ |
| `downloadReadableFile` | `GET /bzz/:reference` [🔗](https://docs.ethswarm.org/api/#tag/BZZ/paths/~1bzz~1%7Breference%7D/get) | ✅✅✅ |
| `downloadData` | `GET /bytes/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes~1%7Breference%7D/get) | ✅✅✅ |
| `downloadReadableData` | `GET /bytes/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes~1%7Breference%7D/get) | ✅✅✅ |
| `downloadChunk` | `GET /chunks/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks~1%7Baddress%7D/get) | ✅✅✅ |
| `createFeedManifest` | `POST /feeds/:owner/:topic` [🔗](https://docs.ethswarm.org/api/#tag/Feed/paths/~1feeds~1%7Bowner%7D~1%7Btopic%7D/post) | ❌✅✅ |
| `isConnected` | `GET /` | ✅✅✅ |
| `getHealth` | `GET /health` [🔗](https://docs.ethswarm.org/api/#tag/Status/paths/~1health/get) | ✅✅✅ |
| `getReadiness` | `GET /readiness` [🔗](https://docs.ethswarm.org/api/#tag/Status/paths/~1readiness/get) | ✅✅✅ |
| `getNodeInfo` | `GET /node` [🔗](https://docs.ethswarm.org/api/#tag/Status/paths/~1node/get) | ✅✅✅ |
| `getChainState` | `GET /chainstate` [🔗](https://docs.ethswarm.org/api/#tag/Status/paths/~1chainstate/get) | ❌✅✅ |
| `getRedistributionState` | `GET /redistributionstate` [🔗](https://docs.ethswarm.org/api/#tag/RedistributionState/paths/~1redistributionstate/get) | ❌❌✅ |
| `getReserveState` | `GET /reservestate` [🔗](https://docs.ethswarm.org/api/#tag/Status/paths/~1reservestate/get) | ❌❌✅ |
| `getStatus` | `GET /status` [🔗](https://docs.ethswarm.org/api/#tag/Node-Status/paths/~1status/get) | ✅✅✅ |
| `getWallet` | `GET /wallet` [🔗](https://docs.ethswarm.org/api/#tag/Wallet/paths/~1wallet/get) | ❌✅✅ |
| `getTopology` | `GET /topology` [🔗](https://docs.ethswarm.org/api/#tag/Connectivity/paths/~1topology/get) | ✅✅✅ |
| `getAddresses` | `GET /addresses` [🔗](https://docs.ethswarm.org/api/#tag/Connectivity/paths/~1addresses/get) | ✅✅✅ |
| `getPeers` | `GET /peers` [🔗](https://docs.ethswarm.org/api/#tag/Connectivity/paths/~1peers/get) | ✅✅✅ |
| `getAllBalances` | `GET /balances` [🔗](https://docs.ethswarm.org/api/#tag/Balance/paths/~1balances/get) | ❌✅✅ |
| `getPeerBalance` | `GET /balances/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Balance/paths/~1balances~1%7Baddress%7D/get) | ❌✅✅ |
| `getPastDueConsumptionBalances` | `GET /consumed` [🔗](https://docs.ethswarm.org/api/#tag/Balance/paths/~1consumed/get) | ❌✅✅ |
| `getPastDueConsumptionPeerBalance` | `GET /consumed/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Balance/paths/~1consumed~1%7Baddress%7D/get) | ❌✅✅ |
| `getAllSettlements` | `GET /settlements` [🔗](https://docs.ethswarm.org/api/#tag/Settlements/paths/~1settlements/get) | ❌✅✅ |
| `getSettlements` | `GET /settlements/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Settlements/paths/~1settlements~1%7Baddress%7D/get) | ❌✅✅ |
| `getChequebookAddress` | `GET /chequebook/address` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1address/get) | ❌✅✅ |
| `getChequebookBalance` | `GET /chequebook/balance` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1balance/get) | ❌✅✅ |
| `getLastCheques` | `GET /chequebook/cheque` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1cheque/get) | ❌✅✅ |
| `getLastChequesForPeer` | `GET /chequebook/cheque/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1cheque~1%7Bpeer-id%7D/get) | ❌✅✅ |
| `getLastCashoutAction` | `GET /chequebook/cashout/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1cashout~1%7Bpeer-id%7D/get) | ❌✅✅ |
| `cashoutLastCheque` | `POST /chequebook/cashout/:peer` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1cashout~1%7Bpeer-id%7D/post) | ❌✅✅ |
| `depositTokens` | `POST /chequebook/deposit` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1deposit/post) | ❌✅✅ |
| `withdrawTokens` | `POST /chequebook/withdraw` [🔗](https://docs.ethswarm.org/api/#tag/Chequebook/paths/~1chequebook~1withdraw/post) | ❌✅✅ |
| `getAllPendingTransactions` | `GET /transactions` [🔗](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions/get) | ❌✅✅ |
| `getPendingTransaction` | `GET /transactions/:id` [🔗](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions~1%7BtxHash%7D/get) | ❌✅✅ |
| `rebroadcastTransaction` | `POST /transactions/:id` [🔗](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions~1%7BtxHash%7D/post) | ❌✅✅ |
| `cancelTransaction` | `DELETE /transactions/:id` [🔗](https://docs.ethswarm.org/api/#tag/Transaction/paths/~1transactions~1%7BtxHash%7D/delete) | ❌✅✅ |
| `createTag` | `POST /tags` [🔗](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags/post) | ❌✅✅ |
| `retrieveTag` | `GET /tags/:id` [🔗](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1%7Buid%7D/get) | ❌✅✅ |
| `getAllTags` | `GET /tags` [🔗](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags/get) | ❌✅✅ |
| `deleteTag` | `DELETE /tags/:id` [🔗](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1%7Buid%7D/delete) | ❌✅✅ |
| `updateTag` | `PATCH /tags/:id` [🔗](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1%7Buid%7D/patch) | ❌✅✅ |
| `pin` | `POST /pins/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Pinning/paths/~1pins~1%7Breference%7D/post) | ✅✅✅ |
| `getAllPins` | `GET /pins` [🔗](https://docs.ethswarm.org/api/#tag/Pinning/paths/~1pins/get) | ✅✅✅ |
| `getPin` | `GET /pins/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Pinning/paths/~1pins~1%7Breference%7D/get) | ✅✅✅ |
| `isReferenceRetrievable` | `GET /stewardship/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Stewardship/paths/~1stewardship~1%7Breference%7D/get) | ✅✅✅ |
| `reuploadPinnedData` | `PUT /stewardship/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Stewardship/paths/~1stewardship~1%7Breference%7D/put) | ❌✅✅ |
| `unpin` | `DELETE /pins/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Pinning/paths/~1pins~1%7Breference%7D/delete) | ✅✅✅ |
| `getGrantees` | `GET /grantee/:reference` [🔗](https://docs.ethswarm.org/api/#tag/ACT/paths/~1grantee~1%7Breference%7D/get) | ❌✅✅ |
| `createGrantees` | `POST /grantee` [🔗](https://docs.ethswarm.org/api/#tag/ACT/paths/~1grantee/post) | ❌✅✅ |
| `patchGrantees` | `PATCH /grantee/:reference` [🔗](https://docs.ethswarm.org/api/#tag/ACT/paths/~1grantee~1%7Breference%7D/patch) | ❌✅✅ |
| `pssSend` | `POST /pss/send/:topic/:target` [🔗](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1send~1%7Btopic%7D~1%7Btargets%7D/post) | ❌✅✅ |
| `pssSubscribe` _Websocket_ | `GET /pss/subscribe/:topic` [🔗](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1subscribe~1%7Btopic%7D/get) | ❌❌✅ |
| `pssReceive` | `GET /pss/subscribe/:topic` [🔗](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1subscribe~1%7Btopic%7D/get) | ❌❌✅ |
| `getAllPostageBatch` | `GET /stamps` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps/get) | ❌✅✅ |
| `getGlobalPostageBatches` | `GET /batches` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1batches/get) | ❌✅✅ |
| `getPostageBatch` | `GET /stamps/:batchId` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps~1%7Bbatch_id%7D/get) | ❌✅✅ |
| `getPostageBatchBuckets` | `GET /stamps/:batchId/buckets` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps~1%7Bbatch_id%7D~1buckets/get) | ❌✅✅ |
| `createPostageBatch` | `POST /stamps/:amount/:depth` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps~1%7Bamount%7D~1%7Bdepth%7D/post) | ❌✅✅ |
| `topUpBatch` | `PATCH /stamps/topup/:batchId/:amount` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps~1topup~1%7Bbatch_id%7D~1%7Bamount%7D/patch) | ❌✅✅ |
| `diluteBatch` | `PATCH /stamps/dilute/:batchId/:depth` [🔗](https://docs.ethswarm.org/api/#tag/Postage-Stamps/paths/~1stamps~1dilute~1%7Bbatch_id%7D~1%7Bdepth%7D/patch) | ❌✅✅ |
| `createEnvelope` | `POST /envelope/:reference` [🔗](https://docs.ethswarm.org/api/#tag/Envelope/paths/~1envelope~1%7Baddress%7D/post) | ❌✅✅ |
| `getStake` | `GET /stake` [🔗](https://docs.ethswarm.org/api/#tag/Staking/paths/~1stake/get) | ❌❌✅ |
| `depositStake` | `POST /stake` [🔗](https://docs.ethswarm.org/api/#tag/Staking/paths/~1stake~1%7Bamount%7D/post) | ❌❌✅ |

### Utils

#### General

- `getCollectionSize`
- `getFolderSize`

#### PSS

- `makeMaxTarget`

#### Erasure Coding

- `approximateOverheadForRedundancyLevel`
- `getRedundancyStat`
- `getRedundancyStats`

#### Stamps

- `getAmountForTtl`
- `getDepthForCapacity`
- `getStampCost`
- `getStampEffectiveBytes`
- `getStampMaximumCapacityBytes`
- `getStampTtlSeconds`
- `getStampUsage`

## Usage

### Upload via Swarm Gateway

```js
import { Bee, NULL_STAMP, SWARM_GATEWAY_URL } from '@ethersphere/bee-js'

main()

async function main() {
const bee = new Bee(SWARM_GATEWAY_URL)
const { reference } = await bee.uploadData(NULL_STAMP, 'Hello, World!')
console.log(reference.toHex())
}
```

### Create or select an existing postage batch

Swarm incentivizes nodes in the network to store content, therefor all uploads require a paid
[postage batch](https://docs.ethswarm.org/docs/learn/technology/contracts/postage-stamp).

```js
import { Bee } from '@ethersphere/bee-js'

async function getOrCreatePostageBatch() {
const bee = new Bee('http://localhost:1633')
let batchId

const batches = await bee.getAllPostageBatch()
const usable = batches.find(x => x.usable)

if (usable) {
batchId = usable.batchID
} else {
batchId = await bee.buyStorage(Size.fromGigabytes(1), Duration.fromDays(7))
}
}
```

> The following examples all assume an existing batchId.

### Upload simple data (Browser + Node.js)

```js
import { Bee } from '@ethersphere/bee-js'

const bee = new Bee('http://localhost:1633')

const uploadResult = await bee.uploadData(batchId, 'Bee is awesome!')
const data = await bee.downloadData(uploadResult.reference)

console.log(data.toUtf8()) // prints 'Bee is awesome!'
```

### Upload data from a file input (React)

```js
import { Bee } from '@ethersphere/bee-js'

const bee = new Bee('http://localhost:1633')
const result = await bee.uploadFile(batchId, file)
```

### Upload multiple files or a directory (React)

```js
import { Bee } from '@ethersphere/bee-js'

const bee = new Bee('http://localhost:1633')
const result = await bee.uploadFiles(batchId, fileList)
```

### Upload arbitrary large file (Node.js)

```js
import { Bee } from '@ethersphere/bee-js'
import { createReadStream } from 'fs'

const bee = new Bee('http://localhost:1633')
const readable = createReadStream('./path/to/large.bin')
const uploadResult = await bee.uploadFile(batchId, readable)
```

### Upload arbitrary large directories (Node.js)

```js
import { Bee } from '@ethersphere/bee-js'
import { createReadStream } from 'fs'

const bee = new Bee('http://localhost:1633')
const uploadResult = await bee.uploadFilesFromDirectory(batchId, './path/to/gallery/')
```

### Customize http/https agent and headers

```js
const bee = new Bee('http://localhost:1633', {
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
headers: {
Authorization: 'Basic ' + Buffer.from('username:password').toString('base64'),
},
})
```

## Contribute

Stay up to date by joining the [official Discord](https://discord.gg/GU22h2utj6) and by keeping an eye on the
[releases tab](https://github.com/ethersphere/bee-js/releases).

There are some ways you can make this module better:

- Consult our [open issues](https://github.com/ethersphere/bee-js/issues) and take on one of them
- Help our tests reach 100% coverage!
- Join us in our [Discord chat](https://discord.gg/wdghaQsGq5) in the #develop-on-swarm channel if you have questions or
want to give feedback

### Setup

Install project dependencies:

```sh
npm install
```

Build the project:

```sh
npm run build
```

After making changes, link the package to your project by running `npm link` in the Bee-JS project root, and
`npm link @ethersphere/bee-js` in your project root.

### Test

Tests are currently run against a mainnet Bee nodes. This is temporary and this section will be revised in the future.

## License

[BSD-3-Clause](./LICENSE)

[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fethersphere%2Fbee-js.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fethersphere%2Fbee-js?ref=badge_large)