var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import * as bzz from "./modules/bzz.js"; import * as stewardship from "./modules/stewardship.js"; import * as tag from "./modules/tag.js"; import * as pinning from "./modules/pinning.js"; import * as bytes from "./modules/bytes.js"; import * as chunk from "./modules/chunk.js"; import * as pss from "./modules/pss.js"; import * as status from "./modules/status.js"; import { BeeArgumentError, BeeError } from "./utils/error.js"; import { prepareWebsocketData } from "./utils/data.js"; import { fileArrayBuffer, isFile } from "./utils/file.js"; import { makeFeedReader, makeFeedWriter } from "./feed/index.js"; import { makeSigner } from "./chunk/signer.js"; import { assertFeedType, DEFAULT_FEED_TYPE } from "./feed/type.js"; import { downloadSingleOwnerChunk, uploadSingleOwnerChunkData } from "./chunk/soc.js"; import { makeTopic, makeTopicFromString } from "./feed/topic.js"; import { createFeedManifest } from "./modules/feed.js"; import { assertBeeUrl, stripLastSlash } from "./utils/url.js"; import { makeEthAddress, makeHexEthAddress } from "./utils/eth.js"; import { wrapBytesWithHelpers } from "./utils/bytes.js"; import { addCidConversionFunction, assertAddressPrefix, assertAllTagsOptions, assertBatchId, assertCollectionUploadOptions, assertData, assertFileData, assertFileUploadOptions, assertPssMessageHandler, assertPublicKey, assertReference, assertReferenceOrEns, assertRequestOptions, assertUploadOptions, makeReferenceOrEns, makeTagUid } from "./utils/type.js"; import { getJsonData, setJsonData } from "./feed/json.js"; import { assertCollection, makeCollectionFromFileList } from "./utils/collection.js"; import { makeCollectionFromFS } from "./utils/collection.node.js"; import { CHUNK_SIZE, SPAN_SIZE } from "./types/index.js"; import { makeDefaultKy, wrapRequestClosure, wrapResponseClosure } from "./utils/http.js"; import { isReadable } from "./utils/stream.js"; import { areAllSequentialFeedsUpdateRetrievable } from "./feed/retrievable.js"; import { ReferenceType } from '@ethersphere/swarm-cid'; /** * The main component that abstracts operations available on the main Bee API. * * Not all methods are always available as it depends in what mode is Bee node launched in. * For example gateway mode and light node mode has only limited set of endpoints enabled. */ export class Bee { /** * @param url URL on which is the main API of Bee node exposed * @param options */ constructor(url, options) { var _a; assertBeeUrl(url); // Remove last slash if present, as our endpoint strings starts with `/...` // which could lead to double slash in URL to which Bee responds with // unnecessary redirects. this.url = stripLastSlash(url); if (options === null || options === void 0 ? void 0 : options.signer) { this.signer = makeSigner(options.signer); } const kyOptions = { prefixUrl: this.url, timeout: (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : false, retry: options === null || options === void 0 ? void 0 : options.retry, fetch: options === null || options === void 0 ? void 0 : options.fetch, hooks: { beforeRequest: [], afterResponse: [] } }; if (options === null || options === void 0 ? void 0 : options.defaultHeaders) { kyOptions.headers = options.defaultHeaders; } if (options === null || options === void 0 ? void 0 : options.onRequest) { kyOptions.hooks.beforeRequest.push(wrapRequestClosure(options.onRequest)); } if (options === null || options === void 0 ? void 0 : options.onResponse) { kyOptions.hooks.afterResponse.push(wrapResponseClosure(options.onResponse)); } this.ky = makeDefaultKy(kyOptions); } /** * Upload data to a Bee node * * @param postageBatchId Postage BatchId to be used to upload the data with * @param data Data to be uploaded * @param options Additional options like tag, encryption, pinning, content-type and request options * * @returns reference is a content hash of the data * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `POST /bytes`](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes/post) */ uploadData(postageBatchId, data, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); assertData(data); if (options) assertUploadOptions(options); return bytes.upload(this.getKy(options), data, postageBatchId, options); }); } /** * Download data as a byte array * * @param reference Bee data reference in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `GET /bytes`](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes~1{reference}/get) */ downloadData(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReferenceOrEns(reference); return bytes.download(this.getKy(options), reference); }); } /** * Download data as a Readable stream * * @param reference Bee data reference in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `GET /bytes`](https://docs.ethswarm.org/api/#tag/Bytes/paths/~1bytes~1{reference}/get) */ downloadReadableData(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReferenceOrEns(reference); return bytes.downloadReadable(this.getKy(options), reference); }); } /** * Upload chunk to a Bee node * * @param postageBatchId Postage BatchId to be used to upload the chunk with * @param data Raw chunk to be uploaded * @param options Additional options like tag, encryption, pinning, content-type and request options * * @returns reference is a content hash of the data * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `POST /chunks`](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks/post) */ uploadChunk(postageBatchId, data, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); if (!(data instanceof Uint8Array)) { throw new TypeError('Data has to be Uint8Array instance!'); } if (data.length < SPAN_SIZE) { throw new BeeArgumentError(`Chunk has to have size of at least ${SPAN_SIZE}.`, data); } if (data.length > CHUNK_SIZE + SPAN_SIZE) { throw new BeeArgumentError(`Chunk has to have size of at most ${CHUNK_SIZE}.`, data); } if (options) assertUploadOptions(options); return chunk.upload(this.getKy(options), data, postageBatchId, options); }); } /** * Download chunk as a byte array * * @param reference Bee chunk reference in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `GET /chunks`](https://docs.ethswarm.org/api/#tag/Chunk/paths/~1chunks~1{reference}/get) */ downloadChunk(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReferenceOrEns(reference); return chunk.download(this.getKy(options), reference); }); } /** * Upload single file to a Bee node. * * **To make sure that you won't loose critical data it is highly recommended to also * locally pin the data with `options.pin = true`** * * @param postageBatchId Postage BatchId to be used to upload the data with * @param data Data or file to be uploaded * @param name Optional name of the uploaded file * @param options Additional options like tag, encryption, pinning, content-type and request options * * @see [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive) * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `POST /bzz`](https://docs.ethswarm.org/api/#tag/File/paths/~1bzz/post) * @returns reference is a content hash of the file */ uploadFile(postageBatchId, data, name, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); assertFileData(data); if (options) assertFileUploadOptions(options); if (name && typeof name !== 'string') { throw new TypeError('name has to be string or undefined!'); } if (isFile(data)) { const fileData = yield fileArrayBuffer(data); const fileName = name !== null && name !== void 0 ? name : data.name; const contentType = data.type; const fileOptions = Object.assign({ contentType }, options); return addCidConversionFunction(yield bzz.uploadFile(this.getKy(options), fileData, postageBatchId, fileName, fileOptions), ReferenceType.MANIFEST); } else if (isReadable(data) && (options === null || options === void 0 ? void 0 : options.tag) && !options.size) { // TODO: Needed until https://github.com/ethersphere/bee/issues/2317 is resolved const result = yield bzz.uploadFile(this.getKy(options), data, postageBatchId, name, options); yield this.updateTag(options.tag, result.reference); return addCidConversionFunction(result, ReferenceType.MANIFEST); } else { return addCidConversionFunction(yield bzz.uploadFile(this.getKy(options), data, postageBatchId, name, options), ReferenceType.MANIFEST); } }); } /** * Download single file. * * @param reference Bee file reference in hex string (either 64 or 128 chars long), ENS domain or Swarm CID. * @param path If reference points to manifest, then this parameter defines path to the file * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * @see Data * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `GET /bzz`](https://docs.ethswarm.org/api/#tag/Collection/paths/~1bzz~1{reference}~1{path}/get) */ downloadFile(reference, path = '', options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); reference = makeReferenceOrEns(reference, ReferenceType.MANIFEST); return bzz.downloadFile(this.getKy(options), reference, path); }); } /** * Download single file as a readable stream * * @param reference Bee file reference in hex string (either 64 or 128 chars long), ENS domain or Swarm CID. * @param path If reference points to manifest / collections, then this parameter defines path to the file * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * * @see [Bee docs - Upload and download](https://docs.ethswarm.org/docs/access-the-swarm/upload-and-download) * @see [Bee API reference - `GET /bzz`](https://docs.ethswarm.org/api/#tag/Collection/paths/~1bzz~1{reference}~1{path}/get) */ downloadReadableFile(reference, path = '', options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); reference = makeReferenceOrEns(reference, ReferenceType.MANIFEST); return bzz.downloadFileReadable(this.getKy(options), reference, path); }); } /** * Upload collection of files to a Bee node * * Uses the FileList API from the browser. * * The returned `UploadResult.tag` might be undefined if called in CORS-enabled environment. * This will be fixed upon next Bee release. https://github.com/ethersphere/bee-js/issues/406 * * @param postageBatchId Postage BatchId to be used to upload the data with * @param fileList list of files to be uploaded * @param options Additional options like tag, encryption, pinning and request options * * @see [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive) * @see [Bee docs - Upload directory](https://docs.ethswarm.org/docs/access-the-swarm/upload-a-directory/) * @see [Bee API reference - `POST /bzz`](https://docs.ethswarm.org/api/#tag/Collection/paths/~1bzz/post) */ uploadFiles(postageBatchId, fileList, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); if (options) assertCollectionUploadOptions(options); const data = yield makeCollectionFromFileList(fileList); return addCidConversionFunction(yield bzz.uploadCollection(this.getKy(options), data, postageBatchId, options), ReferenceType.MANIFEST); }); } /** * Upload Collection that you can assembly yourself. * * The returned `UploadResult.tag` might be undefined if called in CORS-enabled environment. * This will be fixed upon next Bee release. https://github.com/ethersphere/bee-js/issues/406 * * @param postageBatchId * @param collection * @param options Collections and request options */ uploadCollection(postageBatchId, collection, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); assertCollection(collection); if (options) assertCollectionUploadOptions(options); return addCidConversionFunction(yield bzz.uploadCollection(this.ky, collection, postageBatchId, options), ReferenceType.MANIFEST); }); } /** * Upload collection of files. * * Available only in Node.js as it uses the `fs` module. * * The returned `UploadResult.tag` might be undefined if called in CORS-enabled environment. * This will be fixed upon next Bee release. https://github.com/ethersphere/bee-js/issues/406 * * @param postageBatchId Postage BatchId to be used to upload the data with * @param dir the path of the files to be uploaded * @param options Additional options like tag, encryption, pinning and request options * * @see [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive) * @see [Bee docs - Upload directory](https://docs.ethswarm.org/docs/access-the-swarm/upload-a-directory/) * @see [Bee API reference - `POST /bzz`](https://docs.ethswarm.org/api/#tag/Collection/paths/~1bzz/post) */ uploadFilesFromDirectory(postageBatchId, dir, options) { return __awaiter(this, void 0, void 0, function* () { assertBatchId(postageBatchId); if (options) assertCollectionUploadOptions(options); const data = yield makeCollectionFromFS(dir); return addCidConversionFunction(yield bzz.uploadCollection(this.getKy(options), data, postageBatchId, options), ReferenceType.MANIFEST); }); } /** * Create a new Tag which is meant for tracking progres of syncing data across network. * * **Warning! Not allowed when node is in Gateway mode!** * * @param options Options that affects the request behavior * @see [Bee docs - Syncing / Tags](https://docs.ethswarm.org/docs/access-the-swarm/syncing) * @see [Bee API reference - `POST /tags`](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags/post) */ createTag(options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); return tag.createTag(this.getKy(options)); }); } /** * Fetches all tags. * * The listing is limited by options.limit. So you have to iterate using options.offset to get all tags. * * **Warning! Not allowed when node is in Gateway mode!** * * @param options Options that affects the request behavior * @throws TypeError if limit or offset are not numbers or undefined * @throws BeeArgumentError if limit or offset have invalid options * * @see [Bee docs - Syncing / Tags](https://docs.ethswarm.org/docs/access-the-swarm/syncing) * @see [Bee API reference - `GET /tags`](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags/get) */ getAllTags(options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertAllTagsOptions(options); return tag.getAllTags(this.getKy(options), options === null || options === void 0 ? void 0 : options.offset, options === null || options === void 0 ? void 0 : options.limit); }); } /** * Retrieve tag information from Bee node * * **Warning! Not allowed when node is in Gateway mode!** * * @param tagUid UID or tag object to be retrieved * @param options Options that affects the request behavior * @throws TypeError if tagUid is in not correct format * * @see [Bee docs - Syncing / Tags](https://docs.ethswarm.org/docs/access-the-swarm/syncing) * @see [Bee API reference - `GET /tags/{uid}`](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1{uid}/get) * */ retrieveTag(tagUid, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); tagUid = makeTagUid(tagUid); return tag.retrieveTag(this.getKy(options), tagUid); }); } /** * Delete Tag * * **Warning! Not allowed when node is in Gateway mode!** * * @param tagUid UID or tag object to be retrieved * @param options Options that affects the request behavior * @throws TypeError if tagUid is in not correct format * @throws BeeResponse error if something went wrong on the Bee node side while deleting the tag. * * @see [Bee docs - Syncing / Tags](https://docs.ethswarm.org/docs/access-the-swarm/syncing) * @see [Bee API reference - `DELETE /tags/{uid}`](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1{uid}/delete) */ deleteTag(tagUid, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); tagUid = makeTagUid(tagUid); return tag.deleteTag(this.getKy(options), tagUid); }); } /** * Update tag's total chunks count. * * This is important if you are uploading individual chunks with a tag. Then upon finishing the final root chunk, * you can use this method to update the total chunks count for the tag. * * **Warning! Not allowed when node is in Gateway mode!** * * @param tagUid UID or tag object to be retrieved * @param reference The root reference that contains all the chunks to be counted * @param options Options that affects the request behavior * @throws TypeError if tagUid is in not correct format * @throws BeeResponse error if something went wrong on the Bee node side while deleting the tag. * * @see [Bee docs - Syncing / Tags](https://docs.ethswarm.org/docs/access-the-swarm/syncing) * @see [Bee API reference - `PATCH /tags/{uid}`](https://docs.ethswarm.org/api/#tag/Tag/paths/~1tags~1{uid}/patch) */ updateTag(tagUid, reference, options) { return __awaiter(this, void 0, void 0, function* () { assertReference(reference); assertRequestOptions(options); tagUid = makeTagUid(tagUid); return tag.updateTag(this.getKy(options), tagUid, reference); }); } /** * Pin local data with given reference * * **Warning! Not allowed when node is in Gateway mode!** * * @param reference Data reference * @param options Options that affects the request behavior * @throws TypeError if reference is in not correct format * * @see [Bee docs - Pinning](https://docs.ethswarm.org/docs/access-the-swarm/pinning) */ pin(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReference(reference); return pinning.pin(this.getKy(options), reference); }); } /** * Unpin local data with given reference * * **Warning! Not allowed when node is in Gateway mode!** * * @param reference Data reference * @param options Options that affects the request behavior * @throws TypeError if reference is in not correct format * * @see [Bee docs - Pinning](https://docs.ethswarm.org/docs/access-the-swarm/pinning) */ unpin(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReference(reference); return pinning.unpin(this.getKy(options), reference); }); } /** * Get list of all locally pinned references * * **Warning! Not allowed when node is in Gateway mode!** * * @param options Options that affects the request behavior * @see [Bee docs - Pinning](https://docs.ethswarm.org/docs/access-the-swarm/pinning) */ getAllPins(options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); return pinning.getAllPins(this.getKy(options)); }); } /** * Get pinning status of chunk with given reference * * **Warning! Not allowed when node is in Gateway mode!** * * @param reference Bee data reference in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * * @see [Bee docs - Pinning](https://docs.ethswarm.org/docs/access-the-swarm/pinning) */ getPin(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReference(reference); return pinning.getPin(this.getKy(options), reference); }); } /** * Instructs the Bee node to reupload a locally pinned data into the network. * * @param reference Bee data reference to be re-uploaded in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws BeeArgumentError if the reference is not locally pinned * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * * @see [Bee API reference - `PUT /stewardship`](https://docs.ethswarm.org/api/#tag/Stewardship/paths/~1stewardship~1{reference}/put) */ reuploadPinnedData(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReferenceOrEns(reference); yield stewardship.reupload(this.getKy(options), reference); }); } /** * Checks if content specified by reference is retrievable from the network. * * @param reference Bee data reference to be checked in hex string (either 64 or 128 chars long) or ENS domain. * @param options Options that affects the request behavior * @throws TypeError if some of the input parameters is not expected type * @throws BeeArgumentError if there is passed ENS domain with invalid unicode characters * * @see [Bee API reference - `GET /stewardship`](https://docs.ethswarm.org/api/#tag/Stewardship/paths/~1stewardship~1{reference}/get) */ isReferenceRetrievable(reference, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertReferenceOrEns(reference); return stewardship.isRetrievable(this.getKy(options), reference); }); } /** * Functions that validates if feed is retrievable in the network. * * If no index is passed then it check for "latest" update, which is a weaker guarantee as nobody can be really * sure what is the "latest" update. * * If index is passed then it validates all previous sequence index chunks if they are available as they are required * to correctly resolve the feed upto the given index update. * * @param type * @param owner * @param topic * @param index * @param options */ isFeedRetrievable(type, owner, topic, index, options) { return __awaiter(this, void 0, void 0, function* () { const canonicalOwner = makeEthAddress(owner); const canonicalTopic = makeTopic(topic); if (!index) { try { yield this.makeFeedReader(type, canonicalTopic, canonicalOwner).download(); return true; } catch (e) { const err = e; // Only if the error is "not-found" then we return false otherwise we re-throw the error if ((err === null || err === void 0 ? void 0 : err.status) === 404) { return false; } throw e; } } if (type !== 'sequence') { throw new BeeError('Only Sequence type of Feeds is supported at the moment'); } return areAllSequentialFeedsUpdateRetrievable(this, canonicalOwner, canonicalTopic, index, options); }); } /** * Send data to recipient or target with Postal Service for Swarm. * * Because sending a PSS message is slow and CPU intensive, * it is not supposed to be used for general messaging but * most likely for setting up an encrypted communication * channel by sending an one-off message. * * **Warning! Not allowed when node is in Gateway mode!** * * **Warning! If the recipient Bee node is a light node, then he will never receive the message!** * This is because light nodes does not fully participate in the data exchange in Swarm network and hence the message won't arrive to them. * * @param postageBatchId Postage BatchId that will be assigned to sent message * @param topic Topic name * @param target Target message address prefix. Has a limit on length. Recommend to use `Utils.Pss.makeMaxTarget()` to get the most specific target that Bee node will accept. * @param data Message to be sent * @param recipient Recipient public key * @param options Options that affects the request behavior * @throws TypeError if `data`, `batchId`, `target` or `recipient` are in invalid format * * @see [Bee docs - PSS](https://docs.ethswarm.org/docs/dapps-on-swarm/pss) * @see [Bee API reference - `POST /pss`](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1send~1{topic}~1{targets}/post) */ pssSend(postageBatchId, topic, target, data, recipient, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertData(data); assertBatchId(postageBatchId); assertAddressPrefix(target); if (typeof topic !== 'string') { throw new TypeError('topic has to be an string!'); } if (recipient) { assertPublicKey(recipient); return pss.send(this.getKy(options), topic, target, data, postageBatchId, recipient); } else { return pss.send(this.getKy(options), topic, target, data, postageBatchId); } }); } /** * Subscribe to messages for given topic with Postal Service for Swarm * * **Warning! Not allowed when node is in Gateway mode!** * * **Warning! If connected Bee node is a light node, then he will never receive any message!** * This is because light nodes does not fully participate in the data exchange in Swarm network and hence the message won't arrive to them. * * @param topic Topic name * @param handler Message handler interface * * @returns Subscription to a given topic * * @see [Bee docs - PSS](https://docs.ethswarm.org/docs/dapps-on-swarm/pss) * @see [Bee API reference - `GET /pss`](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1subscribe~1{topic}/get) */ pssSubscribe(topic, handler) { assertPssMessageHandler(handler); if (typeof topic !== 'string') { throw new TypeError('topic has to be an string!'); } const ws = pss.subscribe(this.url, topic); let cancelled = false; const cancel = () => { if (cancelled === false) { cancelled = true; // although the WebSocket API offers a `close` function, it seems that // with the library that we are using (isomorphic-ws) it doesn't close // the websocket properly, whereas `terminate` does if (ws.terminate) ws.terminate();else ws.close(); // standard Websocket in browser does not have terminate function } }; const subscription = { topic, cancel }; ws.onmessage = ev => __awaiter(this, void 0, void 0, function* () { const data = yield prepareWebsocketData(ev.data); // ignore empty messages if (data.length > 0) { handler.onMessage(wrapBytesWithHelpers(data), subscription); } }); ws.onerror = ev => { // ignore errors after subscription was cancelled if (!cancelled) { handler.onError(new BeeError(ev.message), subscription); } }; return subscription; } /** * Receive message with Postal Service for Swarm * * Because sending a PSS message is slow and CPU intensive, * it is not supposed to be used for general messaging but * most likely for setting up an encrypted communication * channel by sending an one-off message. * * This is a helper function to wait for exactly one message to * arrive and then cancel the subscription. Additionally a * timeout can be provided for the message to arrive or else * an error will be thrown. * * **Warning! Not allowed when node is in Gateway mode!** * * **Warning! If connected Bee node is a light node, then he will never receive any message!** * This is because light nodes does not fully participate in the data exchange in Swarm network and hence the message won't arrive to them. * * @param topic Topic name * @param timeoutMsec Timeout in milliseconds * * @returns Message in byte array * * @see [Bee docs - PSS](https://docs.ethswarm.org/docs/dapps-on-swarm/pss) * @see [Bee API reference - `GET /pss`](https://docs.ethswarm.org/api/#tag/Postal-Service-for-Swarm/paths/~1pss~1subscribe~1{topic}/get) */ pssReceive(topic, timeoutMsec = 0) { return __awaiter(this, void 0, void 0, function* () { if (typeof topic !== 'string') { throw new TypeError('topic has to be an string!'); } if (typeof timeoutMsec !== 'number') { throw new TypeError('timeoutMsc parameter has to be a number!'); } return new Promise((resolve, reject) => { let timeout; const subscription = this.pssSubscribe(topic, { onError: error => { clearTimeout(timeout); subscription.cancel(); reject(error.message); }, onMessage: message => { clearTimeout(timeout); subscription.cancel(); resolve(message); } }); if (timeoutMsec > 0) { // we need to cast the type because Typescript is getting confused with Node.js' // alternative type definitions timeout = setTimeout(() => { subscription.cancel(); reject(new BeeError('pssReceive timeout')); }, timeoutMsec); } }); }); } /** * Create feed manifest chunk and return the reference to it. * * Feed manifest chunk allows for a feed to be able to be resolved through `/bzz` endpoint. * * @param postageBatchId Postage BatchId to be used to create the Feed Manifest * @param type The type of the feed, can be 'epoch' or 'sequence' * @param topic Topic in hex or bytes * @param owner Owner's ethereum address in hex or bytes * @param options Options that affects the request behavior * * @see [Bee docs - Feeds](https://docs.ethswarm.org/docs/dapps-on-swarm/feeds) * @see [Bee API reference - `POST /feeds`](https://docs.ethswarm.org/api/#tag/Feed/paths/~1feeds~1{owner}~1{topic}/post) * TODO: Once breaking add support for Feed CID */ createFeedManifest(postageBatchId, type, topic, owner, options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options); assertFeedType(type); assertBatchId(postageBatchId); const canonicalTopic = makeTopic(topic); const canonicalOwner = makeHexEthAddress(owner); return createFeedManifest(this.getKy(options), canonicalOwner, canonicalTopic, postageBatchId, { type }); }); } /** * Make a new feed reader for downloading feed updates. * * @param type The type of the feed, can be 'epoch' or 'sequence' * @param topic Topic in hex or bytes * @param owner Owner's ethereum address in hex or bytes * @param options Options that affects the request behavior * * @see [Bee docs - Feeds](https://docs.ethswarm.org/docs/dapps-on-swarm/feeds) */ makeFeedReader(type, topic, owner, options) { assertRequestOptions(options); assertFeedType(type); const canonicalTopic = makeTopic(topic); const canonicalOwner = makeHexEthAddress(owner); return makeFeedReader(this.getKy(options), type, canonicalTopic, canonicalOwner); } /** * Make a new feed writer for updating feeds * * @param type The type of the feed, can be 'epoch' or 'sequence' * @param topic Topic in hex or bytes * @param signer The signer's private key or a Signer instance that can sign data * @param options Options that affects the request behavior * * @see [Bee docs - Feeds](https://docs.ethswarm.org/docs/dapps-on-swarm/feeds) */ makeFeedWriter(type, topic, signer, options) { assertRequestOptions(options); assertFeedType(type); const canonicalTopic = makeTopic(topic); const canonicalSigner = this.resolveSigner(signer); return makeFeedWriter(this.getKy(options), type, canonicalTopic, canonicalSigner); } /** * High-level function that allows you to easily set JSON data to feed. * JSON-like data types are supported. * * The default Signer of Bee instance is used if `options.signer` is not specified. * If none of those two is set error is thrown. * * @param postageBatchId Postage BatchId to be used to upload the data with * @param topic Human readable string, that is internally hashed so there are no constrains there. * @param data JSON compatible data * @param options * @param options.signer Custom instance of Signer or string with private key. * @param options.type Type of Feed * * @throws BeeError if `options.signer` is not specified nor the default Signer on Bee's instance is specified. * * @see [Bee docs - Feeds](https://docs.ethswarm.org/docs/dapps-on-swarm/feeds) */ setJsonFeed(postageBatchId, topic, data, options) { var _a; return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options, 'JsonFeedOptions'); assertBatchId(postageBatchId); const hashedTopic = this.makeFeedTopic(topic); const feedType = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : DEFAULT_FEED_TYPE; const writer = this.makeFeedWriter(feedType, hashedTopic, options === null || options === void 0 ? void 0 : options.signer, options); return setJsonData(this, writer, postageBatchId, data, options); }); } /** * High-level function that allows you to easily get data from feed. * Returned data are parsed using JSON.parse(). * * This method also supports specification of `signer` object passed to constructor. The order of evaluation is: * - `options.address` * - `options.signer` * - `this.signer` * * At least one of these has to be specified! * * @param topic Human readable string, that is internally hashed so there are no constrains there. * @param options * @param options.signer Custom instance of Signer or string with private key. This option is exclusive with `address` option. * @param options.address Ethereum address of owner of the feed that signed it. This option is exclusive with `signer` option. * @param options.type Type of Feed * * @see [Bee docs - Feeds](https://docs.ethswarm.org/docs/dapps-on-swarm/feeds) */ getJsonFeed(topic, options) { var _a; return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options, 'JsonFeedOptions'); const hashedTopic = this.makeFeedTopic(topic); const feedType = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : DEFAULT_FEED_TYPE; if ((options === null || options === void 0 ? void 0 : options.signer) && (options === null || options === void 0 ? void 0 : options.address)) { throw new BeeError('Both options "signer" and "address" can not be specified at one time!'); } let address; if (options === null || options === void 0 ? void 0 : options.address) { address = makeEthAddress(options === null || options === void 0 ? void 0 : options.address); } else { try { address = this.resolveSigner(options === null || options === void 0 ? void 0 : options.signer).address; } catch (e) { if (e instanceof BeeError) { throw new BeeError('Either address, signer or default signer has to be specified!'); } else { throw e; } } } const reader = this.makeFeedReader(feedType, hashedTopic, address, options); return getJsonData(this, reader); }); } /** * Make a new feed topic from a string * * Because the topic has to be 32 bytes long this function * hashes the input string to create a topic string of arbitrary length. * * @param topic The input string */ makeFeedTopic(topic) { return makeTopicFromString(topic); } /** * Returns an object for reading single owner chunks * * @param ownerAddress The ethereum address of the owner * @param options Options that affects the request behavior * @see [Bee docs - Chunk Types](https://docs.ethswarm.org/docs/dapps-on-swarm/chunk-types#single-owner-chunks) */ makeSOCReader(ownerAddress, options) { assertRequestOptions(options); const canonicalOwner = makeEthAddress(ownerAddress); return { owner: makeHexEthAddress(canonicalOwner), download: downloadSingleOwnerChunk.bind(null, this.getKy(options), canonicalOwner) }; } /** * Returns an object for reading and writing single owner chunks * * @param signer The signer's private key or a Signer instance that can sign data * @param options Options that affects the request behavior * @see [Bee docs - Chunk Types](https://docs.ethswarm.org/docs/dapps-on-swarm/chunk-types#single-owner-chunks) */ makeSOCWriter(signer, options) { assertRequestOptions(options); const canonicalSigner = this.resolveSigner(signer); return Object.assign(Object.assign({}, this.makeSOCReader(canonicalSigner.address, options)), { upload: uploadSingleOwnerChunkData.bind(null, this.getKy(options), canonicalSigner) }); } /** * Ping the Bee node to see if there is a live Bee node on the given URL. * * @param options Options that affects the request behavior * @throws If connection was not successful throw error */ checkConnection(options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options, 'PostageBatchOptions'); return status.checkConnection(this.getKy(options)); }); } /** * Ping the Bee node to see if there is a live Bee node on the given URL. * * @param options Options that affects the request behavior * @returns true if successful, false on error */ isConnected(options) { return __awaiter(this, void 0, void 0, function* () { assertRequestOptions(options, 'PostageBatchOptions'); try { yield status.checkConnection(this.getKy(options)); } catch (e) { return false; } return true; }); } /** * @param signer * @private * @throws BeeError if either no Signer was passed or no default Signer was specified for the instance */ resolveSigner(signer) { if (signer) { return makeSigner(signer); } if (this.signer) { return this.signer; } throw new BeeError('You have to pass Signer as property to either the method call or constructor! Non found.'); } getKy(options) { if (!options) { return this.ky; } return this.ky.extend(options); } }