├── .dockerignore ├── .github ├── dependabot.yaml ├── healthcheck.sh ├── labeler.yml ├── pull_request_template.md └── workflows │ ├── auto-release-docker.yml │ ├── close-stale-issue-and-pr.yaml │ ├── deploy-docs.yml │ ├── docker-build.yml │ ├── label.yml │ ├── publish-npm.yml │ ├── release-docker.yml │ ├── reusable │ ├── cached-build │ │ └── action.yml │ ├── collect-and-upload-logs │ │ └── action.yml │ ├── run-entry-point │ │ └── action.yml │ └── run-nodes │ │ └── action.yml │ ├── staging-docs.yml │ ├── test-docs.yml │ ├── test-setup.yml │ ├── validate.yml │ └── yamllint.yml ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── dataSources.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── network-monorepo.iml ├── runConfigurations │ ├── broker__all_.xml │ ├── broker__dev_1_.xml │ ├── broker__dev_2_.xml │ ├── broker__dev_3_.xml │ ├── broker__dev_prod_resend_.xml │ ├── client__all_.xml │ ├── client__end_to_end_.xml │ ├── client__integration_.xml │ ├── client__unit_.xml │ ├── network__all_.xml │ ├── network_tracker__all_.xml │ ├── tracker_1.xml │ ├── tracker_2.xml │ ├── tracker_3.xml │ └── utils__all_.xml └── vcs.xml ├── .npmrc ├── .nvmrc ├── .prettierrc ├── .yamllint ├── CHANGELOG.md ├── Dockerfile.node ├── README.md ├── bin ├── run-entry-point.sh └── run-nodes.sh ├── developer-docs ├── algorithms │ └── duplicate-detection.md ├── index.md ├── install-on-apple-silicon.md ├── logging-practices.md └── testing-best-practices.md ├── docs ├── .gitignore ├── README.md ├── api-docs-smoke-test.sh ├── docs │ ├── guides │ │ ├── _category_.json │ │ ├── become-an-operator.md │ │ ├── how-to-run-streamr-node.md │ │ ├── how-to-stake-and-earn.md │ │ ├── how-to-update-your-streamr-node.md │ │ ├── nodejs.md │ │ ├── overview.md │ │ ├── use-any-language-or-device.md │ │ └── web-app-frameworks.md │ ├── help │ │ ├── _category_.json │ │ ├── debugging-tools.md │ │ ├── developer-faq.md │ │ ├── how-to-contribute.md │ │ ├── how-to-get-support.md │ │ ├── operator-faq.md │ │ ├── project-faq.md │ │ ├── pub-sub.md │ │ └── useful-links.md │ ├── streamr-network │ │ ├── _category_.json │ │ ├── incentives │ │ │ ├── _category_.json │ │ │ ├── network-incentives.md │ │ │ ├── network-penalties.md │ │ │ ├── node-inspection.md │ │ │ └── stream-sponsorships.md │ │ ├── network-roles │ │ │ ├── _category_.json │ │ │ ├── delegators.md │ │ │ ├── operators.md │ │ │ ├── publishers-and-subscribers.md │ │ │ └── sponsors.md │ │ ├── security │ │ │ ├── _category_.json │ │ │ ├── end-to-end-encryption.md │ │ │ ├── quantum-security.md │ │ │ └── signing-and-verification.md │ │ ├── smart-contracts.md │ │ └── streamr-nodes.md │ ├── streamr-project │ │ ├── _category_.json │ │ ├── background.md │ │ └── governance.md │ └── usage │ │ ├── _category_.json │ │ ├── cli-tool.md │ │ ├── configuration.md │ │ ├── connect-apps-and-iot │ │ ├── _category_.json │ │ ├── proxy-publish.md │ │ └── streamr-node-interface.md │ │ ├── identity.md │ │ ├── sdk │ │ ├── _category_.json │ │ ├── how-to-use.md │ │ └── utility-functions.md │ │ └── streams │ │ ├── _category_.json │ │ ├── creating-streams.md │ │ ├── interacting-with-streams.md │ │ ├── msg-ordering.md │ │ ├── partitioning.md │ │ ├── permissions.md │ │ ├── publishing.md │ │ ├── signature-verification.md │ │ ├── store-and-retrieve.md │ │ ├── subscribing.md │ │ └── what-are-streams.md ├── docusaurus.config.ts ├── package-lock.json ├── package.json ├── sidebars.ts ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ └── api-docs.tsx ├── static │ ├── .nojekyll │ ├── assets │ │ ├── default.json │ │ └── testnet-default.json │ └── img │ │ ├── DATA-flows-Operator-contract.jpg │ │ ├── DATA-flows-Sponsorship-contract.jpg │ │ ├── DATA-flows-high-level.jpg │ │ ├── mqtt-guide-1.png │ │ ├── mumbai-to-testnet.jpg │ │ ├── network.png │ │ ├── node-addresses.png │ │ ├── noderunner.png │ │ ├── operator-address.png │ │ ├── operator-flows.png │ │ ├── operator-sponsorship-relational-diagram.png │ │ ├── operator-status-green.png │ │ ├── public-stream.png │ │ ├── quickstart.png │ │ ├── sponsor-diagram.png │ │ ├── stream-sponsorship.png │ │ ├── streamr-logo.svg │ │ ├── streams_partioning_01.jpeg │ │ ├── streams_partioning_02.jpeg │ │ ├── testnets.jpg │ │ └── usage.png └── tsconfig.json ├── eslint.config.mjs ├── jest.config.ts ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── autocertifier-client │ ├── .gitignore │ ├── .npmignore │ ├── package.json │ ├── proto.sh │ ├── protos │ │ └── AutoCertifier.proto │ ├── src │ │ ├── AutoCertifierClient.ts │ │ ├── RestClient.ts │ │ ├── createSelfSignedCertificate.ts │ │ ├── data │ │ │ ├── ApiError.ts │ │ │ ├── CertifiedSubdomain.ts │ │ │ ├── CreateCertifiedSubdomainRequest.ts │ │ │ ├── HttpStatus.ts │ │ │ ├── Session.ts │ │ │ └── UpdateIpAndPortRequest.ts │ │ ├── errors.ts │ │ ├── exports.ts │ │ └── makeHttpRequest.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── autocertifier-server │ ├── .gitignore │ ├── README.md │ ├── bin │ │ ├── copyARecordsToRoute53.ts │ │ └── run.ts │ ├── jest.config.ts │ ├── package.json │ ├── src │ │ ├── AutoCertifierServer.ts │ │ ├── CertificateCreator.ts │ │ ├── ChallengeManager.ts │ │ ├── Database.ts │ │ ├── DnsServer.ts │ │ ├── RestInterface.ts │ │ ├── RestServer.ts │ │ ├── Route53Api.ts │ │ └── StreamrChallenger.ts │ ├── test │ │ ├── integration │ │ │ ├── DnsServer-real-dns-client.test.ts │ │ │ ├── RestServer-real-http-client.test.ts │ │ │ └── StreamrChallenger.test.ts │ │ ├── unit │ │ │ ├── Database.test.ts │ │ │ └── DnsServer.test.ts │ │ └── utils │ │ │ └── self-signed-certs │ │ │ ├── certificate.pem │ │ │ └── key.pem │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── browser-test-runner │ ├── .gitignore │ ├── LICENSE │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── createKarmaConfig.ts │ │ ├── createWebpackConfig.ts │ │ ├── exports.ts │ │ ├── karma-setup.js │ │ └── preload.js │ └── tsconfig.node.json ├── cdn-location │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── data-generation │ │ ├── generateDataFromTSPSolverResult.sh │ │ ├── generateIataToRegionNumberMapping.ts │ │ ├── insertCoordinatesToAirports.ts │ │ ├── intermediate-files │ │ │ ├── airportsWithCoordinates.csv │ │ │ └── airportsWithShortestPath.csv │ │ ├── metropolitancodes.csv │ │ ├── prepareForTSPSolver.sh │ │ ├── tsp-input │ │ │ └── coordinates.csv │ │ └── tsp-output │ │ │ └── shortestpath.csv │ ├── eslint.config.mjs │ ├── jest.config.ts │ ├── package.json │ ├── src │ │ ├── airportCodeToRegion.ts │ │ ├── exports.ts │ │ ├── fetchAirportCodeFromCdn.ts │ │ └── getLocalRegion.ts │ ├── test │ │ └── integration │ │ │ ├── fetchAirportCodeFromCdn.test.ts │ │ │ └── getLocalRegion.test.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── cli-tools │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── bin │ │ ├── streamr-identity-generate.ts │ │ ├── streamr-identity-whoami.ts │ │ ├── streamr-identity.ts │ │ ├── streamr-internal-node-info.ts │ │ ├── streamr-internal-operator-create.ts │ │ ├── streamr-internal-operator-delegate.ts │ │ ├── streamr-internal-operator-grant-controller-role.ts │ │ ├── streamr-internal-operator-stake.ts │ │ ├── streamr-internal-operator-undelegate.ts │ │ ├── streamr-internal-operator-unstake.ts │ │ ├── streamr-internal-show-sdk-config.ts │ │ ├── streamr-internal-sponsorship-create.ts │ │ ├── streamr-internal-sponsorship-sponsor.ts │ │ ├── streamr-internal-token-mint.ts │ │ ├── streamr-internal-visualize-topology.ts │ │ ├── streamr-internal.ts │ │ ├── streamr-mock-data-generate.ts │ │ ├── streamr-mock-data.ts │ │ ├── streamr-storage-node-add-stream.ts │ │ ├── streamr-storage-node-list-streams.ts │ │ ├── streamr-storage-node-list.ts │ │ ├── streamr-storage-node-register.ts │ │ ├── streamr-storage-node-remove-stream.ts │ │ ├── streamr-storage-node-show.ts │ │ ├── streamr-storage-node-unregister.ts │ │ ├── streamr-storage-node.ts │ │ ├── streamr-stream-create.ts │ │ ├── streamr-stream-grant-permission.ts │ │ ├── streamr-stream-publish.ts │ │ ├── streamr-stream-resend-from.ts │ │ ├── streamr-stream-resend-last.ts │ │ ├── streamr-stream-resend-range.ts │ │ ├── streamr-stream-resend.ts │ │ ├── streamr-stream-revoke-permission.ts │ │ ├── streamr-stream-search.ts │ │ ├── streamr-stream-show.ts │ │ ├── streamr-stream-subscribe.ts │ │ ├── streamr-stream.ts │ │ └── streamr.ts │ ├── cli-tools.iml │ ├── jest.config.ts │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── command.ts │ │ ├── common.ts │ │ ├── config.ts │ │ ├── logLevel.ts │ │ ├── permission.ts │ │ └── resend.ts │ ├── test │ │ ├── environment.test.ts │ │ ├── identity.test.ts │ │ ├── internal-operator.test.ts │ │ ├── internal-sponsorship-create.test.ts │ │ ├── internal-sponsorship-sponsor.test.ts │ │ ├── mock-data.test.ts │ │ ├── storage-node.test.ts │ │ ├── stream-create.test.ts │ │ ├── stream-permission.test.ts │ │ ├── stream-publish-subscribe.test.ts │ │ ├── stream-resend.test.ts │ │ ├── stream-search.test.ts │ │ ├── stream-show.test.ts │ │ └── utils.ts │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── dht │ ├── .gitignore │ ├── README.md │ ├── jest.config.ts │ ├── karma-setup.js │ ├── karma.config.js │ ├── package.json │ ├── proto.sh │ ├── protos │ │ ├── DhtRpc.proto │ │ └── tests.proto │ ├── scripts │ │ ├── postbuild.sh │ │ └── rewrite-package.ts │ ├── src │ │ ├── connection │ │ │ ├── Connection.ts │ │ │ ├── ConnectionLockRpcLocal.ts │ │ │ ├── ConnectionLockRpcRemote.ts │ │ │ ├── ConnectionLockStates.ts │ │ │ ├── ConnectionManager.ts │ │ │ ├── ConnectionsView.ts │ │ │ ├── ConnectorFacade.ts │ │ │ ├── Handshaker.ts │ │ │ ├── IConnection.ts │ │ │ ├── ManagedConnection.ts │ │ │ ├── OutputBuffer.ts │ │ │ ├── PendingConnection.ts │ │ │ ├── connectivityChecker.ts │ │ │ ├── connectivityRequestHandler.ts │ │ │ ├── simulator │ │ │ │ ├── Simulator.ts │ │ │ │ ├── SimulatorConnection.ts │ │ │ │ ├── SimulatorConnector.ts │ │ │ │ ├── SimulatorTransport.ts │ │ │ │ └── pings.ts │ │ │ ├── webrtc │ │ │ │ ├── BrowserWebrtcConnection.ts │ │ │ │ ├── IWebrtcConnection.ts │ │ │ │ ├── NodeWebrtcConnection.ts │ │ │ │ ├── WebrtcConnector.ts │ │ │ │ ├── WebrtcConnectorRpcLocal.ts │ │ │ │ ├── WebrtcConnectorRpcRemote.ts │ │ │ │ └── iceServerAsString.ts │ │ │ └── websocket │ │ │ │ ├── AbstractWebsocketClientConnection.ts │ │ │ │ ├── AutoCertifierClientFacade.ts │ │ │ │ ├── BrowserWebsocketClientConnection.ts │ │ │ │ ├── NodeWebsocketClientConnection.ts │ │ │ │ ├── WebsocketClientConnector.ts │ │ │ │ ├── WebsocketClientConnectorRpcLocal.ts │ │ │ │ ├── WebsocketClientConnectorRpcRemote.ts │ │ │ │ ├── WebsocketServer.ts │ │ │ │ ├── WebsocketServerConnection.ts │ │ │ │ └── WebsocketServerConnector.ts │ │ ├── dht │ │ │ ├── DhtNode.ts │ │ │ ├── DhtNodeRpcLocal.ts │ │ │ ├── DhtNodeRpcRemote.ts │ │ │ ├── ExternalApiRpcLocal.ts │ │ │ ├── ExternalApiRpcRemote.ts │ │ │ ├── PeerManager.ts │ │ │ ├── contact │ │ │ │ ├── Contact.ts │ │ │ │ ├── ContactList.ts │ │ │ │ ├── RandomContactList.ts │ │ │ │ ├── RingContactList.ts │ │ │ │ ├── RpcRemote.ts │ │ │ │ ├── SortedContactList.ts │ │ │ │ ├── getClosestNodes.ts │ │ │ │ └── ringIdentifiers.ts │ │ │ ├── discovery │ │ │ │ ├── DiscoverySession.ts │ │ │ │ ├── PeerDiscovery.ts │ │ │ │ └── RingDiscoverySession.ts │ │ │ ├── recursive-operation │ │ │ │ ├── RecursiveOperationManager.ts │ │ │ │ ├── RecursiveOperationRpcLocal.ts │ │ │ │ ├── RecursiveOperationRpcRemote.ts │ │ │ │ ├── RecursiveOperationSession.ts │ │ │ │ ├── RecursiveOperationSessionRpcLocal.ts │ │ │ │ └── RecursiveOperationSessionRpcRemote.ts │ │ │ ├── routing │ │ │ │ ├── DuplicateDetector.ts │ │ │ │ ├── Router.ts │ │ │ │ ├── RouterRpcLocal.ts │ │ │ │ ├── RouterRpcRemote.ts │ │ │ │ ├── RoutingSession.ts │ │ │ │ ├── RoutingTablesCache.ts │ │ │ │ └── getPreviousPeer.ts │ │ │ └── store │ │ │ │ ├── LocalDataStore.ts │ │ │ │ ├── StoreManager.ts │ │ │ │ ├── StoreRpcLocal.ts │ │ │ │ └── StoreRpcRemote.ts │ │ ├── exports.ts │ │ ├── helpers │ │ │ ├── AddressTools.ts │ │ │ ├── Connectivity.ts │ │ │ ├── browser │ │ │ │ ├── isBrowserEnvironment.ts │ │ │ │ └── isBrowserEnvironment_override.ts │ │ │ ├── createPeerDescriptor.ts │ │ │ ├── createPeerDescriptorSignaturePayload.ts │ │ │ ├── debugHelpers.ts │ │ │ ├── errors.ts │ │ │ ├── offering.ts │ │ │ ├── protoClasses.ts │ │ │ ├── protoToString.ts │ │ │ └── version.ts │ │ ├── identifiers.ts │ │ ├── rpc-protocol │ │ │ ├── DhtCallContext.ts │ │ │ └── DhtRpcOptions.ts │ │ ├── transport │ │ │ ├── ITransport.ts │ │ │ ├── ListeningRpcCommunicator.ts │ │ │ └── RoutingRpcCommunicator.ts │ │ └── types │ │ │ ├── ServiceID.ts │ │ │ └── textencoding.d.ts │ ├── test │ │ ├── benchmark │ │ │ ├── Find.test.ts │ │ │ ├── KademliaCorrectness.test.ts │ │ │ ├── RingCorrectness.test.ts │ │ │ ├── SortedContactListBenchmark.test.ts │ │ │ ├── WebsocketServerMemoryLeak.test.ts │ │ │ └── hybrid-network-simulation │ │ │ │ └── RingContactList.test.ts │ │ ├── end-to-end │ │ │ ├── GeoIpLayer0.test.ts │ │ │ ├── Layer0-Layer1.test.ts │ │ │ ├── Layer0.test.ts │ │ │ ├── Layer0MixedConnectionTypes.test.ts │ │ │ ├── Layer0Webrtc-Layer1.test.ts │ │ │ ├── Layer0Webrtc.test.ts │ │ │ ├── Layer1-Scale-WebSocket.test.ts │ │ │ ├── Layer1-Scale-Webrtc.test.ts │ │ │ ├── RecoveryFromFailedAutoCertification.test.ts │ │ │ ├── WebsocketConnectionRequest.test.ts │ │ │ └── memory-leak.test.ts │ │ ├── integration │ │ │ ├── ConnectionLocking.test.ts │ │ │ ├── ConnectionManager.test.ts │ │ │ ├── ConnectivityChecking.test.ts │ │ │ ├── DhtJoinPeerDiscovery.test.ts │ │ │ ├── DhtNode.test.ts │ │ │ ├── DhtNodeExternalAPI.test.ts │ │ │ ├── DhtNodeRpcRemote.test.ts │ │ │ ├── DhtRpc.test.ts │ │ │ ├── Find.test.ts │ │ │ ├── GeoIpConnectivityChecking.test.ts │ │ │ ├── Layer1-scale.test.ts │ │ │ ├── Mock-Layer1-Layer0.test.ts │ │ │ ├── MultipleEntryPointJoining.test.ts │ │ │ ├── ReplicateData.test.ts │ │ │ ├── RouteMessage.test.ts │ │ │ ├── RouterRpcRemote.test.ts │ │ │ ├── SimultaneousConnections.test.ts │ │ │ ├── Store.test.ts │ │ │ ├── StoreAndDelete.test.ts │ │ │ ├── StoreOnDhtWithThreeNodes.test.ts │ │ │ ├── StoreOnDhtWithTwoNodes.test.ts │ │ │ ├── StoreRpcRemote.test.ts │ │ │ ├── WebrtcConnectionManagement.test.ts │ │ │ ├── WebrtcConnectorRpc.test.ts │ │ │ ├── Websocket.test.ts │ │ │ ├── WebsocketClientConnectorRpc.test.ts │ │ │ ├── WebsocketConnectionManagement.test.ts │ │ │ └── rpc-connections-over-webrtc.test.ts │ │ ├── kademlia-simulation │ │ │ └── data │ │ │ │ ├── nodeids.json │ │ │ │ └── orderedneighbors.json │ │ ├── types │ │ │ └── global.d.ts │ │ ├── unit │ │ │ ├── AddressTools.test.ts │ │ │ ├── AutoCertifierClientFacade.test.ts │ │ │ ├── ConnectionManager.test.ts │ │ │ ├── ConnectivityHelpers.test.ts │ │ │ ├── DiscoverySession.test.ts │ │ │ ├── DuplicateDetector.test.ts │ │ │ ├── Handshaker.test.ts │ │ │ ├── ListeningRpcCommunicator.test.ts │ │ │ ├── LocalDataStore.test.ts │ │ │ ├── ManagedConnection.test.ts │ │ │ ├── PeerManager.test.ts │ │ │ ├── PendingConnection.test.ts │ │ │ ├── ProtobufMessage.test.ts │ │ │ ├── RandomContactList.test.ts │ │ │ ├── RecursiveOperationManager.test.ts │ │ │ ├── RecursiveOperationSession.test.ts │ │ │ ├── Router.test.ts │ │ │ ├── RoutingSession.test.ts │ │ │ ├── SortedContactList.test.ts │ │ │ ├── StoreManager.test.ts │ │ │ ├── StoreRpcLocal.test.ts │ │ │ ├── WebrtcConnection.test.ts │ │ │ ├── WebrtcConnector.test.ts │ │ │ ├── WebsocketClientConnector.test.ts │ │ │ ├── WebsocketServer.test.ts │ │ │ ├── WebsocketServerConnector.test.ts │ │ │ ├── connectivityRequestHandler.test.ts │ │ │ ├── createPeerDescriptor.test.ts │ │ │ ├── customMatchers.test.ts │ │ │ ├── getClosestNodes.test.ts │ │ │ ├── version.test.ts │ │ │ └── webrtcReplaceInternalIpWithExternalIp.test.ts │ │ └── utils │ │ │ ├── FakeConnectorFacade.ts │ │ │ ├── FakeRpcCommunicator.ts │ │ │ ├── FakeTransport.ts │ │ │ ├── customMatchers.ts │ │ │ ├── mock │ │ │ ├── MockConnection.ts │ │ │ ├── MockConnectionsView.ts │ │ │ ├── MockRouter.ts │ │ │ ├── MockRpcCommunicator.ts │ │ │ ├── MockTransport.ts │ │ │ └── mockDataEntry.ts │ │ │ ├── topology.ts │ │ │ └── utils.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── geoip-location │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── jest.config.ts │ ├── package.json │ ├── src │ │ ├── GeoIpLocator.ts │ │ ├── downloadGeoIpDatabase.ts │ │ ├── exports.ts │ │ └── tarHelper.ts │ ├── test │ │ ├── helpers │ │ │ ├── TestServer.ts │ │ │ └── fetchFileToMemory.ts │ │ └── unit │ │ │ ├── GeoIpLocator-intervals.test.ts │ │ │ ├── GeoIpLocator-no-network-at-monthly.test.ts │ │ │ ├── GeoIpLocator-no-network-at-start.test.ts │ │ │ ├── GeoIpLocator.test.ts │ │ │ ├── downloadGeoIpDatabase.test.ts │ │ │ └── tarHelper.test.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── node │ ├── .dockerignore │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── broker.iml │ │ ├── codeStyles │ │ │ ├── Project.xml │ │ │ └── codeStyleConfig.xml │ │ ├── dataSources.xml │ │ ├── encodings.xml │ │ ├── inspectionProfiles │ │ │ └── Project_Default.xml │ │ ├── jsLibraryMappings.xml │ │ ├── misc.xml │ │ ├── modules.xml │ │ ├── runConfigurations │ │ │ ├── Run_Broker__8690__8691_.xml │ │ │ ├── Run_Broker__8790__8791_.xml │ │ │ ├── Run_Broker__8890_8891_.xml │ │ │ ├── Run_Tracker__30301_.xml │ │ │ ├── Run_Tracker__30302_.xml │ │ │ ├── Run_Tracker__30303_.xml │ │ │ ├── integration_test.xml │ │ │ ├── test.xml │ │ │ └── unit_test.xml │ │ └── vcs.xml │ ├── LICENSE │ ├── README.md │ ├── bin │ │ ├── delete-expired-data.ts │ │ ├── entry-point.ts │ │ ├── streamr-node-init.ts │ │ └── streamr-node.ts │ ├── configs │ │ ├── development-1.env.json │ │ ├── development-2.env.json │ │ ├── development-3.env.json │ │ ├── development-prod-resend.env.json │ │ ├── docker-1.env.json │ │ ├── docker-2.env.json │ │ └── docker-3.env.json │ ├── configuration.md │ ├── data-api.iml │ ├── jest.config.ts │ ├── package.json │ ├── plugins.md │ ├── src │ │ ├── Plugin.ts │ │ ├── apiAuthentication.ts │ │ ├── broker.ts │ │ ├── config │ │ │ ├── ConfigWizard.ts │ │ │ ├── config.schema.json │ │ │ ├── config.ts │ │ │ ├── definitions.schema.json │ │ │ ├── migration.ts │ │ │ └── validateConfig.ts │ │ ├── exports.ts │ │ ├── helpers │ │ │ ├── PayloadFormat.ts │ │ │ ├── applyPluginClientConfigs.ts │ │ │ ├── generateMnemonicFromAddress.ts │ │ │ ├── parser.ts │ │ │ ├── partitions.ts │ │ │ └── weightedSample.ts │ │ ├── httpServer.ts │ │ ├── pluginRegistry.ts │ │ ├── plugins │ │ │ ├── consoleMetrics │ │ │ │ ├── ConsoleMetricsPlugin.ts │ │ │ │ └── config.schema.json │ │ │ ├── http │ │ │ │ ├── HttpPlugin.ts │ │ │ │ ├── config.schema.json │ │ │ │ └── publishEndpoint.ts │ │ │ ├── info │ │ │ │ ├── InfoPlugin.ts │ │ │ │ └── config.schema.json │ │ │ ├── mqtt │ │ │ │ ├── Bridge.ts │ │ │ │ ├── MqttPlugin.ts │ │ │ │ ├── MqttServer.ts │ │ │ │ └── config.schema.json │ │ │ ├── operator │ │ │ │ ├── ConsistentHashRing.ts │ │ │ │ ├── MaintainTopologyHelper.ts │ │ │ │ ├── MaintainTopologyService.ts │ │ │ │ ├── OperatorFleetState.ts │ │ │ │ ├── OperatorPlugin.ts │ │ │ │ ├── StreamPartAssignments.ts │ │ │ │ ├── announceNodeToContract.ts │ │ │ │ ├── announceNodeToStream.ts │ │ │ │ ├── checkOperatorValueBreach.ts │ │ │ │ ├── closeExpiredFlags.ts │ │ │ │ ├── config.schema.json │ │ │ │ ├── createIsLeaderFn.ts │ │ │ │ ├── formCoordinationStreamId.ts │ │ │ │ ├── heartbeatUtils.ts │ │ │ │ ├── inspectOverTime.ts │ │ │ │ ├── inspectRandomNode.ts │ │ │ │ ├── inspectionUtils.ts │ │ │ │ ├── maintainOperatorValue.ts │ │ │ │ └── reviewSuspectNode.ts │ │ │ ├── storage │ │ │ │ ├── Batch.ts │ │ │ │ ├── BatchManager.ts │ │ │ │ ├── Bucket.ts │ │ │ │ ├── BucketManager.ts │ │ │ │ ├── DataQueryFormat.ts │ │ │ │ ├── DeleteExpiredCmd.ts │ │ │ │ ├── SetMembershipSynchronizer.ts │ │ │ │ ├── Storage.ts │ │ │ │ ├── StorageConfig.ts │ │ │ │ ├── StorageEventListener.ts │ │ │ │ ├── StoragePlugin.ts │ │ │ │ ├── StoragePoller.ts │ │ │ │ ├── config.schema.json │ │ │ │ ├── dataMetadataEndpoint.ts │ │ │ │ ├── dataQueryEndpoint.ts │ │ │ │ └── storageConfigEndpoint.ts │ │ │ ├── subscriber │ │ │ │ ├── SubscriberPlugin.ts │ │ │ │ └── config.schema.json │ │ │ └── websocket │ │ │ │ ├── Connection.ts │ │ │ │ ├── PublishConnection.ts │ │ │ │ ├── SubscribeConnection.ts │ │ │ │ ├── WebsocketPlugin.ts │ │ │ │ ├── WebsocketServer.ts │ │ │ │ └── config.schema.json │ │ └── types │ │ │ ├── consistent-hash │ │ │ └── index.d.ts │ │ │ └── nat-type-identifier │ │ │ └── index.d.ts │ ├── test │ │ ├── fixtures │ │ │ ├── cert.pem │ │ │ └── key.pem │ │ ├── integration │ │ │ ├── broker-subscriptions.test.ts │ │ │ ├── config.test.ts │ │ │ ├── createMessagingPluginTest.ts │ │ │ ├── multiple-publisher-plugins.test.ts │ │ │ └── plugins │ │ │ │ ├── mqtt │ │ │ │ ├── Bridge.test.ts │ │ │ │ └── MqttPlugin.test.ts │ │ │ │ ├── operator │ │ │ │ ├── MaintainTopologyService.test.ts │ │ │ │ ├── OperatorPlugin.test.ts │ │ │ │ ├── announceNodeToContract.test.ts │ │ │ │ ├── announceNodeToStream.test.ts │ │ │ │ ├── checkOperatorValueBreach.test.ts │ │ │ │ └── maintainOperatorValue.test.ts │ │ │ │ ├── storage │ │ │ │ ├── BatchManager.test.ts │ │ │ │ ├── BucketManager.test.ts │ │ │ │ ├── MockStorageConfig.ts │ │ │ │ ├── Storage.test.ts │ │ │ │ ├── StorageConfig.test.ts │ │ │ │ ├── cassanda-queries.test.ts │ │ │ │ ├── dataMetadataEndpoint.test.ts │ │ │ │ └── storage-lots-of-data.test.ts │ │ │ │ ├── subscriber │ │ │ │ └── SubscriberPlugin.test.ts │ │ │ │ └── websocket │ │ │ │ ├── WebsocketPlugin.test.ts │ │ │ │ ├── close.test.ts │ │ │ │ └── ping.test.ts │ │ ├── sequential │ │ │ └── plugins │ │ │ │ └── storage │ │ │ │ ├── CassandraNullPayloads.test.ts │ │ │ │ └── DeleteExpiredCmd.test.ts │ │ ├── smoke │ │ │ ├── inspect.test.ts │ │ │ └── profit.test.ts │ │ ├── types │ │ │ └── global.d.ts │ │ ├── unit │ │ │ ├── ConfigWizard.test.ts │ │ │ ├── Plugin.test.ts │ │ │ ├── config.test.ts │ │ │ ├── configMigration.test.ts │ │ │ ├── helpers │ │ │ │ ├── PayloadFormat.test.ts │ │ │ │ ├── applyPluginClientConfigs.test.ts │ │ │ │ ├── generateMnemonicFromAddress.test.ts │ │ │ │ ├── parser.test.ts │ │ │ │ └── weightedSample.test.ts │ │ │ ├── httpServer.test.ts │ │ │ └── plugins │ │ │ │ ├── http │ │ │ │ └── publishEndpoint.test.ts │ │ │ │ ├── mqtt │ │ │ │ └── Bridge.test.ts │ │ │ │ ├── operator │ │ │ │ ├── ConsistentHashRing.test.ts │ │ │ │ ├── MaintainTopologyHelper.test.ts │ │ │ │ ├── MaintainTopologyService.test.ts │ │ │ │ ├── OperatorFleetState.test.ts │ │ │ │ ├── StreamPartAssignments.test.ts │ │ │ │ ├── announceNodeToContract.test.ts │ │ │ │ ├── closeExpiredFlags.test.ts │ │ │ │ ├── createIsLeaderFn.test.ts │ │ │ │ ├── formCoordinationStreamId.test.ts │ │ │ │ ├── heartbeatUtils.test.ts │ │ │ │ ├── inspectOverTime.test.ts │ │ │ │ └── inspectionUtils.test.ts │ │ │ │ ├── storage │ │ │ │ ├── Batch.test.ts │ │ │ │ ├── Bucket.test.ts │ │ │ │ ├── StorageAssignmentSynchronizer.test.ts │ │ │ │ ├── StorageConfig.test.ts │ │ │ │ ├── StorageEventListener.test.ts │ │ │ │ ├── StoragePoller.test.ts │ │ │ │ ├── dataQueryEndpoint.test.ts │ │ │ │ └── storageConfigEndpoint.test.ts │ │ │ │ └── websocket │ │ │ │ ├── PublishConnection.test.ts │ │ │ │ └── WebsocketServer.test.ts │ │ └── utils.ts │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── proto-rpc │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── eslint.config.mjs │ ├── examples │ │ ├── errors │ │ │ ├── ErrorRpc.proto │ │ │ ├── compile-protobuf.sh │ │ │ ├── errors.ts │ │ │ ├── proto │ │ │ │ ├── ErrorRpc.client.ts │ │ │ │ ├── ErrorRpc.server.ts │ │ │ │ └── ErrorRpc.ts │ │ │ └── run.sh │ │ ├── hello │ │ │ ├── HelloRpc.proto │ │ │ ├── compile-protobuf.sh │ │ │ ├── hello.ts │ │ │ ├── proto │ │ │ │ ├── HelloRpc.client.ts │ │ │ │ ├── HelloRpc.server.ts │ │ │ │ └── HelloRpc.ts │ │ │ └── run.sh │ │ ├── routed-hello │ │ │ ├── RoutedHelloRpc.proto │ │ │ ├── compile-protobuf.sh │ │ │ ├── proto │ │ │ │ ├── RoutedHelloRpc.client.ts │ │ │ │ ├── RoutedHelloRpc.server.ts │ │ │ │ └── RoutedHelloRpc.ts │ │ │ ├── routedhello.ts │ │ │ └── run.sh │ │ └── wakeup │ │ │ ├── WakeUpRpc.proto │ │ │ ├── compile-protobuf.sh │ │ │ ├── proto │ │ │ ├── WakeUpRpc.client.ts │ │ │ ├── WakeUpRpc.server.ts │ │ │ ├── WakeUpRpc.ts │ │ │ └── google │ │ │ │ └── protobuf │ │ │ │ └── empty.ts │ │ │ ├── run.sh │ │ │ └── wakeup.ts │ ├── jest.config.ts │ ├── karma.config.js │ ├── package.json │ ├── proto.sh │ ├── protos │ │ └── ProtoRpc.proto │ ├── src │ │ ├── ClientTransport.ts │ │ ├── ProtoCallContext.ts │ │ ├── RpcCommunicator.ts │ │ ├── ServerRegistry.ts │ │ ├── common.ts │ │ ├── errors.ts │ │ ├── exports.ts │ │ ├── protoClasses.ts │ │ ├── toProtoRpcClient.ts │ │ └── types │ │ │ └── textencoding.d.ts │ ├── test-proto.sh │ ├── test │ │ ├── integration │ │ │ ├── ClientRpcTransport.test.ts │ │ │ └── toProtoRpcClient.test.ts │ │ ├── protos │ │ │ ├── HelloRpc.proto │ │ │ ├── TestProtos.proto │ │ │ └── WakeUpRpc.proto │ │ ├── unit │ │ │ ├── ConversionWrappers.test.ts │ │ │ ├── RpcCommunicator.test.ts │ │ │ └── ServerRegistry.test.ts │ │ └── utils.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── sdk │ ├── .babel.browser.config.js │ ├── .editorconfig │ ├── .gitignore │ ├── .idea │ │ ├── codeStyles │ │ │ ├── Project.xml │ │ │ └── codeStyleConfig.xml │ │ ├── inspectionProfiles │ │ │ └── Project_Default.xml │ │ ├── jsLibraryMappings.xml │ │ ├── misc.xml │ │ ├── modules.xml │ │ ├── streamr-client-javascript.iml │ │ └── vcs.xml │ ├── .vscode │ │ └── launch.json │ ├── LICENSE │ ├── README.md │ ├── bin │ │ └── generate-config-validator.js │ ├── eslint.config.mjs │ ├── fix-esm.sh │ ├── jest.config.ts │ ├── jest.setup.ts │ ├── karma-end-to-end.config.js │ ├── karma-integration.config.js │ ├── karma-setup.js │ ├── karma-unit.config.js │ ├── package.json │ ├── proto.sh │ ├── protos │ │ └── SdkRpc.proto │ ├── readme-header.png │ ├── scripts │ │ └── postbuild.sh │ ├── src │ │ ├── Config.ts │ │ ├── ConfigTest.ts │ │ ├── DestroySignal.ts │ │ ├── Message.ts │ │ ├── MetricsPublisher.ts │ │ ├── NetworkNodeFacade.ts │ │ ├── PersistenceManager.ts │ │ ├── RpcProviderSource.ts │ │ ├── Stream.ts │ │ ├── StreamIDBuilder.ts │ │ ├── StreamMetadata.ts │ │ ├── StreamrClient.ts │ │ ├── StreamrClientError.ts │ │ ├── config.schema.json │ │ ├── contracts │ │ │ ├── ChainEventPoller.ts │ │ │ ├── ContractFactory.ts │ │ │ ├── ERC1271ContractFacade.ts │ │ │ ├── Operator.ts │ │ │ ├── OperatorRegistry.ts │ │ │ ├── StorageNodeRegistry.ts │ │ │ ├── StreamRegistry.ts │ │ │ ├── StreamStorageRegistry.ts │ │ │ ├── contract.ts │ │ │ ├── operatorContractUtils.ts │ │ │ └── searchStreams.ts │ │ ├── encryption │ │ │ ├── EncryptionUtil.ts │ │ │ ├── GroupKey.ts │ │ │ ├── GroupKeyManager.ts │ │ │ ├── KeyExchangeKeyPair.ts │ │ │ ├── LocalGroupKeyStore.ts │ │ │ ├── MLKEMKeyPair.ts │ │ │ ├── PublisherKeyExchange.ts │ │ │ ├── RSAKeyPair.ts │ │ │ ├── SubscriberKeyExchange.ts │ │ │ ├── decrypt.ts │ │ │ └── migrations │ │ │ │ ├── 001-initial.sql │ │ │ │ ├── 002-primary-key.sql │ │ │ │ ├── 003-encryption-keys.sql │ │ │ │ └── 004-latest-encryption-key-ids.sql │ │ ├── ethereumArtifacts │ │ │ ├── IERC1271.d.ts │ │ │ └── IERC1271Abi.json │ │ ├── ethereumUtils.ts │ │ ├── events.ts │ │ ├── exports-browser.ts │ │ ├── exports-commonjs.js │ │ ├── exports-esm.mjs │ │ ├── exports.ts │ │ ├── generated │ │ │ └── validateConfig.js │ │ ├── identity │ │ │ ├── ECDSAKeyPairIdentity.ts │ │ │ ├── EthereumKeyPairIdentity.ts │ │ │ ├── EthereumProviderIdentity.ts │ │ │ ├── Identity.ts │ │ │ ├── IdentityMapping.ts │ │ │ ├── KeyPairIdentity.ts │ │ │ └── MLDSAKeyPairIdentity.ts │ │ ├── index.ts │ │ ├── permission.ts │ │ ├── protocol │ │ │ ├── MessageID.ts │ │ │ ├── MessageRef.ts │ │ │ ├── StreamMessage.ts │ │ │ ├── StreamMessageTranslator.ts │ │ │ ├── ValidationError.ts │ │ │ ├── oldStreamMessageBinaryUtils.ts │ │ │ └── validations.ts │ │ ├── publish │ │ │ ├── GroupKeyQueue.ts │ │ │ ├── MessageFactory.ts │ │ │ ├── Publisher.ts │ │ │ └── messageChain.ts │ │ ├── signature │ │ │ ├── MessageSigner.ts │ │ │ ├── SignatureValidator.ts │ │ │ ├── createLegacySignaturePayload.ts │ │ │ └── createSignaturePayload.ts │ │ ├── subscribe │ │ │ ├── MessagePipelineFactory.ts │ │ │ ├── MessageStream.ts │ │ │ ├── MsgChainUtil.ts │ │ │ ├── Resends.ts │ │ │ ├── Subscriber.ts │ │ │ ├── Subscription.ts │ │ │ ├── SubscriptionSession.ts │ │ │ ├── messagePipeline.ts │ │ │ ├── ordering │ │ │ │ ├── GapFiller.ts │ │ │ │ ├── OrderMessages.ts │ │ │ │ └── OrderedMessageChain.ts │ │ │ ├── resendSubscription.ts │ │ │ └── waitForStorage.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── AggregatedError.ts │ │ │ ├── Gate.ts │ │ │ ├── GeneratorUtils.ts │ │ │ ├── LoggerFactory.ts │ │ │ ├── LoggingJsonRpcProvider.ts │ │ │ ├── Mapping.ts │ │ │ ├── PatchTsyringe.ts │ │ │ ├── Pipeline.ts │ │ │ ├── PushBuffer.ts │ │ │ ├── PushPipeline.ts │ │ │ ├── Scaffold.ts │ │ │ ├── Signal.ts │ │ │ ├── WebStreamToNodeStream.ts │ │ │ ├── addStreamToStorageNode.ts │ │ │ ├── encryptionCompliance.ts │ │ │ ├── iterators.ts │ │ │ ├── persistence │ │ │ ├── BrowserPersistence.mts │ │ │ ├── Persistence.ts │ │ │ ├── PersistenceContext.ts │ │ │ └── ServerPersistence.ts │ │ │ ├── promises.ts │ │ │ ├── utils.ts │ │ │ ├── uuid.ts │ │ │ ├── validateStreamMessage.ts │ │ │ └── waitForAssignmentsToPropagate.ts │ ├── test │ │ ├── benchmarks │ │ │ ├── SigningUtil.test.ts │ │ │ ├── publish.js │ │ │ ├── raw-pipeline.js │ │ │ ├── resend.js │ │ │ └── subscribe.js │ │ ├── browser-smoke-test │ │ │ ├── browser.js │ │ │ ├── nightwatch.conf.js │ │ │ ├── server.js │ │ │ ├── smoke-test.html │ │ │ └── smoke-test.sh │ │ ├── data │ │ │ └── utf8Example.txt │ │ ├── end-to-end │ │ │ ├── MemoryLeaks.test.ts │ │ │ ├── Metrics.test.ts │ │ │ ├── Operator.test.ts │ │ │ ├── Permissions.test.ts │ │ │ ├── StorageNodeRegistry.test.ts │ │ │ ├── StorageNodeRegistry2.test.ts │ │ │ ├── StreamRegistry.test.ts │ │ │ ├── StreamrClient.test.ts │ │ │ ├── binary-publish.test.ts │ │ │ ├── contract-call-cache.test.ts │ │ │ ├── contract-call-error.test.ts │ │ │ ├── decorated-contract.test.ts │ │ │ ├── erc1271-publish-subscribe.test.ts │ │ │ ├── get-diagnostic-info.test.ts │ │ │ ├── publish-subscribe-via-proxy.test.ts │ │ │ ├── publish-subscribe.test.ts │ │ │ ├── resend.test.ts │ │ │ ├── searchStreams.test.ts │ │ │ └── start-node.test.ts │ │ ├── ethereumArtifacts │ │ │ ├── TestERC1271.d.ts │ │ │ └── TestERC1271Abi.json │ │ ├── exports │ │ │ ├── package.json │ │ │ ├── tests │ │ │ │ ├── commonjs.js │ │ │ │ ├── esm.mjs │ │ │ │ └── typescript.ts │ │ │ ├── tsconfig.json │ │ │ └── webpack.config.js │ │ ├── integration │ │ │ ├── GroupKeyPersistence.test.ts │ │ │ ├── NetworkNodeFacade.test.ts │ │ │ ├── PublisherKeyExchange.test.ts │ │ │ ├── Resends.test.ts │ │ │ ├── Resends2.test.ts │ │ │ ├── Sequencing.test.ts │ │ │ ├── Stream.test.ts │ │ │ ├── StreamrClient.test.ts │ │ │ ├── Subscriber.test.ts │ │ │ ├── Subscriber2.test.ts │ │ │ ├── SubscriberKeyExchange.test.ts │ │ │ ├── basics.test.ts │ │ │ ├── client-destroy.test.ts │ │ │ ├── events.test.ts │ │ │ ├── gap-fill.test.ts │ │ │ ├── invalid-messages.test.ts │ │ │ ├── json-rpc-provider.test.ts │ │ │ ├── multiple-clients.test.ts │ │ │ ├── parallel-key-exchange.test.ts │ │ │ ├── parallel-publish.test.ts │ │ │ ├── pre-agreed-encryption-key.test.ts │ │ │ ├── publisher-key-reuse.test.ts │ │ │ ├── quantum-policy.test.ts │ │ │ ├── resend-and-subscribe.test.ts │ │ │ ├── resend-with-existing-key.test.ts │ │ │ ├── revoke-permissions.test.ts │ │ │ ├── sequential-resend-subscribe.test.ts │ │ │ ├── unsubscribe.test.ts │ │ │ ├── update-encryption-key.test.ts │ │ │ └── waitForStorage.test.ts │ │ ├── test-utils │ │ │ ├── FakeJsonRpcServer.ts │ │ │ ├── LeaksDetector.ts │ │ │ ├── ProxyHttpServer.ts │ │ │ ├── customMatchers.ts │ │ │ ├── deployTestERC1271Contract.ts │ │ │ ├── fake │ │ │ │ ├── FakeChain.ts │ │ │ │ ├── FakeERC1271ContractFacade.ts │ │ │ │ ├── FakeEnvironment.ts │ │ │ │ ├── FakeLogger.ts │ │ │ │ ├── FakeNetwork.ts │ │ │ │ ├── FakeNetworkNode.ts │ │ │ │ ├── FakeOperatorRegistry.ts │ │ │ │ ├── FakeStorageNode.ts │ │ │ │ ├── FakeStorageNodeRegistry.ts │ │ │ │ ├── FakeStreamRegistry.ts │ │ │ │ └── FakeStreamStorageRegistry.ts │ │ │ ├── jest-utils.ts │ │ │ ├── publish.ts │ │ │ └── utils.ts │ │ ├── types │ │ │ └── global.d.ts │ │ └── unit │ │ │ ├── AggregatedError.test.ts │ │ │ ├── ChainEventPoller.test.ts │ │ │ ├── Config.test.ts │ │ │ ├── Decrypt.test.ts │ │ │ ├── ECDSAKeyPairIdentity.test.ts │ │ │ ├── ERC1271ContractFacade.test.ts │ │ │ ├── EncryptionUtil.test.ts │ │ │ ├── EthereumKeyPairIdentity.test.ts │ │ │ ├── GapFiller.test.ts │ │ │ ├── GroupKey.test.ts │ │ │ ├── GroupKeyManager.test.ts │ │ │ ├── GroupKeyQueue.test.ts │ │ │ ├── IteratorTest.ts │ │ │ ├── LocalGroupKeyStore.test.ts │ │ │ ├── MLDSAKeyPairIdentity.test.ts │ │ │ ├── Mapping.test.ts │ │ │ ├── MessageFactory.test.ts │ │ │ ├── MessageRef.test.ts │ │ │ ├── MessageStream.test.ts │ │ │ ├── MetricsPublisher.test.ts │ │ │ ├── Operator.test.ts │ │ │ ├── OrderMessages.test.ts │ │ │ ├── OrderMessages2.test.ts │ │ │ ├── OrderedMessageChain.test.ts │ │ │ ├── Pipeline.test.ts │ │ │ ├── Publisher.test.ts │ │ │ ├── PushBuffer.test.ts │ │ │ ├── PushPipeline.test.ts │ │ │ ├── Resends.test.ts │ │ │ ├── Scaffold.test.ts │ │ │ ├── ServerPersistence.test.ts │ │ │ ├── Signal.test.ts │ │ │ ├── SignatureValidator.test.ts │ │ │ ├── Stream.test.ts │ │ │ ├── StreamIDBuilder.test.ts │ │ │ ├── StreamMessage.test.ts │ │ │ ├── StreamMessageTranslator.test.ts │ │ │ ├── StreamMetadata.test.ts │ │ │ ├── StreamrClient.test.ts │ │ │ ├── SubscriptionSession.test.ts │ │ │ ├── WebStreamToNodeStream.test.ts │ │ │ ├── contract.test.ts │ │ │ ├── customMatchers.test.ts │ │ │ ├── events.test.ts │ │ │ ├── iterators.test.ts │ │ │ ├── messagePipeline.test.ts │ │ │ ├── oldStreamMessageBinaryUtils.test.ts │ │ │ ├── promises.test.ts │ │ │ ├── resendSubscription.test.ts │ │ │ ├── utils.test.ts │ │ │ ├── uuid.test.ts │ │ │ ├── validateStreamMessage.test.ts │ │ │ ├── validateStreamMessage2.test.ts │ │ │ ├── validations.test.ts │ │ │ └── waitForAssignmentsToPropagate.test.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── webpack-karma.config.js │ └── webpack.config.js ├── test-utils │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── codeStyles │ │ │ └── codeStyleConfig.xml │ │ ├── inspectionProfiles │ │ │ └── Project_Default.xml │ │ ├── modules.xml │ │ ├── streamr-test-utils.iml │ │ └── vcs.xml │ ├── LICENSE │ ├── README.md │ ├── customMatcherTypes.d.ts │ ├── jest.config.ts │ ├── package.json │ ├── setupCustomMatchers.js │ ├── src │ │ ├── customMatcherTypes.ts │ │ ├── customMatchers.ts │ │ ├── index.ts │ │ └── setupCustomMatchers.ts │ ├── test │ │ ├── customMatchers.test.ts │ │ └── index.test.ts │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── trackerless-network │ ├── .gitignore │ ├── README.md │ ├── jest.config.ts │ ├── karma-setup.js │ ├── karma.config.js │ ├── package.json │ ├── proto.sh │ ├── protos │ │ └── NetworkRpc.proto │ ├── src │ │ ├── ContentDeliveryManager.ts │ │ ├── NetworkNode.ts │ │ ├── NetworkStack.ts │ │ ├── NodeInfoClient.ts │ │ ├── NodeInfoRpcLocal.ts │ │ ├── NodeInfoRpcRemote.ts │ │ ├── StreamPartNetworkSplitAvoidance.ts │ │ ├── StreamPartReconnect.ts │ │ ├── content-delivery-layer │ │ │ ├── ContentDeliveryLayerNode.ts │ │ │ ├── ContentDeliveryRpcLocal.ts │ │ │ ├── ContentDeliveryRpcRemote.ts │ │ │ ├── DuplicateMessageDetector.ts │ │ │ ├── NodeList.ts │ │ │ ├── createContentDeliveryLayerNode.ts │ │ │ ├── formStreamPartDeliveryServiceId.ts │ │ │ ├── inspection │ │ │ │ ├── InspectSession.ts │ │ │ │ └── Inspector.ts │ │ │ ├── neighbor-discovery │ │ │ │ ├── HandshakeRpcLocal.ts │ │ │ │ ├── HandshakeRpcRemote.ts │ │ │ │ ├── Handshaker.ts │ │ │ │ ├── NeighborFinder.ts │ │ │ │ ├── NeighborUpdateManager.ts │ │ │ │ ├── NeighborUpdateRpcLocal.ts │ │ │ │ └── NeighborUpdateRpcRemote.ts │ │ │ ├── plum-tree │ │ │ │ ├── PausedNeighbors.ts │ │ │ │ ├── PlumTreeManager.ts │ │ │ │ ├── PlumTreeRpcLocal.ts │ │ │ │ └── PlumTreeRpcRemote.ts │ │ │ ├── propagation │ │ │ │ ├── FifoMapWithTTL.ts │ │ │ │ ├── Propagation.ts │ │ │ │ └── PropagationTaskStore.ts │ │ │ ├── proxy │ │ │ │ ├── ProxyClient.ts │ │ │ │ ├── ProxyConnectionRpcLocal.ts │ │ │ │ └── ProxyConnectionRpcRemote.ts │ │ │ └── temporary-connection │ │ │ │ ├── TemporaryConnectionRpcLocal.ts │ │ │ │ └── TemporaryConnectionRpcRemote.ts │ │ ├── control-layer │ │ │ ├── ControlLayerNode.ts │ │ │ ├── ExternalNetworkRpc.ts │ │ │ └── PeerDescriptorStoreManager.ts │ │ ├── discovery-layer │ │ │ └── DiscoveryLayerNode.ts │ │ ├── exports.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── test │ │ ├── benchmark │ │ │ ├── StreamPartIdDataKeyDistribution.test.ts │ │ │ └── first-message.ts │ │ ├── end-to-end │ │ │ ├── content-delivery-layer-node-with-real-connections.test.ts │ │ │ ├── external-network-rpc.test.ts │ │ │ ├── inspect.test.ts │ │ │ ├── proxy-and-full-node.test.ts │ │ │ ├── proxy-connections.test.ts │ │ │ ├── proxy-key-exchange.test.ts │ │ │ ├── webrtc-full-node-network.test.ts │ │ │ └── websocket-full-node-network.test.ts │ │ ├── integration │ │ │ ├── ContentDeliveryLayerNode-Layer1Node-Latencies.test.ts │ │ │ ├── ContentDeliveryLayerNode-Layer1Node.test.ts │ │ │ ├── ContentDeliveryManager.test.ts │ │ │ ├── ContentDeliveryRpcRemote.test.ts │ │ │ ├── HandshakeRpcRemote.test.ts │ │ │ ├── Handshakes.test.ts │ │ │ ├── Inspect.test.ts │ │ │ ├── NeighborUpdateRpcRemote.test.ts │ │ │ ├── NetworkNode.test.ts │ │ │ ├── NetworkRpc.test.ts │ │ │ ├── NetworkStack.test.ts │ │ │ ├── NodeInfoRpc.test.ts │ │ │ ├── PlumTreePropagation.test.ts │ │ │ ├── Propagation.test.ts │ │ │ ├── joining-streams-on-offline-peers.test.ts │ │ │ ├── stream-without-default-entrypoints.test.ts │ │ │ └── streamEntryPointReplacing.test.ts │ │ ├── types │ │ │ └── global.d.ts │ │ ├── unit │ │ │ ├── ContentDeliveryLayerNode.test.ts │ │ │ ├── ContentDeliveryManager.test.ts │ │ │ ├── ContentDeliveryRpcLocal.test.ts │ │ │ ├── DuplicateMessageDetector.test.ts │ │ │ ├── ExternalNetworkRpc.test.ts │ │ │ ├── FifoMapWithTtl.test.ts │ │ │ ├── HandshakeRpcLocal.test.ts │ │ │ ├── Handshaker.test.ts │ │ │ ├── InspectSession.test.ts │ │ │ ├── Inspector.test.ts │ │ │ ├── NeighborFinder.test.ts │ │ │ ├── NeighborUpdateRpcLocal.test.ts │ │ │ ├── NetworkNode.test.ts │ │ │ ├── NodeList.test.ts │ │ │ ├── NumberPair.test.ts │ │ │ ├── PeerDescriptorStoreManager.test.ts │ │ │ ├── PlumTreeManager.test.ts │ │ │ ├── PlumTreeRpcLocal.test.ts │ │ │ ├── Propagation.test.ts │ │ │ ├── ProxyConnectionRpcRemote.test.ts │ │ │ ├── StreamPartIDDataKey.test.ts │ │ │ ├── StreamPartNetworkSplitAvoidance.test.ts │ │ │ ├── StreamPartReconnect.test.ts │ │ │ └── TemporaryConnectionRpcLocal.test.ts │ │ └── utils │ │ │ ├── fake │ │ │ └── FakePeerDescriptorStoreManager.ts │ │ │ ├── mock │ │ │ ├── MockConnectionsView.ts │ │ │ ├── MockControlLayerNode.ts │ │ │ ├── MockDiscoveryLayerNode.ts │ │ │ ├── MockHandshaker.ts │ │ │ ├── MockNeighborFinder.ts │ │ │ ├── MockNeighborUpdateManager.ts │ │ │ └── MockTransport.ts │ │ │ └── utils.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json └── utils │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── jest.config.ts │ ├── karma.config.js │ ├── package.json │ ├── src │ ├── Cache.ts │ ├── Defer.ts │ ├── ENSName.ts │ ├── EthereumAddress.ts │ ├── Gate.ts │ ├── Heap.ts │ ├── HexString.ts │ ├── Logger.ts │ ├── MapWithTtl.ts │ ├── Metric.ts │ ├── Multimap.ts │ ├── ObservableEventEmitter.ts │ ├── SigningUtil.ts │ ├── StreamID.ts │ ├── StreamPartID.ts │ ├── TheGraphClient.ts │ ├── UserID.ts │ ├── WeiAmount.ts │ ├── abortableTimers.ts │ ├── addManagedEventListener.ts │ ├── asAbortable.ts │ ├── binaryUtils.ts │ ├── collect.ts │ ├── composeAbortSignals.ts │ ├── crossPlatformCrypto.ts │ ├── executeSafePromise.ts │ ├── exports.ts │ ├── filePathToNodeFormat.ts │ ├── initEventGateway.ts │ ├── ipv4ToNumber.ts │ ├── isENSName.ts │ ├── keyToArrayIndex.ts │ ├── lengthPrefixedFrameUtils.ts │ ├── merge.ts │ ├── pTransaction.ts │ ├── partition.ts │ ├── raceForEvent.ts │ ├── randomString.ts │ ├── retry.ts │ ├── scheduleAtApproximateInterval.ts │ ├── scheduleAtFixedRate.ts │ ├── scheduleAtInterval.ts │ ├── toEthereumAddressOrENSName.ts │ ├── types.ts │ ├── until.ts │ ├── wait.ts │ ├── waitForEvent.ts │ ├── withRateLimit.ts │ └── withTimeout.ts │ ├── test │ ├── Cache.test.ts │ ├── Defer.test.ts │ ├── ENSName.test.ts │ ├── EthereumAddress.test.ts │ ├── Gate.test.ts │ ├── Heap.test.ts │ ├── Logger.test.ts │ ├── MapWithTtl.test.ts │ ├── Metric.test.ts │ ├── Multimap.test.ts │ ├── ObservableEventEmitter.test.ts │ ├── SigningUtil.test.ts │ ├── StreamID.test.ts │ ├── StreamPartID.test.ts │ ├── TheGraphClient.test.ts │ ├── abortableTimers.test.ts │ ├── addManagedEventListener.test.ts │ ├── asAbortable.test.ts │ ├── binaryUtils.test.ts │ ├── collect.test.ts │ ├── composeAbortSignals.test.ts │ ├── executeSafePromise.test.ts │ ├── initEventGateway.test.ts │ ├── ipv4ToNumber.test.ts │ ├── keyToArrayIndex.test.ts │ ├── lengthPrefixFrameUtils.test.ts │ ├── merge.test.ts │ ├── pTransaction.test.ts │ ├── raceForEvent.test.ts │ ├── randomString.test.ts │ ├── retry.test.ts │ ├── scheduleAtApproximateInterval.test.ts │ ├── scheduleAtFixedRate.test.ts │ ├── scheduleAtInterval.test.ts │ ├── toEthereumAddressOrENSName.test.ts │ ├── types │ │ └── global.d.ts │ ├── until.test.ts │ ├── wait.test.ts │ ├── waitForEvent.test.ts │ ├── withRateLimit.test.ts │ └── withTimeout.test.ts │ ├── tsconfig.browser.json │ ├── tsconfig.jest.json │ ├── tsconfig.json │ └── tsconfig.node.json ├── release-git-tags.sh ├── release.sh ├── show-versions.mjs ├── tsconfig.browser.json ├── tsconfig.jest.json ├── tsconfig.node.json └── update-versions.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/npm-debug.log 3 | **/test 4 | **/.idea 5 | **/*.iml 6 | **/jest.config.js 7 | node_modules 8 | *.log 9 | **/*.log 10 | **/*.DS_Store 11 | .git 12 | **/build 13 | **/.gradle 14 | **/dist 15 | .DS_Store 16 | **/tests_output 17 | .editorconfig 18 | **/.editorconfig 19 | -------------------------------------------------------------------------------- /.github/healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "need 2 arguments: " 4 | exit 1 5 | fi 6 | for (( i=0; i < 5; i=i+1 )); do 7 | docker inspect --format '{{json .State.Health }}' $2 8 | curl -s $1; 9 | res=$?; 10 | if test "$res" != "0"; then 11 | echo "Attempting to connect to $1 retrying in $wait_time seconds"; 12 | sleep $wait_time; 13 | wait_time=$(( 2*wait_time )) ; 14 | else 15 | exit 0 16 | fi; 17 | done; 18 | exit 1 19 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | Please provide a summary of the changes and a motivation if applicable. 4 | 5 | ## Changes 6 | 7 | Provide a bullet list of individual changes. Leave this section out if change 8 | set is small and obvious from summary. 9 | 10 | ## Limitations and future improvements 11 | 12 | Provide a bullet list or description of known omissions or limitations of the 13 | solution and/or any ideas for future improvement. Leave section ouf if 14 | not applicable. 15 | 16 | ## Checklist before requesting a review 17 | 18 | - [ ] Is this a breaking change? If it is, be clear in summary. 19 | - [ ] Read through code myself one more time. 20 | - [ ] Make sure any and all `TODO` comments left behind are meant to be left in. 21 | - [ ] Has reasonable passing test coverage? 22 | - [ ] Updated changelog if applicable. 23 | - [ ] Updated documentation if applicable. 24 | -------------------------------------------------------------------------------- /.github/workflows/auto-release-docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: auto release docker 3 | on: 4 | workflow_run: 5 | branches: [main] 6 | workflows: [validate] 7 | types: 8 | - completed 9 | jobs: 10 | node-docker-image: 11 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 12 | uses: ./.github/workflows/docker-build.yml 13 | with: 14 | docker_file: Dockerfile.node 15 | image_name: streamr/node 16 | host_machine_platform: self-hosted 17 | build_platforms: linux/amd64,linux/arm64 18 | branch: ${{ github.event.workflow_run.head_branch }} 19 | push_image: true 20 | secrets: 21 | dockerhub_username: ${{secrets.DOCKERHUB_USERNAME}} 22 | dockerhub_token: ${{secrets.DOCKERHUB_TOKEN}} 23 | -------------------------------------------------------------------------------- /.github/workflows/close-stale-issue-and-pr.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close stale issues and PRs 3 | 4 | on: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | jobs: 8 | stale: 9 | name: Close stale issues and PRs 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/stale@v9 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | days-before-stale: 60 16 | days-before-close: 7 17 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | --- 8 | name: Labeler 9 | on: 10 | pull_request_target: 11 | 12 | jobs: 13 | label: 14 | name: Label 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/labeler@v5 18 | with: 19 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 20 | sync-labels: true 21 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: publish npm 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | type: choice 8 | required: true 9 | default: SELECT 10 | options: 11 | - SELECT 12 | - latest 13 | - beta 14 | - alpha 15 | - internal 16 | - testnet-three 17 | jobs: 18 | publish: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: build (cached) 23 | uses: ./.github/workflows/reusable/cached-build 24 | - run: bash release.sh ${{ github.event.inputs.tag }} 25 | env: 26 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/workflows/release-docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: release docker 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | include_latest_tag: 7 | type: choice 8 | required: true 9 | description: 'Include "latest" as one of the tags to be published' 10 | default: 'false' 11 | options: ['false', 'true'] 12 | jobs: 13 | node-docker-image: 14 | uses: ./.github/workflows/docker-build.yml 15 | with: 16 | docker_file: Dockerfile.node 17 | image_name: streamr/node 18 | host_machine_platform: self-hosted 19 | build_platforms: linux/amd64,linux/arm64 20 | branch: ${{ github.ref_name }} 21 | push_image: true 22 | include_latest_tag: ${{ github.event.inputs.include_latest_tag == 'true' }} 23 | secrets: 24 | dockerhub_username: ${{secrets.DOCKERHUB_USERNAME}} 25 | dockerhub_token: ${{secrets.DOCKERHUB_TOKEN}} 26 | -------------------------------------------------------------------------------- /.github/workflows/reusable/collect-and-upload-logs/action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "collect and upload logs" 3 | description: "collect and upload logs (e.g. Docker services, nodes) on test failure" 4 | inputs: 5 | artifact_prefix: 6 | description: a prefix to use in the filename for the uploaded artifact 7 | required: true 8 | runs: 9 | using: "composite" 10 | steps: 11 | - name: collect docker service logs 12 | uses: jwalton/gh-docker-logs@v2.2.2 13 | with: 14 | dest: 'logs' 15 | - name: upload logs to GitHub 16 | uses: actions/upload-artifact@v4 17 | with: 18 | name: logs-${{ inputs.artifact_prefix }}-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt}} 19 | path: 'logs' 20 | if-no-files-found: ignore 21 | -------------------------------------------------------------------------------- /.github/workflows/reusable/run-entry-point/action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "run entry point" 3 | description: "run entry point dht node directly from the monorepo codebase" 4 | 5 | runs: 6 | using: "composite" 7 | steps: 8 | - name: run entry point 9 | run: | 10 | mkdir -p logs 11 | ./bin/run-entry-point.sh >logs/entry-point-logs.txt 2>&1 & 12 | shell: bash 13 | working-directory: './' 14 | -------------------------------------------------------------------------------- /.github/workflows/reusable/run-nodes/action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "run nodes" 3 | description: "run nodes directly from the monorepo codebase" 4 | 5 | runs: 6 | using: "composite" 7 | steps: 8 | - name: run nodes 9 | run: | 10 | mkdir -p logs 11 | ./bin/run-nodes.sh >logs/node-logs.txt 2>&1 & 12 | shell: bash 13 | working-directory: './' 14 | -------------------------------------------------------------------------------- /.github/workflows/test-docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Building Documentation 3 | 4 | on: 5 | pull_request: 6 | branches: [main, brubeck] 7 | 8 | jobs: 9 | deploy: 10 | name: Test building documentation 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Use Node.js 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version-file: '.nvmrc' 18 | cache: npm 19 | cache-dependency-path: 'package-lock.json' 20 | - name: Install dependencies 21 | run: npm run bootstrap 22 | - name: Build documentation 23 | working-directory: ./docs 24 | run: npm ci && npm run build 25 | - name: API docs smoke test 26 | working-directory: ./docs 27 | run: ./api-docs-smoke-test.sh 28 | -------------------------------------------------------------------------------- /.github/workflows/yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: YAML lint 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | jobs: 11 | lint: 12 | name: Lint yaml files 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out the repo 16 | uses: actions/checkout@v4.1.6 17 | - name: Run YAML Lint 18 | uses: ibiqlik/action-yamllint@v3.1.1 19 | with: 20 | file_or_dir: . 21 | strict: true 22 | no_warnings: false 23 | config_file: .yamllint 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | lerna-debug.log 4 | *.tsbuildinfo 5 | *.heapprofile 6 | *.heapsnapshot 7 | oldsrc 8 | .vscode -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | # GitHub Copilot persisted chat sessions 10 | /copilot/chatSessions 11 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | cassandra 6 | true 7 | com.dbschema.CassandraJdbcDriver 8 | jdbc:cassandra://10.200.10.1:9042/streamr_dev_v2 9 | 10 | 11 | 12 | 13 | 14 | $ProjectFileDir$ 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/broker__all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/broker__dev_1_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/broker__dev_2_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/broker__dev_3_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/broker__dev_prod_resend_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/client__all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/client__end_to_end_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations/client__integration_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations/client__unit_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations/network__all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/network_tracker__all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations/tracker_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/runConfigurations/tracker_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/runConfigurations/tracker_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/runConfigurations/utils__all_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # See bug in peerdeps resolution: https://github.com/npm/cli/issues/2999 2 | legacy-peer-deps = true 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.13.0 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "semi": false 4 | } 5 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | trailing-spaces: {} 6 | line-length: 7 | disable 8 | new-lines: 9 | type: unix 10 | truthy: 11 | disable 12 | new-line-at-end-of-file: 13 | disable 14 | comments: 15 | require-starting-space: false 16 | ignore-shebangs: false 17 | min-spaces-from-content: 1 18 | 19 | ignore: | 20 | /node_modules/ 21 | /packages/dht/node_modules/ 22 | /packages/network/node_modules/ -------------------------------------------------------------------------------- /bin/run-entry-point.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # move to the directory where this script is located so that we can use relative paths 4 | cd "${0%/*}" 5 | 6 | cd ../packages/node/dist/bin 7 | 8 | chmod +x entry-point.js 9 | 10 | ./entry-point.js 11 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | /.vscode 22 | 23 | # Autogenerated API documentation 24 | /docs/usage/sdk/api -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Streamr Docs repo! 2 | 3 | Checkout the [Docs](https://docs.streamr.network)! 4 | 5 | ## Community contributions 6 | Please feel very welcome to submit content suggestions! 7 | 8 | For ease of use, small changes can be suggested by editing the markdown file on GitHub. Every content page has an "Edit this page" link at the bottom- Simply edit the text contents and submit a pull request to have the change reviewed before it is implemented. 9 | 10 | ### Run it locally 11 | Simply, `npm ci` & `npm start` 12 | 13 | ### Create & serve production build locally 14 | Simply, `npm ci` & `npm run build` & `npm run serve` 15 | 16 | ### Clean & clear build folder 17 | Simply, `npm run clear` 18 | 19 | -------------------------------------------------------------------------------- /docs/api-docs-smoke-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # If TypeDoc succeeds, it creates the directory with markdown files that Docusaurus uses to generate the API docs. 4 | # If TypeDoc fails, Docusaurus ignores the error, builds the site without API docs, and the "npm run build" 5 | # script still returns 0. This is why this separate smoke test is needed. 6 | 7 | # Note that the TypeDoc can fail with only [warning] lines printed. In particular, if this smoke test failing, 8 | # and the docs build prints lines like this: 9 | # 10 | # [warning] SignatureTypeString, defined in [...]/packages/sdk/src/Message.ts, is referenced by Message.signatureType but not included in the documentation 11 | # [warning] Found 0 errors and 1 warnings 12 | # 13 | # Then adding the missing classes to exports.ts will probably fix the TypeDoc build. 14 | 15 | if [ ! -d "docs/usage/sdk/api" ]; then 16 | echo 'No files in "docs/usage/sdk/api" directory. Maybe TypeDoc failed?' 17 | exit 1 18 | fi -------------------------------------------------------------------------------- /docs/docs/guides/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Quickstart Guides", 3 | "position": 0, 4 | "collapsed": false, 5 | "link": { 6 | "slug": "/guides", 7 | "type": "generated-index" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/docs/help/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Help", 3 | "position": 6, 4 | "link": { 5 | "slug": "/help", 6 | "type": "generated-index" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/help/how-to-contribute.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # How to contribute 6 | Contributions to these docs, or any of our projects is warmly welcomed! 7 | 8 | You can find others developing on Streamr, as well as get in touch with the Streamr core contributors on our [#Dev Discord channel](https://discord.gg/gZAm8P7hK8). 9 | -------------------------------------------------------------------------------- /docs/docs/help/how-to-get-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Get support 6 | You can find others developing on Streamr, as well as get in touch with the Streamr core contributors on our [#Dev Discord channel](https://discord.gg/gZAm8P7hK8). 7 | 8 | You can also reach out to the core team over [email](mailto:contact@streamr.com)! -------------------------------------------------------------------------------- /docs/docs/streamr-network/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Streamr Network", 3 | "position": 2, 4 | "collapsed": false, 5 | "link": { 6 | "slug": "/streamr-network", 7 | "type": "generated-index" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/docs/streamr-network/incentives/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Incentives", 3 | "position": 4, 4 | "link": { 5 | "slug": "/incentives", 6 | "type": "generated-index" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/streamr-network/network-roles/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Network roles", 3 | "position": 3, 4 | "link": { 5 | "slug": "/network-roles", 6 | "type": "generated-index" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/streamr-network/network-roles/publishers-and-subscribers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Publishers and subscribers 6 | A Publisher is simply a node through which certain data enters the network. The data usually originates in an adjacent application that interfaces with the Streamr node, with the goal of delivering that data to Subscribers via the network. Streamr nodes relay the messages to other nodes they are connected to. 7 | 8 | A Subscriber is a node in the network that wants to receive messages from a stream. Just like Publishers, they also relay the messages to other nodes they are connected to (this is why the Network scales so well). Subscribers may have a range of different motivations for joining a stream – there could be an adjacent application that wants the data, they could be Operator nodes that relay the stream for rewards (see below), or they may even want to help the stream for charitable reasons. 9 | -------------------------------------------------------------------------------- /docs/docs/streamr-network/security/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Security and cryptography", 3 | "position": 5 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/streamr-project/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Streamr Project", 3 | "position": 3, 4 | "link": { 5 | "slug": "/streamr-project", 6 | "type": "generated-index" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/usage/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Usage", 3 | "position": 1, 4 | "collapsed": false, 5 | "link": { 6 | "slug": "/usage", 7 | "type": "generated-index" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/docs/usage/connect-apps-and-iot/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Connect apps & IOT", 3 | "position": 5 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/usage/sdk/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Streamr SDK", 3 | "position": 3 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/usage/streams/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Streams", 3 | "position": 2 4 | } 5 | -------------------------------------------------------------------------------- /docs/docs/usage/streams/msg-ordering.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Message ordering 6 | Streams on the Streamr Network deliver ordered messages. 7 | 8 | If your use case tolerates missing messages and message arriving out-of-order, you can turn off message ordering and gap filling when creating a new instance of the Streamr SDK: 9 | 10 | ```ts 11 | const streamr = new StreamrClient({ 12 | auth: { ... }, 13 | orderMessages: false, 14 | gapFill: false 15 | }) 16 | ``` 17 | 18 | Both of these properties should be disabled in tandem for message ordering and gap filling to be properly turned off. 19 | 20 | By disabling message ordering your application won't perform any filling nor sorting, dispatching messages as they come (faster) but without granting their collective integrity. 21 | -------------------------------------------------------------------------------- /docs/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; 2 | 3 | // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) 4 | const sidebars: SidebarsConfig = { 5 | // By default, Docusaurus generates a sidebar from the docs folder structure 6 | streamrSidebar: [{ type: 'autogenerated', dirName: '.' }], 7 | }; 8 | 9 | export default sidebars; 10 | -------------------------------------------------------------------------------- /docs/src/pages/api-docs.tsx: -------------------------------------------------------------------------------- 1 | import Layout from "@theme/Layout" 2 | import React from "react" 3 | 4 | export default function APIDocs(): JSX.Element { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/assets/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": { 3 | "auth": { 4 | "privateKey": "NODE_PRIVATE_KEY" 5 | } 6 | }, 7 | "plugins": { 8 | "operator": { 9 | "operatorContractAddress": "YOUR_OPERATOR_CONTRACT_ADDRESS" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /docs/static/assets/testnet-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": { 3 | "metrics": false, 4 | "auth": { 5 | "privateKey": "NODE_PRIVATE_KEY" 6 | }, 7 | "environment": "polygonAmoy" 8 | }, 9 | "plugins": { 10 | "operator": { 11 | "operatorContractAddress": "YOUR_OPERATOR_CONTRACT_ADDRESS" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /docs/static/img/DATA-flows-Operator-contract.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/DATA-flows-Operator-contract.jpg -------------------------------------------------------------------------------- /docs/static/img/DATA-flows-Sponsorship-contract.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/DATA-flows-Sponsorship-contract.jpg -------------------------------------------------------------------------------- /docs/static/img/DATA-flows-high-level.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/DATA-flows-high-level.jpg -------------------------------------------------------------------------------- /docs/static/img/mqtt-guide-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/mqtt-guide-1.png -------------------------------------------------------------------------------- /docs/static/img/mumbai-to-testnet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/mumbai-to-testnet.jpg -------------------------------------------------------------------------------- /docs/static/img/network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/network.png -------------------------------------------------------------------------------- /docs/static/img/node-addresses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/node-addresses.png -------------------------------------------------------------------------------- /docs/static/img/noderunner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/noderunner.png -------------------------------------------------------------------------------- /docs/static/img/operator-address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/operator-address.png -------------------------------------------------------------------------------- /docs/static/img/operator-flows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/operator-flows.png -------------------------------------------------------------------------------- /docs/static/img/operator-sponsorship-relational-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/operator-sponsorship-relational-diagram.png -------------------------------------------------------------------------------- /docs/static/img/operator-status-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/operator-status-green.png -------------------------------------------------------------------------------- /docs/static/img/public-stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/public-stream.png -------------------------------------------------------------------------------- /docs/static/img/quickstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/quickstart.png -------------------------------------------------------------------------------- /docs/static/img/sponsor-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/sponsor-diagram.png -------------------------------------------------------------------------------- /docs/static/img/stream-sponsorship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/stream-sponsorship.png -------------------------------------------------------------------------------- /docs/static/img/streams_partioning_01.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/streams_partioning_01.jpeg -------------------------------------------------------------------------------- /docs/static/img/streams_partioning_02.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/streams_partioning_02.jpeg -------------------------------------------------------------------------------- /docs/static/img/testnets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/testnets.jpg -------------------------------------------------------------------------------- /docs/static/img/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/docs/static/img/usage.png -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | }, 7 | "exclude": [".docusaurus", "build"] 8 | } 9 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | 3 | const config = { 4 | preset: 'ts-jest', 5 | transform: { 6 | '^.+\\.ts$': ['ts-jest', { 7 | tsconfig: 'tsconfig.jest.json' 8 | }], 9 | }, 10 | testEnvironment: 'node', 11 | clearMocks: true, 12 | setupFilesAfterEnv: ['jest-extended/all'], 13 | } as const satisfies Config.InitialOptions 14 | 15 | export default config 16 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent", 6 | "npmClient": "npm", 7 | "npmClientArgs": [ 8 | "--prefer-offline", 9 | "--no-audit", 10 | "--progress=false" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/autocertifier-client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | generated 5 | -------------------------------------------------------------------------------- /packages/autocertifier-client/.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /src 3 | /test 4 | /generated 5 | jest.config.js 6 | tsconfig.json 7 | tsconfig.*.json 8 | *.tsbuildinfo 9 | .eslintignore 10 | .eslintrc 11 | -------------------------------------------------------------------------------- /packages/autocertifier-client/proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./generated 2 | npx protoc --ts_out ./generated --ts_opt server_generic,generate_dependencies,long_type_number --proto_path ../.. packages/autocertifier-client/protos/AutoCertifier.proto 3 | -------------------------------------------------------------------------------- /packages/autocertifier-client/protos/AutoCertifier.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | message HasSessionRequest { 5 | string sessionId = 1; 6 | } 7 | 8 | message HasSessionResponse { 9 | } 10 | 11 | service AutoCertifierRpc { 12 | rpc hasSession (HasSessionRequest) returns (HasSessionResponse); 13 | } 14 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/ApiError.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode } from '../errors' 2 | 3 | // TODO: only used by server package? 4 | export interface ApiError { 5 | code: ErrorCode 6 | message?: string 7 | } 8 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/CertifiedSubdomain.ts: -------------------------------------------------------------------------------- 1 | export interface CertifiedSubdomain { 2 | fqdn: string 3 | authenticationToken: string 4 | certificate: string 5 | privateKey: string 6 | } 7 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/CreateCertifiedSubdomainRequest.ts: -------------------------------------------------------------------------------- 1 | export interface CreateCertifiedSubdomainRequest { 2 | streamrWebSocketPort: number 3 | sessionId: string 4 | streamrWebSocketCaCert?: string 5 | } 6 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/HttpStatus.ts: -------------------------------------------------------------------------------- 1 | // TODO: server stuff, shouldn't be in this package or remove completely as these are standard numbers 2 | export enum HttpStatus { 3 | OK = 200, 4 | CREATED = 201, 5 | NO_CONTENT = 204, 6 | BAD_REQUEST = 400, 7 | UNAUTHORIZED = 401, 8 | FORBIDDEN = 403, 9 | NOT_FOUND = 404, 10 | METHOD_NOT_ALLOWED = 405, 11 | CONFLICT = 409, 12 | INTERNAL_SERVER_ERROR = 500, 13 | NOT_IMPLEMENTED = 501, 14 | SERVICE_UNAVAILABLE = 503 15 | } 16 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/Session.ts: -------------------------------------------------------------------------------- 1 | export interface Session { 2 | id: string 3 | } 4 | -------------------------------------------------------------------------------- /packages/autocertifier-client/src/data/UpdateIpAndPortRequest.ts: -------------------------------------------------------------------------------- 1 | // TODO: rename to UpdateIpRequest? 2 | export interface UpdateIpAndPortRequest { 3 | token: string 4 | sessionId: string 5 | streamrWebSocketPort: number 6 | } 7 | -------------------------------------------------------------------------------- /packages/autocertifier-client/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "generated/**/*", 11 | "test/**/*" 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /packages/autocertifier-client/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "generated/**/*", 6 | "test/**/*" 7 | ], 8 | "references": [ 9 | { "path": "../utils/tsconfig.node.json" } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/autocertifier-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/autocertifier-client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*" 10 | ], 11 | "references": [ 12 | { "path": "../utils/tsconfig.node.json" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/autocertifier-server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | .env 5 | -------------------------------------------------------------------------------- /packages/autocertifier-server/bin/run.ts: -------------------------------------------------------------------------------- 1 | import { AutoCertifierServer } from '../src/AutoCertifierServer' 2 | 3 | const main = async () => { 4 | const autoCertifierServer = new AutoCertifierServer() 5 | await autoCertifierServer.start() 6 | } 7 | 8 | // TODO: catch handling 9 | main() 10 | -------------------------------------------------------------------------------- /packages/autocertifier-server/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/autocertifier-server/src/ChallengeManager.ts: -------------------------------------------------------------------------------- 1 | export interface ChallengeManager { 2 | createChallenge(fqdn: string, value: string): Promise 3 | deleteChallenge(fqdn: string, value: string): Promise 4 | } 5 | -------------------------------------------------------------------------------- /packages/autocertifier-server/src/RestInterface.ts: -------------------------------------------------------------------------------- 1 | import { CertifiedSubdomain, Session } from '@streamr/autocertifier-client' 2 | 3 | // TODO: is this interface needed? Could be useful for testing purposes? 4 | // TODO: should streamrWebsocketPort be renamed? ie. requestorStreamrWebsocketPort / requestorWebsocketPort 5 | export interface RestInterface { 6 | createSession(): Promise 7 | 8 | createNewSubdomainAndCertificate(ipAddress: string, port: string, streamrWebSocketPort: string, 9 | sessionId: string): Promise 10 | 11 | createNewCertificateForSubdomain(subdomain: string, ipAddress: string, port: string, 12 | streamrWebSocketPort: string, sessionId: string, token: string): Promise 13 | 14 | updateSubdomainIp(subdomain: string, ipAddress: string, port: string, 15 | streamrWebSocketPort: string, sessionId: string, token: string): Promise 16 | } 17 | -------------------------------------------------------------------------------- /packages/autocertifier-server/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/autocertifier-server/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "bin/**/*", 6 | "test/**/*" 7 | ], 8 | "references": [ 9 | { "path": "../utils/tsconfig.node.json" }, 10 | { "path": "../test-utils/tsconfig.node.json" }, 11 | { "path": "../proto-rpc/tsconfig.node.json" }, 12 | { "path": "../autocertifier-client/tsconfig.node.json"} 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/autocertifier-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/autocertifier-server/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "bin/**/*", 10 | ], 11 | "references": [ 12 | { "path": "../utils/tsconfig.node.json" }, 13 | { "path": "../test-utils/tsconfig.node.json" }, 14 | { "path": "../proto-rpc/tsconfig.node.json" }, 15 | { "path": "../dht/tsconfig.node.json"} 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/browser-test-runner/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /packages/browser-test-runner/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { createKarmaConfig } from './createKarmaConfig' 2 | export { createWebpackConfig } from './createWebpackConfig' 3 | -------------------------------------------------------------------------------- /packages/browser-test-runner/src/preload.js: -------------------------------------------------------------------------------- 1 | // Loads non-browser compatible components to Electron's NodeJS sandbox during tests 2 | 3 | process.once('loaded', () => { 4 | // eslint-disable-next-line import/no-extraneous-dependencies 5 | window.WebSocket = require('ws') 6 | // eslint-disable-next-line import/no-extraneous-dependencies 7 | window.Express = require('express') 8 | window.HTTP = require('http') 9 | window.HTTPS = require('https') 10 | window.Buffer = require('buffer/').Buffer 11 | // maybe we can set this karma-setup 12 | // eslint-disable-next-line no-underscore-dangle 13 | window._streamr_electron_test = true 14 | }) 15 | -------------------------------------------------------------------------------- /packages/browser-test-runner/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "src/**/*" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/cdn-location/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | .env 5 | -------------------------------------------------------------------------------- /packages/cdn-location/data-generation/generateDataFromTSPSolverResult.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npx ts-node data-generation/generateIataToRegionNumberMapping.ts -------------------------------------------------------------------------------- /packages/cdn-location/data-generation/metropolitancodes.csv: -------------------------------------------------------------------------------- 1 | BJS PEK 2 | JKT CGK 3 | OSA KIX 4 | SPK CTS 5 | SEL ICN 6 | TYO NRT 7 | BUH OTP 8 | EAP BSL 9 | LON LHR 10 | MIL MXP 11 | MOW SVO 12 | PAR CDG 13 | ROM FCO 14 | STO ARN 15 | CHI ORD 16 | DTT DTW 17 | QHO IAH 18 | QLA LAX 19 | QMI MIA 20 | YMQ YUL 21 | NYC JFK 22 | QSF SFO 23 | YTO YYZ 24 | WAS IAD 25 | BUE EZE 26 | RIO GIG 27 | SAO GRU 28 | TXL BER 29 | DLP CDG 30 | KIV RMO 31 | ITJ NVT 32 | QWJ GRU 33 | ZGN CAN 34 | -------------------------------------------------------------------------------- /packages/cdn-location/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs' 2 | 3 | export default [ 4 | { 5 | ignores: [ 6 | 'data-generation/final-data/*.ts' 7 | ] 8 | }, 9 | ...baseConfig 10 | ] 11 | -------------------------------------------------------------------------------- /packages/cdn-location/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/cdn-location/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { 2 | getLocalRegion, 3 | getLocalRegionWithCache, 4 | getLocalRegionByCoordinates, 5 | getLocalAirportCode, 6 | getLocalAirportCodeByCoordinates 7 | } from './getLocalRegion' 8 | -------------------------------------------------------------------------------- /packages/cdn-location/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/cdn-location/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "data-generation/**/*", 6 | "test/**/*" 7 | ], 8 | "references": [ 9 | { "path": "../utils/tsconfig.node.json" }, 10 | { "path": "../test-utils/tsconfig.node.json" }, 11 | { "path": "../proto-rpc/tsconfig.node.json" }, 12 | { "path": "../autocertifier-client/tsconfig.node.json"} 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cdn-location/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/cdn-location/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "references": [ 11 | { "path": "../utils/tsconfig.node.json" }, 12 | { "path": "../test-utils/tsconfig.node.json" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/cli-tools/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | dist 4 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-identity-whoami.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient) => { 8 | console.info(await client.getUserId()) 9 | }) 10 | .description('displays your user id (public key)') 11 | .parseAsync() 12 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-identity.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | 5 | program 6 | .version(pkg.version) 7 | .usage(' []') 8 | .description('identity subcommands') 9 | .command('whoami', 'displays your public key') 10 | .command('generate', 'generates a new key pair') 11 | .parse() 12 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-operator-delegate.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | import { parseEther } from 'ethers' 7 | 8 | createClientCommand(async (client: StreamrClient, operatorContractAddress: string, dataTokenAmount: string) => { 9 | await _operatorContractUtils.delegate( 10 | await client.getSigner(), 11 | operatorContractAddress, 12 | parseEther(dataTokenAmount) 13 | ) 14 | }) 15 | .description('delegate funds to an operator') 16 | .arguments(' ') 17 | .parseAsync() 18 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-operator-grant-controller-role.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, operatorContractAddress: string, userId: string) => { 8 | const contract = _operatorContractUtils.getOperatorContract(operatorContractAddress).connect(await client.getSigner()) 9 | await (await contract.grantRole(await contract.CONTROLLER_ROLE(), userId)).wait() 10 | }) 11 | .description('grant controller role to a user') 12 | .arguments(' ') 13 | .parseAsync() 14 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-operator-stake.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | import { parseEther } from 'ethers' 7 | 8 | createClientCommand(async (client: StreamrClient, operatorContractAddress: string, sponsorshipAddress: string, dataTokenAmount: string) => { 9 | await _operatorContractUtils.stake( 10 | await client.getSigner(), 11 | operatorContractAddress, 12 | sponsorshipAddress, 13 | parseEther(dataTokenAmount) 14 | ) 15 | }) 16 | .description('stake funds to a sponsorship') 17 | .arguments(' ') 18 | .parseAsync() 19 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-operator-undelegate.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | import { parseEther } from 'ethers' 7 | 8 | createClientCommand(async (client: StreamrClient, operatorContractAddress: string, dataTokenAmount: string) => { 9 | await _operatorContractUtils.undelegate( 10 | await client.getSigner(), 11 | operatorContractAddress, 12 | parseEther(dataTokenAmount) 13 | ) 14 | }) 15 | .description('undelegate funds from an operator') 16 | .arguments(' ') 17 | .parseAsync() 18 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-operator-unstake.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, operatorContractAddress: string, sponsorshipAddress: string) => { 8 | await _operatorContractUtils.unstake( 9 | await client.getSigner(), 10 | operatorContractAddress, 11 | sponsorshipAddress 12 | ) 13 | }) 14 | .arguments(' ') 15 | .description('unstake all funds from a sponsorship') 16 | .parseAsync() 17 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-show-sdk-config.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import StreamrClient from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient) => { 8 | const config = client.getConfig() 9 | console.info(JSON.stringify(config, undefined, 4)) 10 | }) 11 | .parseAsync() 12 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-internal-sponsorship-sponsor.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient, _operatorContractUtils } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | import { parseEther } from 'ethers' 7 | 8 | createClientCommand(async (client: StreamrClient, sponsorshipAddress: string, dataTokenAmount: string) => { 9 | await _operatorContractUtils.sponsor( 10 | await client.getSigner(), 11 | sponsorshipAddress, 12 | parseEther(dataTokenAmount) 13 | ) 14 | }) 15 | .description('sponsor a stream') 16 | .arguments(' ') 17 | .parseAsync() 18 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-mock-data.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | 5 | program 6 | .version(pkg.version) 7 | .usage(' []') 8 | .description('mock-data subcommands') 9 | .command('generate', 'generate random JSON or binary data') 10 | .parse() 11 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-add-stream.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, storageNodeAddress: string, streamId: string) => { 8 | const stream = await client.getStream(streamId) 9 | await stream.addToStorageNode(storageNodeAddress) 10 | }) 11 | .arguments(' ') 12 | .description('add stream to a storage node') 13 | .parseAsync() 14 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-list-streams.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import EasyTable from 'easy-table' 5 | import { StreamrClient } from '@streamr/sdk' 6 | import { createClientCommand } from '../src/command' 7 | 8 | createClientCommand((async (client: StreamrClient, storageNodeAddress: string) => { 9 | const { streams } = await client.getStoredStreams(storageNodeAddress) 10 | if (streams.length > 0) { 11 | console.info(EasyTable.print(await Promise.all(streams.map(async (stream) => { 12 | return { 13 | id: stream.id, 14 | partitions: await stream.getPartitionCount() 15 | } 16 | })))) 17 | } 18 | })) 19 | .arguments('') 20 | .description('list stream parts in a storage node') 21 | .parseAsync() 22 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-list.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import EasyTable from 'easy-table' 6 | import { createClientCommand, Options as BaseOptions } from '../src/command' 7 | 8 | interface Options extends BaseOptions { 9 | stream?: string 10 | } 11 | 12 | createClientCommand(async (client: StreamrClient, options: Options) => { 13 | const streamId = options.stream 14 | const addresses = await client.getStorageNodes(streamId) 15 | if (addresses.length > 0) { 16 | console.info(EasyTable.print(addresses.map((address: string) => ({ 17 | address 18 | })))) 19 | } 20 | }) 21 | .description('fetch a list of storage nodes') 22 | .option('-s, --stream ', 'only storage nodes which store the given stream (needs authentication)') 23 | .parseAsync() 24 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-register.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, urls: string) => { 8 | await client.setStorageNodeMetadata({ 9 | urls: urls.split(',') 10 | }) 11 | }) 12 | .arguments('') 13 | .description('register the current wallet as a storage node with the provided metadata URLs (comma-separated)') 14 | .parseAsync() 15 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-remove-stream.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, storageNodeAddress: string, streamId: string) => { 8 | const stream = await client.getStream(streamId) 9 | await stream.removeFromStorageNode(storageNodeAddress) 10 | }) 11 | .arguments(' ') 12 | .description('remove stream from a storage node') 13 | .parseAsync() 14 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-show.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient, storageNodeAddress: string) => { 8 | const metadata = await client.getStorageNodeMetadata(storageNodeAddress) 9 | console.info(JSON.stringify(metadata, null, 2)) 10 | }) 11 | .arguments('') 12 | .description('show information about a storage node') 13 | .parseAsync() 14 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node-unregister.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { StreamrClient } from '@streamr/sdk' 5 | import { createClientCommand } from '../src/command' 6 | 7 | createClientCommand(async (client: StreamrClient) => { 8 | await client.setStorageNodeMetadata(undefined) 9 | }) 10 | .description('unregister the current wallet as a storage node') 11 | .parseAsync() 12 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-storage-node.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | 5 | program 6 | .version(pkg.version) 7 | .usage(' []') 8 | .description('storage node subcommands') 9 | .command('list', 'list storage nodes') 10 | .command('show', 'show information about a storage node') 11 | .command('register', 'register a storage node') 12 | .command('unregister', 'unregister a storage node') 13 | .command('add-stream', 'add stream') 14 | .command('remove-stream', 'remove stream') 15 | .command('list-streams', 'list stream in a storage node') 16 | .parse() 17 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-stream-grant-permission.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { PermissionAssignment, Stream } from '@streamr/sdk' 5 | import { runModifyPermissionsCommand } from '../src/permission' 6 | 7 | runModifyPermissionsCommand( 8 | (stream: Stream, assigment: PermissionAssignment) => stream.grantPermissions(assigment), 9 | 'grant' 10 | ) 11 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-stream-resend.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { createCommand } from '../src/command' 5 | 6 | createCommand() 7 | .usage(' []') 8 | .description('request resend of stream and print JSON messages to stdout line-by-line') 9 | .command('from', 'request messages starting from given date-time') 10 | .command('last', 'request last N messages') 11 | .command('range', 'request messages between two given date-times') 12 | .parse() 13 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-stream-revoke-permission.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import '../src/logLevel' 3 | 4 | import { PermissionAssignment, Stream } from '@streamr/sdk' 5 | import { runModifyPermissionsCommand } from '../src/permission' 6 | 7 | runModifyPermissionsCommand( 8 | (stream: Stream, assigment: PermissionAssignment) => stream.revokePermissions(assigment), 9 | 'revoke' 10 | ) 11 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr-stream.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | 5 | program 6 | .version(pkg.version) 7 | .usage(' []') 8 | .description('stream subcommands') 9 | .command('subscribe', 'subscribe to a stream') 10 | .command('publish', 'publish to a stream') 11 | .command('search', 'search for streams') 12 | .command('show', 'info about a stream') 13 | .command('create', 'create a new stream') 14 | .command('resend', 'request resend of a stream') 15 | .command('grant-permission', 'grant permission') 16 | .command('revoke-permission', 'revoke permission') 17 | .parse() 18 | -------------------------------------------------------------------------------- /packages/cli-tools/bin/streamr.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | 5 | program 6 | .version(pkg.version) 7 | .usage(' []') 8 | .description('command line tools for interacting with Streamr https://streamr.network') 9 | .command('stream', 'stream subcommands') 10 | .command('storage-node', 'storage node subcommands') 11 | .command('mock-data', 'mock data subcommands') 12 | .command('identity', 'subcommands for keys and wallets') 13 | .command('internal', 'subcommands for internal use') 14 | .parse() 15 | -------------------------------------------------------------------------------- /packages/cli-tools/cli-tools.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/cli-tools/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/cli-tools/src/logLevel.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal logging for NetworkNode and other users of Logger.ts in the network package. 3 | * This file needs to be imported before any of the network package classes 4 | * so that the environment variable is updated before any Logger instances are created. 5 | * The import is needed for the files where network packages are used (typically 6 | * the files which call createClientCommand()). 7 | */ 8 | 9 | process.env.LOG_LEVEL = process.env.LOG_LEVEL ?? 'error' 10 | -------------------------------------------------------------------------------- /packages/cli-tools/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "bin/**/*", 6 | "test/**/*" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/cli-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/cli-tools/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "dist" 6 | }, 7 | "include": [ 8 | "package.json", 9 | "src/**/*", 10 | "bin/**/*" 11 | ], 12 | "references": [ 13 | { "path": "../utils/tsconfig.node.json" }, 14 | { "path": "../sdk/tsconfig.node.json" } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/dht/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | coverage 5 | generated 6 | test/data/nodeids.json 7 | test/data/orderedneighbors.json 8 | -------------------------------------------------------------------------------- /packages/dht/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | import defaultConfig from '../../jest.config' 3 | 4 | const config: Config.InitialOptions = { 5 | ...defaultConfig, 6 | setupFilesAfterEnv: [ 7 | ...defaultConfig.setupFilesAfterEnv, 8 | './test/utils/customMatchers.ts', 9 | ], 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /packages/dht/karma-setup.js: -------------------------------------------------------------------------------- 1 | import * as customMatchers from './test/utils/customMatchers' 2 | window.expect.extend(customMatchers) -------------------------------------------------------------------------------- /packages/dht/proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./generated 2 | npx protoc --ts_out ./generated --ts_opt server_generic,generate_dependencies,long_type_number --proto_path ../.. packages/dht/protos/DhtRpc.proto 3 | -------------------------------------------------------------------------------- /packages/dht/protos/tests.proto: -------------------------------------------------------------------------------- 1 | 2 | syntax = "proto3"; 3 | option optimize_for = CODE_SIZE; 4 | 5 | package tests; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | message TestMessage { 10 | string messageId = 1; 11 | google.protobuf.Any body = 2; 12 | } 13 | 14 | message SomeMessage { 15 | string juttu = 1; 16 | } -------------------------------------------------------------------------------- /packages/dht/scripts/postbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p dist 4 | 5 | SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 6 | 7 | cd "${SCRIPT_DIR}/.." 8 | 9 | # Sanitize the final package.json 10 | npx ts-node scripts/rewrite-package.ts 11 | -------------------------------------------------------------------------------- /packages/dht/src/connection/ConnectionsView.ts: -------------------------------------------------------------------------------- 1 | import { DhtAddress } from '../identifiers' 2 | import { PeerDescriptor } from '../../generated/packages/dht/protos/DhtRpc' 3 | 4 | export interface ConnectionsView { 5 | getConnections: () => PeerDescriptor[] 6 | getConnectionCount: () => number 7 | hasConnection: (nodeId: DhtAddress) => boolean 8 | } 9 | -------------------------------------------------------------------------------- /packages/dht/src/connection/OutputBuffer.ts: -------------------------------------------------------------------------------- 1 | import { Defer } from '@streamr/utils' 2 | import { SendFailed } from '../helpers/errors' 3 | 4 | export class OutputBuffer { 5 | 6 | private readonly buffer: Uint8Array[] = [] 7 | private readonly deferredPromise: Defer = new Defer() 8 | 9 | push(message: Uint8Array): Defer { 10 | this.buffer.push(message) 11 | return this.deferredPromise 12 | } 13 | 14 | getBuffer(): Uint8Array[] { 15 | return this.buffer 16 | } 17 | 18 | resolve(): void { 19 | this.buffer.length = 0 20 | this.deferredPromise.resolve() 21 | } 22 | 23 | reject(): void { 24 | this.buffer.length = 0 25 | this.deferredPromise.reject(new SendFailed('Could not send buffered messages')) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /packages/dht/src/connection/simulator/SimulatorTransport.ts: -------------------------------------------------------------------------------- 1 | import { MetricsContext } from '@streamr/utils' 2 | import { PeerDescriptor } from '../../../generated/packages/dht/protos/DhtRpc' 3 | import { ConnectionManager } from '../ConnectionManager' 4 | import { Simulator } from './Simulator' 5 | import { SimulatorConnectorFacade } from '../ConnectorFacade' 6 | 7 | export class SimulatorTransport extends ConnectionManager { 8 | constructor(localPeerDescriptor: PeerDescriptor, simulator: Simulator) { 9 | super({ 10 | createConnectorFacade: () => new SimulatorConnectorFacade(localPeerDescriptor, simulator), 11 | metricsContext: new MetricsContext(), 12 | allowIncomingPrivateConnections: false 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/dht/src/connection/webrtc/iceServerAsString.ts: -------------------------------------------------------------------------------- 1 | import { IceServer } from './WebrtcConnector' 2 | 3 | export function iceServerAsString({ url, port, username, password, tcp }: IceServer): string { 4 | const [protocol, hostname] = url.split(':') 5 | if (hostname === undefined) { 6 | throw new Error(`invalid stun/turn format: ${url}`) 7 | } 8 | if (username === undefined && password === undefined) { 9 | return `${protocol}:${hostname}:${port}` 10 | } 11 | if (username !== undefined && password !== undefined) { 12 | return `${protocol}:${username}:${password}@${hostname}:${port}${(tcp !== undefined) ? '?transport=tcp' : ''}` 13 | } 14 | throw new Error(`username (${username}) and password (${password}) must be supplied together`) 15 | } 16 | -------------------------------------------------------------------------------- /packages/dht/src/connection/websocket/WebsocketClientConnectorRpcRemote.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WebsocketConnectionRequest 3 | } from '../../../generated/packages/dht/protos/DhtRpc' 4 | import { Logger } from '@streamr/utils' 5 | import { RpcRemote } from '../../dht/contact/RpcRemote' 6 | import { WebsocketClientConnectorRpcClient } from '../../../generated/packages/dht/protos/DhtRpc.client' 7 | import { toNodeId } from '../../identifiers' 8 | 9 | const logger = new Logger(module) 10 | 11 | export class WebsocketClientConnectorRpcRemote extends RpcRemote { 12 | 13 | async requestConnection(): Promise { 14 | logger.trace(`Requesting WebSocket connection from ${toNodeId(this.getLocalPeerDescriptor())}`) 15 | const request: WebsocketConnectionRequest = {} 16 | const options = this.formDhtRpcOptions() 17 | return this.getClient().requestConnection(request, options) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/dht/src/dht/contact/Contact.ts: -------------------------------------------------------------------------------- 1 | import { PeerDescriptor } from '../../../generated/packages/dht/protos/DhtRpc' 2 | import { DhtAddress, toNodeId } from '../../identifiers' 3 | 4 | export class Contact { 5 | 6 | private peerDescriptor: PeerDescriptor 7 | 8 | constructor(peerDescriptor: PeerDescriptor) { 9 | this.peerDescriptor = peerDescriptor 10 | } 11 | 12 | public getPeerDescriptor(): PeerDescriptor { 13 | return this.peerDescriptor 14 | } 15 | 16 | public getNodeId(): DhtAddress { 17 | return toNodeId(this.peerDescriptor) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/dht/src/dht/contact/getClosestNodes.ts: -------------------------------------------------------------------------------- 1 | import { PeerDescriptor } from '../../exports' 2 | import { DhtAddress } from '../../identifiers' 3 | import { Contact } from './Contact' 4 | import { SortedContactList } from './SortedContactList' 5 | 6 | export const getClosestNodes = ( 7 | referenceId: DhtAddress, 8 | contacts: Iterable, 9 | opts?: { 10 | maxCount?: number 11 | excludedNodeIds?: Set 12 | } 13 | ): PeerDescriptor[] => { 14 | const list = new SortedContactList({ 15 | referenceId, 16 | allowToContainReferenceId: true, 17 | excludedNodeIds: opts?.excludedNodeIds, 18 | maxSize: opts?.maxCount 19 | }) 20 | for (const contact of contacts) { 21 | list.addContact(new Contact(contact)) 22 | } 23 | return list.getClosestContacts().map((n) => n.getPeerDescriptor()) 24 | } 25 | -------------------------------------------------------------------------------- /packages/dht/src/dht/routing/DuplicateDetector.ts: -------------------------------------------------------------------------------- 1 | export class DuplicateDetector { 2 | 3 | private values: Set = new Set() 4 | private queue: string[] = [] 5 | private maxItemCount: number 6 | 7 | constructor( 8 | maxItemCount: number, 9 | ) { 10 | this.maxItemCount = maxItemCount 11 | } 12 | 13 | public add(value: string): void { 14 | this.values.add(value) 15 | this.queue.push(value) 16 | if (this.queue.length > this.maxItemCount) { 17 | const removed = this.queue.shift()! 18 | this.values.delete(removed) 19 | } 20 | } 21 | 22 | public isMostLikelyDuplicate(value: string): boolean { 23 | return this.values.has(value) 24 | } 25 | 26 | public size(): number { 27 | return this.values.size 28 | } 29 | 30 | public clear(): void { 31 | this.values.clear() 32 | this.queue = [] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/dht/src/dht/routing/getPreviousPeer.ts: -------------------------------------------------------------------------------- 1 | import last from 'lodash/last' 2 | import { PeerDescriptor, RouteMessageWrapper } from '../../../generated/packages/dht/protos/DhtRpc' 3 | 4 | export const getPreviousPeer = (routeMessage: RouteMessageWrapper): PeerDescriptor | undefined => { 5 | return last(routeMessage.routingPath) 6 | } 7 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/AddressTools.ts: -------------------------------------------------------------------------------- 1 | import ipaddr from 'ipaddr.js' 2 | 3 | // IPv4 private address ranges as specified by RFC 1918 4 | // and private loopback addresses 5 | const IPv4PrivateRanges = [ 6 | '10.0.0.0/8', 7 | '172.16.0.0/12', 8 | '192.168.0.0/16', 9 | '127.0.0.0/8' 10 | ].map((a) => ipaddr.parseCIDR(a)) 11 | 12 | export function isPrivateIPv4(address: string): boolean { 13 | if (ipaddr.IPv4.isValid(address)) { 14 | const ip = ipaddr.IPv4.parse(address) 15 | for (const range of IPv4PrivateRanges) { 16 | if (ip.match(range)) { 17 | return true 18 | } 19 | } 20 | } 21 | 22 | return false 23 | } 24 | 25 | export function getAddressFromIceCandidate(candidate: string): string | undefined { 26 | const fields = candidate.split(' ').filter((field) => field.length > 0) 27 | return fields.length >= 5 && ipaddr.isValid(fields[4]) ? fields[4] : undefined 28 | } 29 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/browser/isBrowserEnvironment.ts: -------------------------------------------------------------------------------- 1 | export const isBrowserEnvironment = (): boolean => false 2 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/browser/isBrowserEnvironment_override.ts: -------------------------------------------------------------------------------- 1 | // webpack overwrites the isBrowserEnvironment.ts with this file when it creates the browser bundle 2 | 3 | export const isBrowserEnvironment = (): boolean => true 4 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/debugHelpers.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@streamr/utils' 2 | 3 | export const debugVars: Record = [] 4 | 5 | export function logInfoIf(logger: Logger, condition: boolean, msg: string): void { 6 | if (condition) { 7 | logger.info(msg) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/offering.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | import { DhtAddress } from '../identifiers' 3 | 4 | type Offerer = 'local' | 'remote' 5 | 6 | export const getOfferer = (localNodeId: DhtAddress, remoteNodeId: DhtAddress): Offerer => { 7 | return getOfferingHash(localNodeId + ',' + remoteNodeId) < getOfferingHash(remoteNodeId + ',' + localNodeId) 8 | ? 'local' 9 | : 'remote' 10 | } 11 | 12 | const getOfferingHash = (idPair: string): number => { 13 | const buffer = crypto.createHash('md5').update(idPair).digest() 14 | return buffer.readInt32LE(0) 15 | } 16 | -------------------------------------------------------------------------------- /packages/dht/src/helpers/protoToString.ts: -------------------------------------------------------------------------------- 1 | import { IMessageType } from '@protobuf-ts/runtime' 2 | 3 | import { protoClasses } from './protoClasses' 4 | import { protoClasses as rpcProtoClasses } from '@streamr/proto-rpc' 5 | 6 | const typeRegistry = protoClasses.concat(rpcProtoClasses) 7 | 8 | export function protoToString>(protoObj: T, 9 | objectType: ClassType): string { 10 | 11 | let ret = '' 12 | try { 13 | ret = objectType.toJsonString(protoObj, { 14 | typeRegistry 15 | }) 16 | } catch (_e) { 17 | ret = '[type not in type registry]' 18 | } 19 | 20 | return ret 21 | } 22 | -------------------------------------------------------------------------------- /packages/dht/src/rpc-protocol/DhtCallContext.ts: -------------------------------------------------------------------------------- 1 | import { ProtoCallContext } from '@streamr/proto-rpc' 2 | import { PeerDescriptor } from '../../generated/packages/dht/protos/DhtRpc' 3 | import { DhtRpcOptions } from './DhtRpcOptions' 4 | 5 | export class DhtCallContext extends ProtoCallContext implements DhtRpcOptions { 6 | // used by client 7 | targetDescriptor?: PeerDescriptor 8 | sourceDescriptor?: PeerDescriptor 9 | clientId?: number 10 | connect?: boolean 11 | sendIfStopped?: boolean 12 | doNotBufferWhileConnecting?: boolean 13 | //used in incoming calls 14 | incomingSourceDescriptor?: PeerDescriptor 15 | } 16 | -------------------------------------------------------------------------------- /packages/dht/src/rpc-protocol/DhtRpcOptions.ts: -------------------------------------------------------------------------------- 1 | import { ProtoRpcOptions } from '@streamr/proto-rpc' 2 | import { PeerDescriptor } from '../../generated/packages/dht/protos/DhtRpc' 3 | 4 | export interface DhtRpcOptions extends ProtoRpcOptions { 5 | targetDescriptor?: PeerDescriptor 6 | sourceDescriptor?: PeerDescriptor 7 | clientId?: number 8 | connect?: boolean 9 | sendIfStopped?: boolean 10 | doNotBufferWhileConnecting?: boolean 11 | } 12 | -------------------------------------------------------------------------------- /packages/dht/src/types/ServiceID.ts: -------------------------------------------------------------------------------- 1 | export type ServiceID = string 2 | -------------------------------------------------------------------------------- /packages/dht/src/types/textencoding.d.ts: -------------------------------------------------------------------------------- 1 | import { TextEncoder as _TextEncoder, TextDecoder as _TextDecoder } from 'node:util' 2 | 3 | declare global { 4 | const TextEncoder: typeof _TextEncoder 5 | const TextDecoder: typeof _TextDecoder 6 | } 7 | -------------------------------------------------------------------------------- /packages/dht/test/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest-extended' 2 | -------------------------------------------------------------------------------- /packages/dht/test/unit/ProtobufMessage.test.ts: -------------------------------------------------------------------------------- 1 | /* 2 | import { ProtobufMessage } from '../../src/protobufclasses/ProtobufMessage' 3 | import { Message, RpcMessage } from '../../generated/DhtRpc' 4 | import { v4 } from 'uuid' 5 | import { MessageType } from '@protobuf-ts/runtime' 6 | */ 7 | 8 | describe('ProtobufMessage', () => { 9 | 10 | it('can parse and print a Message', async () => { 11 | 12 | /* 13 | const message: Message = { 14 | messageId: v4(), 15 | messageType: 16 | } 17 | const data = Message.toBinary(message) 18 | ProtobufMessage.fromBinary(data) 19 | */ 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /packages/dht/test/unit/version.test.ts: -------------------------------------------------------------------------------- 1 | import { isMaybeSupportedProtocolVersion } from '../../src/helpers/version' 2 | 3 | describe('version', () => { 4 | 5 | it('supported', () => { 6 | expect(isMaybeSupportedProtocolVersion('1.0')).toBe(true) 7 | expect(isMaybeSupportedProtocolVersion('1.1')).toBe(true) 8 | expect(isMaybeSupportedProtocolVersion('2.0')).toBe(true) 9 | expect(isMaybeSupportedProtocolVersion('3.5')).toBe(true) 10 | }) 11 | 12 | it('not supported', () => { 13 | expect(isMaybeSupportedProtocolVersion('')).toBe(false) 14 | expect(isMaybeSupportedProtocolVersion('100.0.0-testnet-three.3')).toBe(false) 15 | expect(isMaybeSupportedProtocolVersion('0.0')).toBe(false) 16 | expect(isMaybeSupportedProtocolVersion('0.1')).toBe(false) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/dht/test/unit/webrtcReplaceInternalIpWithExternalIp.test.ts: -------------------------------------------------------------------------------- 1 | import { replaceInternalIpWithExternalIp } from '../../src/connection/webrtc/WebrtcConnector' 2 | 3 | describe('replaceIpIfCandidateTypeIsHost', () => { 4 | 5 | const hostCandidate = 'candidate:1 1 UDP 2013266431 127.0.0.1 30000 typ host' 6 | const srflxCandidate = 'candidate:1 1 UDP 2013266431 127.0.0.1 30000 typ srflx' 7 | 8 | it('replaces ip if candidate type is host', () => { 9 | const replaced = replaceInternalIpWithExternalIp(hostCandidate, '0.0.0.0') 10 | expect(replaced).toEqual('candidate:1 1 UDP 2013266431 0.0.0.0 30000 typ host') 11 | }) 12 | 13 | it('does not replace candidate if type is not host', () => { 14 | const replaced = replaceInternalIpWithExternalIp(srflxCandidate, '0.0.0.0') 15 | expect(replaced).toEqual(srflxCandidate) 16 | }) 17 | 18 | }) 19 | -------------------------------------------------------------------------------- /packages/dht/test/utils/mock/MockConnection.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'eventemitter3' 2 | import { ConnectionEvents, IConnection } from '../../../src/connection/IConnection' 3 | 4 | export class MockConnection extends EventEmitter implements IConnection { 5 | 6 | public sentData: Uint8Array[] = [] 7 | 8 | send(data: Uint8Array): Promise { 9 | this.sentData.push(data) 10 | return Promise.resolve() 11 | } 12 | 13 | close(graceful: boolean): Promise { 14 | this.emit('disconnected', graceful) 15 | return Promise.resolve() 16 | } 17 | 18 | destroy(): void { 19 | this.removeAllListeners() 20 | } 21 | 22 | emitData(message: Uint8Array): void { 23 | this.emit('data', message) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /packages/dht/test/utils/mock/MockConnectionsView.ts: -------------------------------------------------------------------------------- 1 | import { PeerDescriptor } from '../../../generated/packages/dht/protos/DhtRpc' 2 | 3 | export class MockConnectionsView { 4 | // eslint-disable-next-line class-methods-use-this 5 | getConnections(): PeerDescriptor[] { 6 | return [] 7 | } 8 | 9 | // eslint-disable-next-line class-methods-use-this 10 | getConnectionCount(): number { 11 | return 0 12 | } 13 | 14 | // eslint-disable-next-line class-methods-use-this 15 | hasConnection(): boolean { 16 | return false 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/dht/test/utils/mock/MockRpcCommunicator.ts: -------------------------------------------------------------------------------- 1 | import { RoutingRpcCommunicator } from '../../../src/transport/RoutingRpcCommunicator' 2 | 3 | export class MockRpcCommunicator extends RoutingRpcCommunicator { 4 | constructor() { 5 | super('mock-service', async () => {}) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/dht/test/utils/mock/MockTransport.ts: -------------------------------------------------------------------------------- 1 | import { ITransport, TransportEvents } from '../../../src/transport/ITransport' 2 | import { EventEmitter } from 'eventemitter3' 3 | import { PeerDescriptor } from '../../../generated/packages/dht/protos/DhtRpc' 4 | 5 | export class MockTransport extends EventEmitter implements ITransport { 6 | 7 | // eslint-disable-next-line class-methods-use-this 8 | async send(): Promise { 9 | 10 | } 11 | 12 | // eslint-disable-next-line class-methods-use-this 13 | getLocalPeerDescriptor(): PeerDescriptor { 14 | return PeerDescriptor.create() 15 | } 16 | 17 | // eslint-disable-next-line class-methods-use-this 18 | stop(): void { 19 | 20 | } 21 | 22 | // eslint-disable-next-line class-methods-use-this 23 | getDiagnosticInfo(): Record { 24 | return {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/dht/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*", 11 | "package.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/dht/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*", 10 | "test/**/*", 11 | "package.json", 12 | "scripts" 13 | ], 14 | "references": [ 15 | { "path": "../utils/tsconfig.node.json" }, 16 | { "path": "../test-utils/tsconfig.node.json" }, 17 | { "path": "../proto-rpc/tsconfig.node.json" }, 18 | { "path": "../autocertifier-client/tsconfig.node.json" }, 19 | { "path": "../cdn-location/tsconfig.node.json" }, 20 | { "path": "../geoip-location/tsconfig.node.json" } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/dht/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/dht/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*", 10 | "package.json" 11 | ], 12 | "references": [ 13 | { "path": "../utils/tsconfig.node.json" }, 14 | { "path": "../test-utils/tsconfig.node.json" }, 15 | { "path": "../proto-rpc/tsconfig.node.json" }, 16 | { "path": "../autocertifier-client/tsconfig.node.json" }, 17 | { "path": "../cdn-location/tsconfig.node.json" }, 18 | { "path": "../geoip-location/tsconfig.node.json" } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/geoip-location/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | .env 5 | -------------------------------------------------------------------------------- /packages/geoip-location/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/geoip-location/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { GeoIpLocator } from './GeoIpLocator' 2 | -------------------------------------------------------------------------------- /packages/geoip-location/test/helpers/fetchFileToMemory.ts: -------------------------------------------------------------------------------- 1 | export const fetchFileToMemory = async (url: string): Promise => { 2 | const response = await fetch(url) 3 | if (!response.ok) { 4 | throw new Error('HTTP error when downloading ' + url + ', status: ' + response.status) 5 | } 6 | return new Uint8Array(await response.arrayBuffer()) 7 | } 8 | -------------------------------------------------------------------------------- /packages/geoip-location/test/unit/GeoIpLocator-no-network-at-start.test.ts: -------------------------------------------------------------------------------- 1 | import { GeoIpLocator } from '../../src/GeoIpLocator' 2 | 3 | describe('GeoIpLocator', () => { 4 | let dirCounter = 0 5 | const dbPath = '/tmp' 6 | 7 | const getDbDir = () => { 8 | dirCounter++ 9 | return dbPath + '/geolite2-no-nw-start' + dirCounter 10 | } 11 | 12 | it('start throws if no network connectivity', async () => { 13 | const dbDir = getDbDir() 14 | 15 | const fetchMock = jest 16 | .spyOn(globalThis, 'fetch') 17 | .mockImplementation(async () => { 18 | throw new Error('API is down') 19 | }) 20 | 21 | const locator = new GeoIpLocator(dbDir) 22 | 23 | await expect(locator.start()).rejects.toThrow() 24 | 25 | fetchMock.mockRestore() 26 | 27 | locator.stop() 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/geoip-location/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/geoip-location/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "test/**/*" 6 | ], 7 | "references": [ 8 | { "path": "../utils/tsconfig.node.json" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/geoip-location/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/geoip-location/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "references": [ 11 | { "path": "../utils/tsconfig.node.json" }, 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/node/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test 4 | .idea/ 5 | *.iml 6 | .* 7 | *.png 8 | *.md 9 | *.xml 10 | LICENSE 11 | jest.config.js 12 | -------------------------------------------------------------------------------- /packages/node/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Dependency directory 25 | # Commenting this out is preferred by some people, see 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Users Environment Variables 30 | .idea/workspace.xml 31 | .idea/usage.statistics.xml 32 | .idea/tasks.xml 33 | .idea/dictionaries/ 34 | .lock-wscript 35 | .vscode/launch.json -------------------------------------------------------------------------------- /packages/node/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Datasource local storage ignored files 2 | /dataSources/ 3 | /dataSources.local.xml -------------------------------------------------------------------------------- /packages/node/.idea/broker.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/node/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /packages/node/.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | cassandra 6 | true 7 | com.dbschema.CassandraJdbcDriver 8 | jdbc:cassandra://localhost:9042/ 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/node/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/node/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /packages/node/.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/node/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/node/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Broker__8690__8691_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Broker__8790__8791_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Broker__8890_8891_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Tracker__30301_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Tracker__30302_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/Run_Tracker__30303_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/integration_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/node/.idea/runConfigurations/unit_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/node/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/node/bin/streamr-node-init.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander' 3 | import pkg from '../package.json' 4 | import { start } from '../src/config/ConfigWizard' 5 | 6 | program 7 | .version(pkg.version) 8 | .name('streamr-node-init') 9 | .description('Run the configuration wizard for the Streamr node.') 10 | 11 | ;(async () => { 12 | try { 13 | await start() 14 | } catch (e) { 15 | console.error('Streamr Node Config Wizard encountered an error:\n', e) 16 | } 17 | })() 18 | -------------------------------------------------------------------------------- /packages/node/configs/development-prod-resend.env.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.streamr.network/config-v3.schema.json", 3 | "client": { 4 | "auth": { 5 | "privateKey": "0x87ba653d4320abb5231d10a4abfad9ea1371dcbcdc247ab2f961e2754ae781ca" 6 | }, 7 | "network": { 8 | "controlLayer": { 9 | "websocketServerEnableTls": false 10 | } 11 | }, 12 | "metrics": false 13 | }, 14 | "httpServer": { 15 | "port": 8791 16 | }, 17 | "plugins": {} 18 | } 19 | -------------------------------------------------------------------------------- /packages/node/data-api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/node/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | import defaultConfig from '../../jest.config' 3 | 4 | const config: Config.InitialOptions = { 5 | ...defaultConfig, 6 | testTimeout: 10000, 7 | } 8 | 9 | export default config 10 | -------------------------------------------------------------------------------- /packages/node/src/apiAuthentication.ts: -------------------------------------------------------------------------------- 1 | export interface ApiAuthentication { 2 | keys: string[] 3 | } 4 | 5 | export const isValidAuthentication = (apiKey?: string, apiAuthentication?: ApiAuthentication): boolean => { 6 | if (apiAuthentication !== undefined) { 7 | if (apiKey === undefined) { 8 | return false 9 | } 10 | return apiAuthentication.keys.includes(apiKey) 11 | } else { 12 | return true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/node/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { createBroker, type Broker } from './broker' 2 | export type { Config } from './config/config' 3 | export type { StreamrClientConfig } from '@streamr/sdk' 4 | -------------------------------------------------------------------------------- /packages/node/src/helpers/generateMnemonicFromAddress.ts: -------------------------------------------------------------------------------- 1 | import { Mnemonic } from 'ethers' 2 | import { EthereumAddress } from '@streamr/utils' 3 | 4 | /** 5 | * @param address - valid eth address with leading 0x 6 | */ 7 | export const generateMnemonicFromAddress = (address: EthereumAddress): string => { 8 | return Mnemonic.entropyToPhrase(address) 9 | .split(' ') 10 | .slice(0, 3) 11 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) 12 | .join(' ') 13 | } 14 | -------------------------------------------------------------------------------- /packages/node/src/plugins/consoleMetrics/config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "config.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "description": "Console metrics plugin configuration", 6 | "required": [ 7 | "interval" 8 | ], 9 | "additionalProperties": false, 10 | "properties": { 11 | "interval": { 12 | "type": "integer", 13 | "description": "Interval (in seconds) in which to collect and report metrics", 14 | "minimum": 0, 15 | "default": 60 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/node/src/plugins/http/HttpPlugin.ts: -------------------------------------------------------------------------------- 1 | import { createEndpoint } from './publishEndpoint' 2 | import { ApiPluginConfig, Plugin } from '../../Plugin' 3 | import PLUGIN_CONFIG_SCHEMA from './config.schema.json' 4 | import { Schema } from 'ajv' 5 | import { StreamrClient } from '@streamr/sdk' 6 | 7 | export class HttpPlugin extends Plugin { 8 | 9 | async start(streamrClient: StreamrClient): Promise { 10 | this.addHttpServerEndpoint(createEndpoint(streamrClient)) 11 | } 12 | 13 | // eslint-disable-next-line class-methods-use-this 14 | async stop(): Promise { 15 | } 16 | 17 | // eslint-disable-next-line class-methods-use-this 18 | override getConfigSchema(): Schema { 19 | return PLUGIN_CONFIG_SCHEMA 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/node/src/plugins/http/config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "config.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "description": "HTTP plugin configuration", 6 | "additionalProperties": false, 7 | "properties": { 8 | "apiAuthentication": { 9 | "$ref": "definitions.schema.json#/definitions/apiAuthenticationOverride" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/node/src/plugins/info/config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "config.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "description": "Info plugin configuration", 6 | "additionalProperties": false, 7 | "properties": { 8 | "apiAuthentication": { 9 | "$ref": "definitions.schema.json#/definitions/apiAuthenticationOverride" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/node/src/plugins/operator/createIsLeaderFn.ts: -------------------------------------------------------------------------------- 1 | import { StreamrClient } from '@streamr/sdk' 2 | import { OperatorFleetState } from './OperatorFleetState' 3 | import { Logger } from '@streamr/utils' 4 | 5 | export async function createIsLeaderFn( 6 | streamrClient: StreamrClient, 7 | operatorFleetState: OperatorFleetState, 8 | logger?: Logger 9 | ): Promise<() => boolean> { 10 | const myNodeId = await streamrClient.getNodeId() 11 | return () => { 12 | const leaderNodeId = operatorFleetState.getLeaderNodeId() 13 | const isLeader = myNodeId === leaderNodeId 14 | logger?.debug('Check whether leader', { isLeader, leaderNodeId, myNodeId }) 15 | return isLeader 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/node/src/plugins/operator/formCoordinationStreamId.ts: -------------------------------------------------------------------------------- 1 | import { EthereumAddress, StreamID, toStreamID } from '@streamr/utils' 2 | 3 | export function formCoordinationStreamId(operatorContractAddress: EthereumAddress): StreamID { 4 | return toStreamID('/operator/coordination', operatorContractAddress) 5 | } 6 | -------------------------------------------------------------------------------- /packages/node/src/plugins/subscriber/config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "config.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "type": "object", 5 | "description": "Subscriber plugin configuration", 6 | "additionalProperties": false, 7 | "properties": { 8 | "streams": { 9 | "type": "array", 10 | "items": { 11 | "type": "object", 12 | "properties": { 13 | "streamId": { 14 | "type": "string" 15 | }, 16 | "streamPartition": { 17 | "type": "number" 18 | }, 19 | "additionalProperties": false 20 | } 21 | }, 22 | "uniqueItems": true, 23 | "default": [] 24 | }, 25 | "subscriptionRetryInterval": { 26 | "type": "number", 27 | "description": "DEPRECATED", 28 | "default": 30000 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/node/src/types/consistent-hash/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'consistent-hash' { 2 | export default class ConsistentHash { 3 | constructor(options?: { 4 | range?: number 5 | weight?: number 6 | distribution?: 'uniform' | string 7 | }) 8 | 9 | add(node: string): ConsistentHash 10 | 11 | get(name: string, count: number): string[] | null 12 | 13 | remove(node: string): ConsistentHash 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/node/src/types/nat-type-identifier/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'nat-type-identifier' { 2 | export default function getNatType(opts: { 3 | logsEnabled?: boolean 4 | sampleCount?: number 5 | stunHost?: string 6 | }): Promise 7 | } 8 | -------------------------------------------------------------------------------- /packages/node/test/integration/plugins/storage/MockStorageConfig.ts: -------------------------------------------------------------------------------- 1 | import { StreamPartID } from '@streamr/utils' 2 | 3 | export const createMockStorageConfig = (streamParts: StreamPartID[]): any => { 4 | return { 5 | hasStreamPart: (streamPart: StreamPartID) => { 6 | return streamParts.includes(streamPart) 7 | }, 8 | getStreamParts: () => { 9 | return streamParts 10 | }, 11 | addChangeListener: () => {}, 12 | startChainEventsListener: jest.fn(), 13 | stopChainEventsListener: jest.fn(), 14 | startAssignmentEventListener: jest.fn(), 15 | stopAssignmentEventListener: jest.fn(), 16 | cleanup: jest.fn().mockResolvedValue(undefined) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/node/test/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest-extended' 2 | -------------------------------------------------------------------------------- /packages/node/test/unit/plugins/operator/formCoordinationStreamId.test.ts: -------------------------------------------------------------------------------- 1 | import { formCoordinationStreamId } from '../../../../src/plugins/operator/formCoordinationStreamId' 2 | import { toEthereumAddress } from '@streamr/utils' 3 | 4 | describe(formCoordinationStreamId, () => { 5 | it('forms coordination stream id', () => { 6 | const address = toEthereumAddress('0x1234567890123456789012345678901234567890') 7 | expect(formCoordinationStreamId(address)) 8 | .toEqual('0x1234567890123456789012345678901234567890/operator/coordination') 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /packages/node/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "src/**/*.json", 6 | "bin/*.ts", 7 | "package.json", 8 | "test/**/*" 9 | ], 10 | "references": [ 11 | { "path": "../utils/tsconfig.node.json" }, 12 | { "path": "../test-utils/tsconfig.node.json" }, 13 | { "path": "../sdk/tsconfig.node.json" } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/node/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "src/**/*.json", 9 | "bin/*.ts", 10 | "package.json" 11 | ], 12 | "references": [ 13 | { "path": "../utils/tsconfig.node.json" }, 14 | { "path": "../test-utils/tsconfig.node.json" }, 15 | { "path": "../sdk/tsconfig.node.json" } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/proto-rpc/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | lerna-debug.log 4 | *.tsbuildinfo 5 | *.heapprofile 6 | *.heapsnapshot 7 | oldsrc 8 | dist 9 | coverage 10 | generated 11 | test/simulation/data/nodeids.json 12 | test/simulation/data/orderedneighbors.json 13 | test/proto/ -------------------------------------------------------------------------------- /packages/proto-rpc/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs' 2 | 3 | export default [ 4 | { 5 | ignores: [ 6 | 'test/proto/**', 7 | 'examples/**' 8 | ] 9 | }, 10 | ...baseConfig 11 | ] 12 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/errors/ErrorRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | service ErrorRpcService { 5 | rpc timeout (HelloRequest) returns (HelloResponse); 6 | rpc serverError (HelloRequest) returns (HelloResponse); 7 | rpc unknownMethod (HelloRequest) returns (HelloResponse); 8 | } 9 | 10 | message HelloRequest { 11 | string myName = 1; 12 | } 13 | 14 | message HelloResponse { 15 | string greeting = 1; 16 | } -------------------------------------------------------------------------------- /packages/proto-rpc/examples/errors/compile-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p ./proto 4 | npx protoc --ts_out $(pwd)/proto --ts_opt server_generic,generate_dependencies --proto_path $(pwd) ErrorRpc.proto 5 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/errors/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npx ts-node examples/errors/errors.ts -------------------------------------------------------------------------------- /packages/proto-rpc/examples/hello/HelloRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | service HelloRpcService { 5 | rpc sayHello (HelloRequest) returns (HelloResponse); 6 | } 7 | 8 | message HelloRequest { 9 | string myName = 1; 10 | } 11 | 12 | message HelloResponse { 13 | string greeting = 1; 14 | } -------------------------------------------------------------------------------- /packages/proto-rpc/examples/hello/compile-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p ./proto 4 | npx protoc --ts_out $(pwd)/proto --ts_opt server_generic,generate_dependencies --proto_path $(pwd) HelloRpc.proto 5 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/hello/proto/HelloRpc.server.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.8.0 with parameter server_generic,generate_dependencies 2 | // @generated from protobuf file "HelloRpc.proto" (syntax proto3) 3 | // tslint:disable 4 | import { HelloResponse } from "./HelloRpc"; 5 | import { HelloRequest } from "./HelloRpc"; 6 | import { ServerCallContext } from "@protobuf-ts/runtime-rpc"; 7 | /** 8 | * @generated from protobuf service HelloRpcService 9 | */ 10 | export interface IHelloRpcService { 11 | /** 12 | * @generated from protobuf rpc: sayHello(HelloRequest) returns (HelloResponse); 13 | */ 14 | sayHello(request: HelloRequest, context: T): Promise; 15 | } 16 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/hello/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npx ts-node examples/hello/hello.ts -------------------------------------------------------------------------------- /packages/proto-rpc/examples/routed-hello/RoutedHelloRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | service RoutedHelloRpcService { 5 | rpc sayHello (RoutedHelloRequest) returns (RoutedHelloResponse); 6 | } 7 | 8 | message RoutedHelloRequest { 9 | string myName = 1; 10 | } 11 | 12 | message RoutedHelloResponse { 13 | string greeting = 1; 14 | } -------------------------------------------------------------------------------- /packages/proto-rpc/examples/routed-hello/compile-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p ./proto 4 | npx protoc --ts_out $(pwd)/proto --ts_opt server_generic,generate_dependencies --proto_path $(pwd) RoutedHelloRpc.proto 5 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/routed-hello/proto/RoutedHelloRpc.server.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.8.0 with parameter server_generic,generate_dependencies 2 | // @generated from protobuf file "RoutedHelloRpc.proto" (syntax proto3) 3 | // tslint:disable 4 | import { RoutedHelloResponse } from "./RoutedHelloRpc"; 5 | import { RoutedHelloRequest } from "./RoutedHelloRpc"; 6 | import { ServerCallContext } from "@protobuf-ts/runtime-rpc"; 7 | /** 8 | * @generated from protobuf service RoutedHelloRpcService 9 | */ 10 | export interface IRoutedHelloRpcService { 11 | /** 12 | * @generated from protobuf rpc: sayHello(RoutedHelloRequest) returns (RoutedHelloResponse); 13 | */ 14 | sayHello(request: RoutedHelloRequest, context: T): Promise; 15 | } 16 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/routed-hello/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npx ts-node examples/routed-hello/routedhello.ts -------------------------------------------------------------------------------- /packages/proto-rpc/examples/wakeup/WakeUpRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | import "google/protobuf/empty.proto"; 5 | 6 | service WakeUpRpcService { 7 | rpc wakeUp (WakeUpRequest) returns (google.protobuf.Empty); 8 | } 9 | 10 | message WakeUpRequest { 11 | string reason = 1; 12 | } 13 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/wakeup/compile-protobuf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p ./proto 4 | npx protoc -I $(pwd)/../../protos --ts_out $(pwd)/proto --ts_opt server_generic,generate_dependencies --proto_path $(pwd) WakeUpRpc.proto 5 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/wakeup/proto/WakeUpRpc.server.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.8.0 with parameter server_generic,generate_dependencies 2 | // @generated from protobuf file "WakeUpRpc.proto" (syntax proto3) 3 | // tslint:disable 4 | import { Empty } from "./google/protobuf/empty"; 5 | import { WakeUpRequest } from "./WakeUpRpc"; 6 | import { ServerCallContext } from "@protobuf-ts/runtime-rpc"; 7 | /** 8 | * @generated from protobuf service WakeUpRpcService 9 | */ 10 | export interface IWakeUpRpcService { 11 | /** 12 | * @generated from protobuf rpc: wakeUp(WakeUpRequest) returns (google.protobuf.Empty); 13 | */ 14 | wakeUp(request: WakeUpRequest, context: T): Promise; 15 | } 16 | -------------------------------------------------------------------------------- /packages/proto-rpc/examples/wakeup/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npx ts-node examples/wakeup/wakeup.ts -------------------------------------------------------------------------------- /packages/proto-rpc/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/proto-rpc/karma.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-require-imports 2 | const { createKarmaConfig, createWebpackConfig } = require('@streamr/browser-test-runner') 3 | 4 | const TEST_PATHS = ['test/**/*.ts'] 5 | 6 | module.exports = createKarmaConfig(TEST_PATHS, createWebpackConfig({ 7 | entry: './src/exports.ts', 8 | libraryName: 'proto-rpc', 9 | fallback: { 10 | module: false 11 | } 12 | })) 13 | -------------------------------------------------------------------------------- /packages/proto-rpc/proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./generated 2 | npx protoc --ts_out ./generated --ts_opt server_generic,generate_dependencies --proto_path protos protos/ProtoRpc.proto --experimental_allow_proto3_optional 3 | -------------------------------------------------------------------------------- /packages/proto-rpc/protos/ProtoRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | package protorpc; 5 | 6 | import "google/protobuf/empty.proto"; 7 | import "google/protobuf/any.proto"; 8 | 9 | enum RpcErrorType { 10 | SERVER_TIMEOUT = 0; 11 | CLIENT_TIMEOUT = 1; 12 | UNKNOWN_RPC_METHOD = 2; 13 | CLIENT_ERROR = 3; 14 | SERVER_ERROR = 4; 15 | } 16 | 17 | message RpcMessage { 18 | map header = 1; 19 | google.protobuf.Any body = 2; 20 | string requestId = 3; 21 | optional RpcErrorType errorType = 4; 22 | optional string errorClassName = 5; 23 | optional string errorCode = 6; 24 | optional string errorMessage = 7; 25 | } 26 | 27 | // Dummy message to force the generation of the typescript class "google.prototype.Empty" 28 | // We need this generated class in RpcCommunicator 29 | 30 | message Mnfo2uhnf92hvqi2nviouq2hv9puhq { 31 | google.protobuf.Empty empty = 1; 32 | } -------------------------------------------------------------------------------- /packages/proto-rpc/src/common.ts: -------------------------------------------------------------------------------- 1 | import * as Err from './errors' 2 | 3 | // TODO: Replace with streamr-utils library import 4 | export function promiseTimeout(ms: number, givenPromise: Promise): Promise { 5 | const timeoutPromise = new Promise((_resolve, reject) => { 6 | const timeoutRef = setTimeout(() => { 7 | reject(new Err.RpcTimeout('Timed out in ' + ms + 'ms.')) 8 | }, ms) 9 | 10 | // Clear timeout if promise wins race 11 | givenPromise 12 | .finally(() => clearTimeout(timeoutRef)) 13 | .catch(() => null) 14 | }) 15 | 16 | return Promise.race([ 17 | givenPromise, 18 | timeoutPromise 19 | ]) as Promise 20 | } 21 | -------------------------------------------------------------------------------- /packages/proto-rpc/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { RpcCommunicator, type RpcCommunicatorOptions, StatusCode } from './RpcCommunicator' 2 | export { type ProtoRpcOptions, ProtoCallContext } from './ProtoCallContext' 3 | export { toProtoRpcClient, type ProtoRpcClient, type ClassType } from './toProtoRpcClient' 4 | export { ClientTransport } from './ClientTransport' 5 | export type { MethodOptions } from './ServerRegistry' 6 | export * as RpcError from './errors' 7 | export { protoClasses } from './protoClasses' 8 | export { RpcMessage } from '../generated/ProtoRpc' 9 | -------------------------------------------------------------------------------- /packages/proto-rpc/src/protoClasses.ts: -------------------------------------------------------------------------------- 1 | import { IMessageType } from '@protobuf-ts/runtime' 2 | import { RpcMessage } from '../generated/ProtoRpc' 3 | 4 | export const protoClasses: IMessageType[] = [ RpcMessage ] 5 | -------------------------------------------------------------------------------- /packages/proto-rpc/src/types/textencoding.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var */ 2 | import { TextEncoder as _TextEncoder, TextDecoder as _TextDecoder } from 'node:util' 3 | 4 | declare global { 5 | var TextEncoder: typeof _TextEncoder 6 | var TextDecoder: typeof _TextDecoder 7 | } 8 | -------------------------------------------------------------------------------- /packages/proto-rpc/test-proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./test/proto 2 | npx protoc --ts_out ./test/proto --ts_opt server_generic,generate_dependencies --proto_path test/protos test/protos/*.proto --experimental_allow_proto3_optional 3 | -------------------------------------------------------------------------------- /packages/proto-rpc/test/protos/HelloRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | service HelloRpcService { 5 | rpc sayHello (HelloRequest) returns (HelloResponse); 6 | } 7 | 8 | message HelloRequest { 9 | string myName = 1; 10 | } 11 | 12 | message HelloResponse { 13 | string greeting = 1; 14 | } -------------------------------------------------------------------------------- /packages/proto-rpc/test/protos/WakeUpRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option optimize_for = CODE_SIZE; 3 | 4 | import "google/protobuf/empty.proto"; 5 | 6 | service WakeUpRpcService { 7 | rpc wakeUp (WakeUpRequest) returns (google.protobuf.Empty); 8 | } 9 | 10 | message WakeUpRequest { 11 | string reason = 1; 12 | } 13 | -------------------------------------------------------------------------------- /packages/proto-rpc/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/proto-rpc/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*", 10 | "test/**/*", 11 | ], 12 | "references": [ 13 | { "path": "../utils/tsconfig.node.json" }, 14 | { "path": "../test-utils/tsconfig.node.json" } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/proto-rpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/proto-rpc/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*" 10 | ], 11 | "references": [ 12 | { "path": "../utils/tsconfig.node.json" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/sdk/.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | -------------------------------------------------------------------------------- /packages/sdk/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /packages/sdk/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /packages/sdk/.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/sdk/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /packages/sdk/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/sdk/.idea/streamr-client-javascript.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/sdk/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/sdk/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import baseConfig from '../../eslint.config.mjs' 2 | 3 | export default [ 4 | { 5 | ignores: [ 6 | 'src/ethereumArtifacts/**', 7 | 'test/exports/**', 8 | 'test/benchmarks/**', 9 | 'test/memory/*' 10 | ] 11 | }, 12 | ...baseConfig 13 | ] 14 | -------------------------------------------------------------------------------- /packages/sdk/fix-esm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p dist/src/ 3 | cp -f src/exports-esm.mjs dist/src/exports-esm.mjs 4 | -------------------------------------------------------------------------------- /packages/sdk/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | import defaultConfig from '../../jest.config' 3 | 4 | const config: Config.InitialOptions = { 5 | ...defaultConfig, 6 | globalSetup: './jest.setup.ts', 7 | setupFilesAfterEnv: [ 8 | ...defaultConfig.setupFilesAfterEnv, 9 | './test/test-utils/customMatchers.ts', 10 | '@streamr/test-utils/setupCustomMatchers', 11 | ], 12 | } 13 | 14 | export default config 15 | -------------------------------------------------------------------------------- /packages/sdk/jest.setup.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | import { GitRevisionPlugin } from 'git-revision-webpack-plugin' 3 | import 'reflect-metadata' 4 | import pkg from './package.json' 5 | 6 | export default async function setup(): Promise { 7 | if (process.env.GIT_VERSION) { 8 | return 9 | } 10 | 11 | const gitRevisionPlugin = new GitRevisionPlugin() 12 | 13 | const [GIT_VERSION, GIT_COMMITHASH, GIT_BRANCH] = await Promise.all([ 14 | gitRevisionPlugin.version(), 15 | gitRevisionPlugin.commithash(), 16 | gitRevisionPlugin.branch(), 17 | ]) 18 | 19 | Object.assign(process.env, { 20 | version: pkg.version, 21 | GIT_VERSION, 22 | GIT_COMMITHASH, 23 | GIT_BRANCH, 24 | }, process.env) // don't override whatever is in process.env 25 | } 26 | -------------------------------------------------------------------------------- /packages/sdk/karma-end-to-end.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const webpackConfig = require('./webpack-karma.config') 3 | const { createKarmaConfig } = require('@streamr/browser-test-runner') 4 | 5 | module.exports = createKarmaConfig(['test/end-to-end/**/*.ts'], webpackConfig, __dirname) 6 | -------------------------------------------------------------------------------- /packages/sdk/karma-integration.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const webpackConfig = require('./webpack-karma.config') 3 | const { createKarmaConfig } = require('@streamr/browser-test-runner') 4 | 5 | module.exports = createKarmaConfig(['test/integration/**/*.ts'], webpackConfig, __dirname) 6 | -------------------------------------------------------------------------------- /packages/sdk/karma-setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This setup script is executed indirectly by Karma via the `browser-test-runner` 3 | * package. See karma-config.js in that package for more details. 4 | */ 5 | 6 | // eslint-disable-next-line @typescript-eslint/no-require-imports 7 | require('./test/test-utils/customMatchers') 8 | 9 | const { customMatchers } = require('@streamr/test-utils') 10 | expect.extend(customMatchers) -------------------------------------------------------------------------------- /packages/sdk/karma-unit.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const webpackConfig = require('./webpack-karma.config') 3 | const { createKarmaConfig } = require('@streamr/browser-test-runner') 4 | 5 | module.exports = createKarmaConfig(['test/unit/**/*.ts'], webpackConfig, __dirname) 6 | -------------------------------------------------------------------------------- /packages/sdk/proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./src/generated 2 | npx protoc --ts_out ./src/generated --ts_opt server_generic,generate_dependencies,long_type_number --proto_path ../.. packages/sdk/protos/SdkRpc.proto 3 | -------------------------------------------------------------------------------- /packages/sdk/protos/SdkRpc.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option optimize_for = CODE_SIZE; 4 | 5 | import "packages/dht/protos/DhtRpc.proto"; 6 | 7 | service OperatorDiscovery { 8 | rpc discoverOperators (OperatorDiscoveryRequest) returns (OperatorDiscoveryResponse); 9 | } 10 | 11 | message OperatorDiscoveryRequest { 12 | string streamPartId = 1; 13 | } 14 | 15 | message OperatorDiscoveryResponse { 16 | repeated dht.PeerDescriptor operators = 1; 17 | } 18 | -------------------------------------------------------------------------------- /packages/sdk/readme-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamr-dev/network/3da65fba3a8feff141ab288197e4d3905efbbc7b/packages/sdk/readme-header.png -------------------------------------------------------------------------------- /packages/sdk/scripts/postbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p dist 4 | 5 | SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 6 | 7 | cd "${SCRIPT_DIR}/.." 8 | 9 | # Copy over the migrations 10 | mkdir -p dist/src/encryption/migrations 11 | cp -f src/encryption/migrations/* dist/src/encryption/migrations 12 | -------------------------------------------------------------------------------- /packages/sdk/src/StreamMetadata.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_PARTITION_COUNT, ensureValidStreamPartitionCount } from '@streamr/utils' 2 | import { StreamrClientError } from './StreamrClientError' 3 | 4 | export type StreamMetadata = Record 5 | 6 | export const parseMetadata = (metadata: string): StreamMetadata => { 7 | try { 8 | return JSON.parse(metadata) 9 | } catch (_ignored) { 10 | return {} 11 | } 12 | } 13 | 14 | export const getPartitionCount = (metadata: StreamMetadata): number => { 15 | const metadataValue = metadata.partitions as number | undefined 16 | if (metadataValue !== undefined) { 17 | try { 18 | ensureValidStreamPartitionCount(metadataValue) 19 | } catch { 20 | throw new StreamrClientError(`Invalid partition count: ${metadataValue}`, 'INVALID_STREAM_METADATA') 21 | } 22 | } 23 | return metadataValue ?? DEFAULT_PARTITION_COUNT 24 | } 25 | -------------------------------------------------------------------------------- /packages/sdk/src/encryption/KeyExchangeKeyPair.ts: -------------------------------------------------------------------------------- 1 | import { AsymmetricEncryptionType } from '@streamr/trackerless-network' 2 | 3 | export interface KeyExchangeKeyPair { 4 | getPublicKey(): Uint8Array 5 | getPrivateKey(): Uint8Array 6 | getEncryptionType(): AsymmetricEncryptionType 7 | } 8 | -------------------------------------------------------------------------------- /packages/sdk/src/encryption/migrations/001-initial.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- Up 3 | -------------------------------------------------------------------------------- 4 | 5 | CREATE TABLE IF NOT EXISTS GroupKeys ( 6 | id TEXT, 7 | groupKey TEXT, 8 | streamId TEXT 9 | ); 10 | 11 | CREATE UNIQUE INDEX IF NOT EXISTS name ON GroupKeys (id); 12 | 13 | -------------------------------------------------------------------------------- 14 | -- Down 15 | -------------------------------------------------------------------------------- 16 | 17 | DROP INDEX IF EXISTS name; 18 | DROP TABLE IF EXISTS GroupKeys; 19 | -------------------------------------------------------------------------------- /packages/sdk/src/encryption/migrations/003-encryption-keys.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- Up 3 | -------------------------------------------------------------------------------- 4 | 5 | CREATE TABLE IF NOT EXISTS EncryptionKeys ( 6 | key_ TEXT NOT NULL PRIMARY KEY, 7 | value_ TEXT 8 | ); 9 | 10 | INSERT INTO EncryptionKeys (key_, value_) SELECT "LEGACY::" || id, groupKey FROM GroupKeys; 11 | 12 | -------------------------------------------------------------------------------- 13 | -- Down 14 | -------------------------------------------------------------------------------- 15 | 16 | DROP TABLE EncryptionKeys; 17 | 18 | -------------------------------------------------------------------------------- /packages/sdk/src/encryption/migrations/004-latest-encryption-key-ids.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- Up 3 | -------------------------------------------------------------------------------- 4 | 5 | CREATE TABLE IF NOT EXISTS LatestEncryptionKeyIds ( 6 | key_ TEXT NOT NULL PRIMARY KEY, 7 | value_ TEXT 8 | ); 9 | 10 | -------------------------------------------------------------------------------- 11 | -- Down 12 | -------------------------------------------------------------------------------- 13 | 14 | DROP TABLE LatestEncryptionKeyIds; 15 | 16 | -------------------------------------------------------------------------------- /packages/sdk/src/ethereumArtifacts/IERC1271Abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | "function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4 magicValue)" 3 | ] -------------------------------------------------------------------------------- /packages/sdk/src/exports-browser.ts: -------------------------------------------------------------------------------- 1 | import * as StreamrClientExports from './exports' 2 | 3 | Object.assign(StreamrClientExports.StreamrClient, StreamrClientExports) 4 | 5 | export default StreamrClientExports.StreamrClient 6 | -------------------------------------------------------------------------------- /packages/sdk/src/exports-commonjs.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | // CJS entrypoint. 3 | const StreamrClientExports = require('./exports') 4 | 5 | Object.assign(StreamrClientExports.StreamrClient, StreamrClientExports) 6 | 7 | // required to get require('@streamr/sdk') instead of require('@streamr/sdk').default 8 | module.exports = StreamrClientExports.StreamrClient 9 | -------------------------------------------------------------------------------- /packages/sdk/src/exports-esm.mjs: -------------------------------------------------------------------------------- 1 | // ESM EntryPoint 2 | export * from './index.js' 3 | // required to get import StreamrClient from '@streamr/sdk' to work 4 | export { StreamrClient as default } from './index.js' 5 | // note this file is manually copied as-is into dist/src since we don't want tsc to compile it to commonjs 6 | -------------------------------------------------------------------------------- /packages/sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Streamr JavaScript Client 3 | * 4 | * @packageDocumentation 5 | * @module StreamrClient 6 | */ 7 | 8 | import { StreamrClient } from './StreamrClient' 9 | 10 | export * from './exports' 11 | export default StreamrClient 12 | // Note awful export wrappers in exports-commonjs.js & exports-esm.mjs 13 | -------------------------------------------------------------------------------- /packages/sdk/src/protocol/ValidationError.ts: -------------------------------------------------------------------------------- 1 | export class ValidationError extends Error { 2 | 3 | public code?: string 4 | 5 | constructor(msg: string, code?: string) { 6 | super(msg) 7 | this.code = code 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/sdk/src/protocol/oldStreamMessageBinaryUtils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StreamMessage as NewStreamMessage 3 | } from '@streamr/trackerless-network' 4 | import { StreamMessage as OldStreamMessage, } from './StreamMessage' 5 | import { StreamMessageTranslator } from './StreamMessageTranslator' 6 | 7 | export function convertStreamMessageToBytes(oldStreamMessage: OldStreamMessage): Uint8Array { 8 | return NewStreamMessage.toBinary(StreamMessageTranslator.toProtobuf(oldStreamMessage)) 9 | } 10 | 11 | export function convertBytesToStreamMessage(bytes: Uint8Array): OldStreamMessage { 12 | return StreamMessageTranslator.toClientProtocol(NewStreamMessage.fromBinary(bytes)) 13 | } 14 | -------------------------------------------------------------------------------- /packages/sdk/src/protocol/validations.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from './ValidationError' 2 | 3 | export function validateIsDefined(varName: string, varValue: unknown): void | never { 4 | if (varValue === undefined) { 5 | throw new ValidationError(`Expected ${varName} to not be undefined.`) 6 | } 7 | } 8 | 9 | export function validateIsNotNegativeInteger(varName: string, varValue?: number, allowUndefined = false): void | never { 10 | if (allowUndefined && varValue === undefined) { 11 | return 12 | } 13 | validateIsDefined(varName, varValue) 14 | if (!Number.isInteger(varValue)) { 15 | throw new ValidationError(`Expected ${varName} to be an integer but was a ${typeof varValue} (${varValue}).`) 16 | } 17 | if (varValue! < 0) { 18 | throw new ValidationError(`Expected ${varName} to not be negative (${varValue}).`) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/sdk/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility Types 3 | */ 4 | 5 | import { F } from 'ts-toolbelt' 6 | 7 | export type MaybeAsync = T | F.Promisify // Utility Type: make a function maybe async 8 | 9 | export type StreamDefinition = string 10 | | { id: string, partition?: number } 11 | | { stream: string, partition?: number } 12 | | { streamId: string, partition?: number } 13 | 14 | -------------------------------------------------------------------------------- /packages/sdk/src/utils/LoggerFactory.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@streamr/utils' 2 | import { inject, Lifecycle, scoped } from 'tsyringe' 3 | import { ConfigInjectionToken, StrictStreamrClientConfig } from '../Config' 4 | 5 | @scoped(Lifecycle.ContainerScoped) 6 | export class LoggerFactory { 7 | 8 | private readonly config: Pick 9 | 10 | constructor( 11 | @inject(ConfigInjectionToken) config: Pick 12 | ) { 13 | this.config = config 14 | } 15 | 16 | createLogger(module: NodeJS.Module): Logger { 17 | return new Logger(module, { id: this.config.id }, this.config.logLevel) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/sdk/src/utils/persistence/Persistence.ts: -------------------------------------------------------------------------------- 1 | export interface Persistence { 2 | get(key: string): Promise 3 | set(key: string, value: string): Promise 4 | } 5 | -------------------------------------------------------------------------------- /packages/sdk/src/utils/persistence/PersistenceContext.ts: -------------------------------------------------------------------------------- 1 | import { UserID } from '@streamr/utils' 2 | 3 | // TODO: make into abstract base class and define abstract constructor to enforce options? 4 | export interface PersistenceContext { 5 | get(key: string, namespace: string): Promise 6 | set(key: string, value: string, namespace: string): Promise 7 | close(): Promise 8 | } 9 | 10 | export interface PersistenceContextOptions { 11 | ownerId: UserID 12 | namespaces: string[] 13 | } 14 | -------------------------------------------------------------------------------- /packages/sdk/src/utils/uuid.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuidv4 } from 'uuid' 2 | import uniqueId from 'lodash/uniqueId' 3 | 4 | export const SEPARATOR = '-' 5 | 6 | let UUID: string 7 | 8 | export function uuid(label = ''): string { 9 | if (typeof UUID === 'undefined') { 10 | // Create UUID on the first use of the function in order to avoid premature `uuidv4` calls. 11 | // Doing it outside will break browser projects that utilize server-side rendering (no 12 | // `window` while build's target is `web`). 13 | UUID = uuidv4() 14 | } 15 | 16 | // Incrementing + human readable uuid 17 | return uniqueId(`${UUID}${label ? `${SEPARATOR}${label}` : ''}`) 18 | } 19 | -------------------------------------------------------------------------------- /packages/sdk/test/browser-smoke-test/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const path = require('path') 3 | const express = require('express') 4 | const { Logger } = require('@streamr/utils') 5 | 6 | const logger = new Logger(module) 7 | const app = express() 8 | 9 | app.use('/static', express.static(path.join(__dirname, '/../../dist'))) 10 | app.get('/stop', (_req, res) => { 11 | res.end() 12 | 13 | if (server) { 14 | logger.info('Stopping...') 15 | server.close() 16 | } 17 | }) 18 | app.get('/', (_req, res) => { 19 | res.sendFile(path.join(__dirname, 'smoke-test.html')) 20 | }) 21 | 22 | const server = app.listen(8880, () => { 23 | logger.info('Serving on', server.address()) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/sdk/test/browser-smoke-test/smoke-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd "$(dirname "$0")" 3 | npm run build-browser-production 4 | node server.js & 5 | pid=$! 6 | ../../../../node_modules/.bin/nightwatch --headless --verbose --timeout=30000 --config=nightwatch.conf.js browser.js 7 | kill $pid 8 | -------------------------------------------------------------------------------- /packages/sdk/test/end-to-end/StreamrClient.test.ts: -------------------------------------------------------------------------------- 1 | import { isRunningInElectron } from '@streamr/test-utils' 2 | import { StreamrClient } from '../../src' 3 | 4 | describe('StreamrClient', () => { 5 | let client: StreamrClient 6 | 7 | beforeEach(async () => { 8 | client = new StreamrClient({ 9 | environment: 'dev2' 10 | }) 11 | }, 30 * 1000) 12 | 13 | afterEach(async () => { 14 | await client.destroy() 15 | }) 16 | 17 | it('getPeerDescriptor', async () => { 18 | const descriptor = await client.getPeerDescriptor() 19 | expect(descriptor).toMatchObject({ 20 | nodeId: expect.toBeString(), 21 | type: isRunningInElectron() ? 'browser' : 'nodejs', 22 | }) 23 | expect(descriptor.nodeId).toEqual(await client.getNodeId()) 24 | }, 30 * 1000) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/sdk/test/end-to-end/start-node.test.ts: -------------------------------------------------------------------------------- 1 | import { StreamrClient } from '../../src/StreamrClient' 2 | 3 | describe('start node', () => { 4 | 5 | it('start without websocket', async () => { 6 | const client = new StreamrClient({ 7 | environment: 'dev2', 8 | network: { 9 | controlLayer: { 10 | websocketPortRange: null 11 | } 12 | } 13 | }) 14 | const node = client.getNode() 15 | expect((await node.getPeerDescriptor()).websocket).toBeUndefined() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/sdk/test/exports/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-streamr-exports", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "commonjs.js", 6 | "private": true, 7 | "scripts": { 8 | "pretest": "rm -Rf dist", 9 | "test": "npm run test-commonjs && npm run test-esm && npm run test-ts # skip && npm run webpack", 10 | "build-ts": "tsc --project ./tsconfig.json", 11 | "pretest-ts": "npm run build-ts", 12 | "test-ts": "node dist/tests/typescript.js", 13 | "test-esm": "node tests/esm.mjs", 14 | "test-commonjs": "node tests/commonjs.js", 15 | "webpack": "../../node_modules/.bin/webpack --progress", 16 | "link": "mkdir -p node_modules && ln -fs ../../../dist/ node_modules/streamr-client" 17 | }, 18 | "author": "Tim Oxley ", 19 | "license": "ISC", 20 | "dependencies": { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/sdk/test/exports/tests/commonjs.js: -------------------------------------------------------------------------------- 1 | // checks that require works 2 | const StreamrClient = require('@streamr/sdk') 3 | 4 | console.info('const StreamrClient = require(\'@streamr/sdk\'):', { StreamrClient }) 5 | 6 | const client = new StreamrClient() 7 | 8 | client.connect().then(async () => { 9 | console.info('success') 10 | await client.destroy() 11 | process.exit(0) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/sdk/test/exports/tests/esm.mjs: -------------------------------------------------------------------------------- 1 | // check esm works, as native and via webpack + babel. Also see typescript.ts 2 | import DefaultExport, * as NamedExports from '@streamr/sdk' 3 | 4 | console.info('import DefaultExport, * as NamedExports from \'@streamr/sdk\':', { DefaultExport, NamedExports }) 5 | 6 | const StreamrClient = DefaultExport 7 | 8 | const client = new StreamrClient() 9 | console.assert(!!NamedExports.Subscription, 'NamedExports should have Subscription') 10 | client.connect().then(async () => { 11 | console.info('success') 12 | await client.destroy() 13 | process.exit(0) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/sdk/test/exports/tests/typescript.ts: -------------------------------------------------------------------------------- 1 | // check ts esm works via tsc 2 | 3 | import DefaultExport, * as NamedExports from '@streamr/sdk' 4 | 5 | console.info('import DefaultExport, * as NamedExports from \'@streamr/sdk\':', { DefaultExport, NamedExports }) 6 | 7 | const StreamrClient = DefaultExport 8 | 9 | const client = new StreamrClient() 10 | 11 | console.assert(!!NamedExports.Subscription, 'NamedExports should have Subscription') 12 | 13 | client.connect().then(async () => { 14 | console.info('success') 15 | await client.destroy() 16 | process.exit(0) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/sdk/test/exports/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "tests/typescript.ts" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sdk/test/test-utils/fake/FakeLogger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@streamr/utils' 2 | 3 | // @ts-expect-error not implementing logger 4 | export class FakeLogger implements Logger { 5 | debug = jest.fn() 6 | error = jest.fn() 7 | fatal = jest.fn() 8 | info = jest.fn() 9 | trace = jest.fn() 10 | warn = jest.fn() 11 | } 12 | -------------------------------------------------------------------------------- /packages/sdk/test/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest-extended' 2 | import '@streamr/test-utils/customMatcherTypes' 3 | -------------------------------------------------------------------------------- /packages/sdk/test/unit/GroupKey.test.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | import { GroupKey } from '../../src/encryption/GroupKey' 3 | 4 | describe('GroupKey', () => { 5 | 6 | describe('constructor', () => { 7 | 8 | it('does not throw with valid values', () => { 9 | new GroupKey('mockId', Buffer.from('aB123456789012345678901234567890')) 10 | }) 11 | 12 | it('throws if key is the wrong size', () => { 13 | expect(() => { 14 | new GroupKey('test', crypto.randomBytes(16)) 15 | }).toThrow('size') 16 | }) 17 | 18 | it('throws if key is not a buffer', () => { 19 | expect(() => { 20 | // @ts-expect-error expected error below is desirable, show typecheks working as intended 21 | new GroupKey('test', Array.from(crypto.randomBytes(32))) 22 | }).toThrow('Buffer') 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/sdk/test/unit/Stream.test.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | 3 | import { Stream } from '../../src/Stream' 4 | 5 | describe('Stream', () => { 6 | 7 | it('getPartitionCount', async () => { 8 | const stream = new Stream( 9 | undefined as any, 10 | { 11 | getStreamMetadata: async () => ({ partitions: 150 }) 12 | } as any 13 | ) 14 | await expect(() => stream.getPartitionCount()).rejects.toThrowStreamrClientError({ 15 | message: 'Invalid partition count: 150', 16 | code: 'INVALID_STREAM_METADATA' 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/sdk/test/unit/events.test.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | 3 | import { FakeEnvironment } from '../test-utils/fake/FakeEnvironment' 4 | 5 | describe('events', () => { 6 | describe('emit', () => { 7 | it('publish', async () => { 8 | const environment = new FakeEnvironment() 9 | const client = environment.createClient() 10 | const onEmit = jest.fn() 11 | // @ts-expect-error internal event 12 | client.on('messagePublished', onEmit) 13 | const stream = await client.createStream('/test') 14 | await client.publish(stream.id, {}) 15 | await stream.publish({}) 16 | expect(onEmit).toHaveBeenCalledTimes(2) 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /packages/sdk/test/unit/uuid.test.ts: -------------------------------------------------------------------------------- 1 | import { uuid } from '../../src/utils/uuid' 2 | 3 | function extractCounterAtEndOfString(str: string): number | never { 4 | return parseInt(str.match(/\d+$/)![0]) 5 | } 6 | 7 | describe('uuid', () => { 8 | it('generates different ids', () => { 9 | expect(uuid('test')).not.toEqual(uuid('test')) 10 | }) 11 | it('includes text', () => { 12 | expect(uuid('test')).toContain('test') 13 | }) 14 | it('increments', () => { 15 | const uid = uuid('test') // generate new text to ensure count starts at 1 16 | const firstValue = uuid(uid) 17 | const secondValue = uuid(uid) 18 | expect(extractCounterAtEndOfString(firstValue)).toBeLessThan(extractCounterAtEndOfString(secondValue)) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /packages/sdk/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "noEmit": true, 6 | "declarationDir": "dist/types", 7 | "outDir": "dist", 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "strictBindCallApply": true, 11 | "baseUrl": "." 12 | }, 13 | "include": [ 14 | "package.json", 15 | "src/**/*", 16 | "src/**/*.json", 17 | "src/config.schema.json" 18 | ], 19 | "exclude": ["src/exports-esm.mjs"], 20 | "references": [ 21 | { "path": "../test-utils/tsconfig.node.json" }, 22 | { "path": "../trackerless-network/tsconfig.browser.json" } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/sdk/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "declarationDir": "dist/types", 6 | "lib": ["es2021", "dom"], 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "resolveJsonModule": true, 10 | "noImplicitOverride": false 11 | }, 12 | "include": [ 13 | "src/**/*", 14 | "src/**/*.json", 15 | "src/config.schema.json" 16 | ], 17 | "exclude": [ 18 | "src/exports-esm.mjs", 19 | "jest.config.ts" 20 | ], 21 | "references": [ 22 | { "path": "../test-utils/tsconfig.node.json" }, 23 | { "path": "../trackerless-network/tsconfig.node.json" }, 24 | { "path": "../dht/tsconfig.node.json" } 25 | 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/sdk/webpack-karma.config.js: -------------------------------------------------------------------------------- 1 | const defaultConfig = require('./webpack.config') 2 | 3 | module.exports = (env, argv) => { 4 | const config = defaultConfig(env, argv) 5 | 6 | return { 7 | ...config, 8 | resolve: { 9 | ...config.resolve, 10 | fallback: { 11 | ...config.resolve.fallback, 12 | v8: false, 13 | 'jest-leak-detector': false, 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/test-utils/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /packages/test-utils/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /packages/test-utils/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /packages/test-utils/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /packages/test-utils/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/test-utils/.idea/streamr-test-utils.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/test-utils/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/test-utils/README.md: -------------------------------------------------------------------------------- 1 | # @streamr/test-utils 2 | A collection of shared test utilities. 3 | 4 | ## Install 5 | ``` 6 | npm install --save-dev @streamr/test-utils 7 | ``` 8 | -------------------------------------------------------------------------------- /packages/test-utils/customMatcherTypes.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file injects custom matcher types into Jest. 3 | * 4 | * The actual type declarations are in "./dist/src/customMatcherTypes.d.ts", and this file serves 5 | * as an alias. In a dependent library the type information is typically injected by adding 6 | * an import statement to test/types/global.d.ts. Since this file is listed in package.json's 7 | * "files" section, we can import the types like this: 8 | * import '@streamr/test-utils/customMatcherTypes' 9 | */ 10 | 11 | /// 12 | -------------------------------------------------------------------------------- /packages/test-utils/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | import defaultConfig from '../../jest.config' 3 | 4 | const config: Config.InitialOptions = { 5 | ...defaultConfig, 6 | setupFilesAfterEnv: [ 7 | ...defaultConfig.setupFilesAfterEnv, 8 | './src/setupCustomMatchers.ts', 9 | ], 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /packages/test-utils/setupCustomMatchers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file adds the custom matcher functionality to Jest. 3 | * 4 | * The actual setup is in "./dist/src/setupCustomMatchers.js", and this file serves as an alias. 5 | * Since this file is listed in package.json's "files" section, we can setup the custom matchers 6 | * like this: 7 | * setupFilesAfterEnv: ['@streamr/test-utils/setupCustomMatchers'] 8 | */ 9 | 10 | require('./dist/src/setupCustomMatchers') 11 | -------------------------------------------------------------------------------- /packages/test-utils/src/customMatcherTypes.ts: -------------------------------------------------------------------------------- 1 | import { CustomMatchers } from './customMatchers' 2 | 3 | // we could ES2015 module syntax (https://jestjs.io/docs/expect#expectextendmatchers), 4 | // but the IDE doesn't find custom matchers if we do that 5 | declare global { 6 | // eslint-disable-next-line @typescript-eslint/no-namespace 7 | namespace jest { 8 | // eslint-disable-next-line @typescript-eslint/no-empty-object-type 9 | interface Expect extends CustomMatchers {} 10 | // eslint-disable-next-line @typescript-eslint/no-empty-object-type 11 | interface Matchers extends CustomMatchers {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/test-utils/src/setupCustomMatchers.ts: -------------------------------------------------------------------------------- 1 | import * as customMatchers from './customMatchers' 2 | 3 | expect.extend(customMatchers) 4 | -------------------------------------------------------------------------------- /packages/test-utils/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "test/**/*" 6 | ], 7 | "references": [ 8 | { "path": "../utils/tsconfig.node.json" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/test-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/test-utils/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "types": ["node", "jest"] 6 | }, 7 | "include": [ 8 | "src/**/*" 9 | ], 10 | "references": [ 11 | { "path": "../utils/tsconfig.node.json" } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/trackerless-network/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | generated/ 4 | 5 | # IntelliJ IDEA 6 | .idea/workspace.xml 7 | .idea/tasks.xml 8 | .idea/dictionaries 9 | .idea/httpRequests 10 | .idea/git_toolbox_prj.xml 11 | bin/.idea 12 | 13 | docs 14 | .vscode 15 | coverage 16 | 17 | FirstMessageTime.log 18 | Topology.csv 19 | -------------------------------------------------------------------------------- /packages/trackerless-network/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | import defaultConfig from '../../jest.config' 3 | 4 | const config: Config.InitialOptions = { 5 | ...defaultConfig, 6 | setupFilesAfterEnv: [ 7 | ...defaultConfig.setupFilesAfterEnv, 8 | '@streamr/test-utils/setupCustomMatchers', 9 | ], 10 | testTimeout: 15000, 11 | } 12 | 13 | export default config 14 | -------------------------------------------------------------------------------- /packages/trackerless-network/karma-setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This setup script is executed indirectly by Karma via the `browser-test-runner` 3 | * package. See karma-config.js in that package for more details. 4 | */ 5 | 6 | const { customMatchers } = require('@streamr/test-utils') 7 | expect.extend(customMatchers) -------------------------------------------------------------------------------- /packages/trackerless-network/proto.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ./generated 2 | npx protoc --ts_out ./generated --ts_opt server_generic,generate_dependencies,long_type_number --proto_path ../.. packages/trackerless-network/protos/NetworkRpc.proto --experimental_allow_proto3_optional 3 | -------------------------------------------------------------------------------- /packages/trackerless-network/src/NodeInfoRpcRemote.ts: -------------------------------------------------------------------------------- 1 | import { NodeInfoResponse } from '../generated/packages/trackerless-network/protos/NetworkRpc' 2 | import { NodeInfoRpcClient } from '../generated/packages/trackerless-network/protos/NetworkRpc.client' 3 | import { RpcRemote } from '@streamr/dht' 4 | 5 | export class NodeInfoRpcRemote extends RpcRemote { 6 | 7 | async getInfo(): Promise { 8 | return this.getClient().getInfo({}, this.formDhtRpcOptions()) 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /packages/trackerless-network/src/content-delivery-layer/formStreamPartDeliveryServiceId.ts: -------------------------------------------------------------------------------- 1 | import { ServiceID } from '@streamr/dht' 2 | import { StreamPartID } from '@streamr/utils' 3 | 4 | export const formStreamPartContentDeliveryServiceId = (streamPartId: StreamPartID): ServiceID => { 5 | // could be "content-delivery" instead of "delivery", but that is a breaking change 6 | return `stream-part-delivery-${streamPartId}` 7 | } 8 | -------------------------------------------------------------------------------- /packages/trackerless-network/src/control-layer/ControlLayerNode.ts: -------------------------------------------------------------------------------- 1 | import { ConnectionsView, DataEntry, DhtAddress, ITransport, PeerDescriptor } from '@streamr/dht' 2 | import { Any } from '../../generated/google/protobuf/any' 3 | 4 | export interface ControlLayerNode extends ITransport { 5 | joinDht(entryPointDescriptors: PeerDescriptor[]): Promise 6 | hasJoined(): boolean 7 | getLocalPeerDescriptor(): PeerDescriptor 8 | fetchDataFromDht(key: DhtAddress): Promise 9 | storeDataToDht(key: DhtAddress, data: Any): Promise 10 | deleteDataFromDht(key: DhtAddress, waitForCompletion: boolean): Promise 11 | waitForNetworkConnectivity(): Promise 12 | getTransport(): ITransport 13 | getNeighbors(): PeerDescriptor[] 14 | getConnectionsView(): ConnectionsView 15 | start(): Promise 16 | stop(): Promise 17 | } 18 | -------------------------------------------------------------------------------- /packages/trackerless-network/src/exports.ts: -------------------------------------------------------------------------------- 1 | export { NetworkNode, createNetworkNode } from './NetworkNode' 2 | export { type NetworkOptions, NetworkStack } from './NetworkStack' 3 | export { type ContentDeliveryManagerOptions, streamPartIdToDataKey } from './ContentDeliveryManager' 4 | export { 5 | AsymmetricEncryptionType, 6 | ContentType, 7 | EncryptionType, 8 | EncryptedGroupKey, 9 | GroupKeyRequest, 10 | GroupKeyResponse, 11 | MessageID, 12 | MessageRef, 13 | ProxyDirection, 14 | SignatureType, 15 | StreamMessage, 16 | ControlLayerInfo 17 | } from '../generated/packages/trackerless-network/protos/NetworkRpc' 18 | export type { ExternalRpcClient, ExternalRpcClientClass } from './control-layer/ExternalNetworkRpc' 19 | export type { NodeInfo, StreamPartitionInfo, ContentDeliveryLayerNeighborInfo } from './types' 20 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@streamr/test-utils/customMatcherTypes' 2 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/unit/StreamPartIDDataKey.test.ts: -------------------------------------------------------------------------------- 1 | import { StreamPartIDUtils } from '@streamr/utils' 2 | import { streamPartIdToDataKey } from '../../src/ContentDeliveryManager' 3 | 4 | describe('StreamPartIDtoDataKey', () => { 5 | 6 | it('generated key length is correct (160 bits)', () => { 7 | const streamPartId = StreamPartIDUtils.parse('stream#0') 8 | const dataKey = streamPartIdToDataKey(streamPartId) 9 | expect(dataKey.length).toEqual(40) 10 | }) 11 | 12 | }) 13 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/utils/mock/MockConnectionsView.ts: -------------------------------------------------------------------------------- 1 | import { PeerDescriptor } from '@streamr/dht' 2 | 3 | export class MockConnectionsView { 4 | // eslint-disable-next-line class-methods-use-this 5 | getConnections(): PeerDescriptor[] { 6 | return [] 7 | } 8 | 9 | // eslint-disable-next-line class-methods-use-this 10 | getConnectionCount(): number { 11 | return 0 12 | } 13 | 14 | // eslint-disable-next-line class-methods-use-this 15 | hasConnection(): boolean { 16 | return false 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/utils/mock/MockHandshaker.ts: -------------------------------------------------------------------------------- 1 | import { Methods } from '@streamr/test-utils' 2 | import { Handshaker } from '../../../src/content-delivery-layer/neighbor-discovery/Handshaker' 3 | import { DhtAddress } from '@streamr/dht' 4 | 5 | export class MockHandshaker implements Methods { 6 | 7 | // eslint-disable-next-line class-methods-use-this 8 | getOngoingHandshakes(): Set { 9 | return new Set() 10 | } 11 | 12 | // eslint-disable-next-line class-methods-use-this 13 | async attemptHandshakesOnContacts(excludedIds: DhtAddress[]): Promise { 14 | return excludedIds 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/utils/mock/MockNeighborFinder.ts: -------------------------------------------------------------------------------- 1 | import { Methods } from '@streamr/test-utils' 2 | import { NeighborFinder } from '../../../src/content-delivery-layer/neighbor-discovery/NeighborFinder' 3 | 4 | export class MockNeighborFinder implements Methods { 5 | 6 | // eslint-disable-next-line class-methods-use-this 7 | start(): void { 8 | 9 | } 10 | 11 | // eslint-disable-next-line class-methods-use-this 12 | stop(): void { 13 | 14 | } 15 | 16 | // eslint-disable-next-line class-methods-use-this 17 | isRunning(): boolean { 18 | return false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/utils/mock/MockNeighborUpdateManager.ts: -------------------------------------------------------------------------------- 1 | import { Methods } from '@streamr/test-utils' 2 | import { NeighborUpdateManager } from '../../../src/content-delivery-layer/neighbor-discovery/NeighborUpdateManager' 3 | import { NeighborUpdate } from '../../../generated/packages/trackerless-network/protos/NetworkRpc' 4 | 5 | export class MockNeighborUpdateManager implements Methods { 6 | 7 | // eslint-disable-next-line class-methods-use-this 8 | async start(): Promise { 9 | 10 | } 11 | 12 | // eslint-disable-next-line class-methods-use-this 13 | stop(): void { 14 | 15 | } 16 | 17 | // eslint-disable-next-line class-methods-use-this 18 | async neighborUpdate(): Promise { 19 | return NeighborUpdate.create() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/trackerless-network/test/utils/mock/MockTransport.ts: -------------------------------------------------------------------------------- 1 | import { ITransport, PeerDescriptor, TransportEvents } from '@streamr/dht' 2 | import { EventEmitter } from 'eventemitter3' 3 | 4 | export class MockTransport extends EventEmitter implements ITransport { 5 | 6 | // eslint-disable-next-line class-methods-use-this 7 | async send(): Promise { 8 | 9 | } 10 | 11 | // eslint-disable-next-line class-methods-use-this 12 | getLocalPeerDescriptor(): PeerDescriptor { 13 | return PeerDescriptor.create() 14 | } 15 | 16 | // eslint-disable-next-line class-methods-use-this 17 | stop(): void { 18 | } 19 | 20 | // eslint-disable-next-line class-methods-use-this 21 | getDiagnosticInfo(): Record { 22 | return {} 23 | } 24 | 25 | // eslint-disable-next-line class-methods-use-this 26 | enablePrivateClientMode(): void { 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /packages/trackerless-network/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "generated/**/*", 11 | "test/**/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/trackerless-network/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*", 10 | "test/**/*", 11 | "package.json" 12 | ], 13 | "references": [ 14 | { "path": "../proto-rpc/tsconfig.node.json" }, 15 | { "path": "../dht/tsconfig.node.json" } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/trackerless-network/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/trackerless-network/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "noImplicitOverride": false 6 | }, 7 | "include": [ 8 | "src/**/*", 9 | "generated/**/*", 10 | "test/benchmark/first-message.ts", 11 | "test/utils/utils.ts", 12 | "package.json" 13 | ], 14 | "references": [ 15 | { "path": "../dht/tsconfig.node.json" } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/utils/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # @streamr/utils 2 | A collection of shared common utilities. 3 | 4 | ## Install 5 | ``` 6 | npm install @streamr/utils 7 | ``` 8 | -------------------------------------------------------------------------------- /packages/utils/jest.config.ts: -------------------------------------------------------------------------------- 1 | export { default } from '../../jest.config' 2 | -------------------------------------------------------------------------------- /packages/utils/karma.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-require-imports 2 | const { createKarmaConfig, createWebpackConfig } = require('@streamr/browser-test-runner') 3 | 4 | const TEST_PATHS = ['test/**/*.ts'] 5 | 6 | module.exports = createKarmaConfig(TEST_PATHS, createWebpackConfig({ 7 | entry: './src/exports.ts', 8 | libraryName: 'utils', 9 | fallback: { 10 | module: false 11 | } 12 | })) 13 | -------------------------------------------------------------------------------- /packages/utils/src/Cache.ts: -------------------------------------------------------------------------------- 1 | export class Cache { 2 | 3 | private value?: V 4 | private valueTimestamp?: number 5 | private readonly valueFactory: () => Promise 6 | private readonly maxAgeInMilliseconds: number 7 | 8 | constructor(valueFactory: () => Promise, maxAgeInMilliseconds: number) { 9 | this.valueFactory = valueFactory 10 | this.maxAgeInMilliseconds = maxAgeInMilliseconds 11 | } 12 | 13 | async get(): Promise { 14 | const now = Date.now() 15 | if ((this.valueTimestamp === undefined) || (now > (this.valueTimestamp + this.maxAgeInMilliseconds))) { 16 | this.value = await this.valueFactory() 17 | this.valueTimestamp = now 18 | } 19 | return this.value! 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/utils/src/ENSName.ts: -------------------------------------------------------------------------------- 1 | import { BrandedString } from './types' 2 | 3 | export function isENSNameFormatIgnoreCase(str: string): boolean { 4 | return str.indexOf('.') > 0 5 | } 6 | 7 | export type ENSName = BrandedString<'ENSName'> 8 | 9 | export function toENSName(str: string): ENSName | never { 10 | if (isENSNameFormatIgnoreCase(str)) { 11 | return str.toLowerCase() as ENSName 12 | } 13 | throw new Error(`not a valid ENS name: "${str}"`) 14 | } 15 | -------------------------------------------------------------------------------- /packages/utils/src/EthereumAddress.ts: -------------------------------------------------------------------------------- 1 | import { BrandedString } from './types' 2 | 3 | const REGEX = /^0x[a-fA-F0-9]{40}$/ 4 | export const PREFIXED_STRING_LENGTH = 42 5 | 6 | export type EthereumAddress = BrandedString<'EthereumAddress'> 7 | 8 | export function toEthereumAddress(str: string): EthereumAddress | never { 9 | if (REGEX.test(str)) { 10 | return str.toLowerCase() as EthereumAddress 11 | } 12 | throw new Error(`not a valid Ethereum address: "${str}"`) 13 | } 14 | -------------------------------------------------------------------------------- /packages/utils/src/Gate.ts: -------------------------------------------------------------------------------- 1 | import { Defer } from './Defer' 2 | 3 | /* 4 | * Gate to lock access to some resource. 5 | */ 6 | export class Gate { 7 | 8 | private pending?: Defer 9 | 10 | constructor(isOpen: boolean) { 11 | if (!isOpen) { 12 | this.close() 13 | } 14 | } 15 | 16 | open(): void { 17 | this.clearPending() 18 | } 19 | 20 | close(): void { 21 | this.pending ??= new Defer() 22 | } 23 | 24 | isOpen(): boolean { 25 | return !this.pending 26 | } 27 | 28 | private clearPending(): void { 29 | const { pending } = this 30 | if (pending === undefined) { 31 | return 32 | } 33 | this.pending = undefined 34 | pending.resolve(undefined) 35 | } 36 | 37 | async waitUntilOpen(): Promise { 38 | if (this.pending) { 39 | await this.pending 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/utils/src/HexString.ts: -------------------------------------------------------------------------------- 1 | export type HexString = string 2 | -------------------------------------------------------------------------------- /packages/utils/src/WeiAmount.ts: -------------------------------------------------------------------------------- 1 | export type WeiAmount = bigint 2 | 3 | const PRECISION = 1e18 4 | 5 | export const multiplyWeiAmount = (val1: WeiAmount, val2: number): WeiAmount => { 6 | return val1 * BigInt(PRECISION * val2) / BigInt(PRECISION) 7 | } 8 | -------------------------------------------------------------------------------- /packages/utils/src/addManagedEventListener.ts: -------------------------------------------------------------------------------- 1 | export const addManagedEventListener = void>( 2 | emitter: { 3 | on: (eventName: TEventName, listener: TListener) => unknown 4 | off: (eventName: TEventName, listener: TListener) => unknown 5 | }, 6 | eventName: TEventName, 7 | listener: TListener, 8 | abortSignal: AbortSignal 9 | ): void => { 10 | if (!abortSignal.aborted) { 11 | emitter.on(eventName, listener) 12 | abortSignal.addEventListener('abort', () => { 13 | emitter.off(eventName, listener) 14 | }, { 15 | once: true 16 | }) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/utils/src/collect.ts: -------------------------------------------------------------------------------- 1 | export const collect = async (source: AsyncIterable, maxCount?: number): Promise => { 2 | if ((maxCount !== undefined) && (maxCount <= 0)) { 3 | return [] 4 | } 5 | const items: T[] = [] 6 | for await (const item of source) { 7 | items.push(item) 8 | if ((maxCount !== undefined) && (items.length >= maxCount)) { 9 | break 10 | } 11 | } 12 | return items 13 | } 14 | -------------------------------------------------------------------------------- /packages/utils/src/crossPlatformCrypto.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | 3 | declare const self: any 4 | 5 | export function getSubtle(): crypto.webcrypto.SubtleCrypto { 6 | // in browser main thread, self === window 7 | // in web workers, self is defined but window is not 8 | // in node.js, self is undefined 9 | const subtle = typeof self !== 'undefined' ? self?.crypto?.subtle : crypto.webcrypto.subtle 10 | if (!subtle) { 11 | const url = 'https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto' 12 | throw new Error(`SubtleCrypto not supported. This feature is available only in secure contexts (HTTPS) & Node 16+. ${url}`) 13 | } 14 | return subtle 15 | } 16 | -------------------------------------------------------------------------------- /packages/utils/src/executeSafePromise.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from './Logger' 2 | 3 | const logger = new Logger(module) 4 | 5 | /** 6 | * Execute a promise that should never reject. If it does, log the error and exit the process. 7 | * To be used in places where we want to "annotate" that the intention of a promise is never 8 | * to reject (unless something is really wrong). 9 | */ 10 | export const executeSafePromise = async (createPromise: () => Promise): Promise => { 11 | try { 12 | return await createPromise() 13 | } catch (err: any) { 14 | logger.fatal('Assertion failure!', { message: err?.message, err }) 15 | if (process.exit !== undefined) { 16 | process.exit(1) 17 | } else { 18 | // cause an unhandled promise rejection on purpose 19 | throw new Error('executeSafePromise: Assertion failure!', err) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/utils/src/filePathToNodeFormat.ts: -------------------------------------------------------------------------------- 1 | import os from 'os' 2 | 3 | // TODO: use untildify instead? 4 | export function filePathToNodeFormat(filePath: string): string { 5 | if (filePath.startsWith('~/')) { 6 | return filePath.replace('~', os.homedir()) 7 | } else { 8 | return filePath 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/utils/src/ipv4ToNumber.ts: -------------------------------------------------------------------------------- 1 | export const ipv4ToNumber = (ip: string): number => { 2 | const octets = ip.split('.').map(Number) 3 | return ((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]) >>> 0 4 | } 5 | 6 | export const numberToIpv4 = (value: number): string => { 7 | const octets = [24, 16, 8, 0].map((shift) => (value >> shift) & 255) 8 | return octets.join('.') 9 | } 10 | -------------------------------------------------------------------------------- /packages/utils/src/isENSName.ts: -------------------------------------------------------------------------------- 1 | import { EthereumAddress } from './EthereumAddress' 2 | import { ENSName, isENSNameFormatIgnoreCase } from './ENSName' 3 | 4 | export function isENSName(domain: EthereumAddress | ENSName): domain is ENSName { 5 | return isENSNameFormatIgnoreCase(domain) 6 | } 7 | -------------------------------------------------------------------------------- /packages/utils/src/merge.ts: -------------------------------------------------------------------------------- 1 | import isArray from 'lodash/isArray' 2 | import mergeWith from 'lodash/mergeWith' 3 | 4 | /* 5 | * Does deep merge. This is similar to `lodash` merge, but handles arrays differently: 6 | * `lodash` merges elements of arrays by their indices, this overwrites the existing 7 | * value with the array 8 | */ 9 | export const merge = (...sources: (Partial | undefined)[]): TTarget => { 10 | const result: Record = {} 11 | mergeWith(result, ...sources, (_: any, srcValue: any) => { 12 | if (isArray(srcValue)) { 13 | return [...srcValue] 14 | } else { 15 | return undefined // no customization: does the default merging for this field 16 | } 17 | }) 18 | return result as TTarget 19 | } 20 | -------------------------------------------------------------------------------- /packages/utils/src/pTransaction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an iterable of promises, settles them one at a time. 3 | * If all of them resolve, returns the list of values. 4 | * If one of them rejects, provides a callback for reverting the 5 | * preceding (already resolved) values. 6 | */ 7 | export async function pTransaction( 8 | promises: Iterable>, 9 | rollback: (target: T) => Promise | void 10 | ): Promise { 11 | const results: T[] = [] 12 | for (const promise of promises) { 13 | try { 14 | results.push(await promise) 15 | } catch (err) { 16 | await Promise.allSettled(results.map((r) => rollback(r))) 17 | throw err 18 | } 19 | } 20 | return results 21 | } 22 | -------------------------------------------------------------------------------- /packages/utils/src/partition.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PARTITION_COUNT = 1 2 | export const MAX_PARTITION_COUNT = 100 3 | 4 | export function ensureValidStreamPartitionIndex(streamPartition: number | undefined): void | never { 5 | if (!Number.isSafeInteger(streamPartition) || streamPartition! < 0 || streamPartition! >= MAX_PARTITION_COUNT) { 6 | throw new Error(`invalid streamPartition value: ${streamPartition}`) 7 | } 8 | } 9 | 10 | export function ensureValidStreamPartitionCount(streamPartition: number | undefined): void | never { 11 | if (!Number.isSafeInteger(streamPartition) || streamPartition! < 0 || streamPartition! > MAX_PARTITION_COUNT) { 12 | throw new Error(`invalid streamPartition value: ${streamPartition}`) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/utils/src/randomString.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 2 | 3 | // From: https://stackoverflow.com/questions/10726909/random-alpha-numeric-string-in-javascript 4 | export function randomString(length: number, charset = DEFAULT_CHARSET): string { 5 | let result = '' 6 | for (let i = 0; i < length; ++i) { 7 | result += charset[Math.floor(Math.random() * charset.length)] 8 | } 9 | return result 10 | } 11 | -------------------------------------------------------------------------------- /packages/utils/src/retry.ts: -------------------------------------------------------------------------------- 1 | import { wait } from './wait' 2 | 3 | export const retry = async ( 4 | task: () => Promise, 5 | onRetryableFailure: (message: string, error: any) => void, 6 | description: string, 7 | maxCount: number, 8 | delay: number, 9 | ): Promise => { 10 | for (let i = 0; i < maxCount; i++) { 11 | try { 12 | const result = await task() 13 | return result 14 | } catch (err: any) { 15 | if (i < (maxCount - 1)) { 16 | const message = `${description} failed, retrying in ${delay} ms` 17 | onRetryableFailure(message, err) 18 | await wait(delay) 19 | } 20 | } 21 | } 22 | throw new Error(`${description} failed after ${maxCount} attempts`) 23 | } 24 | -------------------------------------------------------------------------------- /packages/utils/src/scheduleAtInterval.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @param {number} interval - number of milliseconds to wait after a task is completed 3 | */ 4 | import { repeatScheduleTask } from './scheduleAtFixedRate' 5 | import { setAbortableTimeout } from './abortableTimers' 6 | 7 | export const scheduleAtInterval = async ( 8 | task: () => Promise, 9 | interval: number, 10 | executeAtStart: boolean, 11 | abortSignal: AbortSignal 12 | ): Promise => { 13 | if (abortSignal.aborted) { 14 | return 15 | } 16 | if (executeAtStart) { 17 | await task() 18 | } 19 | repeatScheduleTask((doneCb) => { 20 | setAbortableTimeout(async () => { 21 | await task() 22 | doneCb() 23 | }, interval, abortSignal) 24 | }, abortSignal) 25 | } 26 | -------------------------------------------------------------------------------- /packages/utils/src/toEthereumAddressOrENSName.ts: -------------------------------------------------------------------------------- 1 | import { EthereumAddress, toEthereumAddress } from './EthereumAddress' 2 | import { ENSName, isENSNameFormatIgnoreCase, toENSName } from './ENSName' 3 | 4 | export function toEthereumAddressOrENSName(str: string): EthereumAddress | ENSName | never { 5 | return isENSNameFormatIgnoreCase(str) ? toENSName(str) : toEthereumAddress(str) 6 | } 7 | -------------------------------------------------------------------------------- /packages/utils/src/types.ts: -------------------------------------------------------------------------------- 1 | export type BrandedString = string & { __brand: T } 2 | 3 | export type Events = { [K in keyof T]: (payload: any) => void } 4 | 5 | export type ChangeFieldType = Omit & Record 6 | 7 | export type MapKey = string | number | boolean | symbol | bigint | object 8 | -------------------------------------------------------------------------------- /packages/utils/src/wait.ts: -------------------------------------------------------------------------------- 1 | import { asAbortable } from './asAbortable' 2 | 3 | /** 4 | * Wait for a specific time 5 | * @param ms time to wait for in milliseconds 6 | * @param abortSignal to control abortion of any wait 7 | * @returns {Promise} resolves when time has passed 8 | */ 9 | export function wait(ms: number, abortSignal?: AbortSignal): Promise { 10 | let timeoutRef: NodeJS.Timeout 11 | return asAbortable( 12 | new Promise((resolve) => { 13 | timeoutRef = setTimeout(resolve, ms) 14 | }), 15 | abortSignal 16 | ).finally(() => { 17 | clearTimeout(timeoutRef) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /packages/utils/src/withRateLimit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wraps a rate limiter around a function that ensures the function is invoked max once per `intervalInMs`. 3 | */ 4 | export function withRateLimit(fn: () => Promise, intervalInMs: number): () => Promise { 5 | let lastInvocationTimestamp = 0 6 | return async () => { 7 | const now = Date.now() 8 | if (now - lastInvocationTimestamp >= intervalInMs) { 9 | lastInvocationTimestamp = now 10 | await fn() 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/utils/test/Cache.test.ts: -------------------------------------------------------------------------------- 1 | import { Cache } from '../src/Cache' 2 | import { wait } from '../src/wait' 3 | 4 | const MAX_AGE = 100 5 | const JITTER_FACTOR = 10 6 | 7 | describe('Cache', () => { 8 | 9 | it('happy path', async () => { 10 | let plainValue = 'foo' 11 | const valueFactory = jest.fn().mockImplementation(async () => plainValue) 12 | const cache = new Cache(valueFactory, MAX_AGE) 13 | expect(await cache.get()).toEqual('foo') 14 | expect(valueFactory).toHaveBeenCalledTimes(1) 15 | plainValue = 'bar' 16 | // should not change yet 17 | expect(await cache.get()).toEqual('foo') 18 | expect(valueFactory).toHaveBeenCalledTimes(1) 19 | // changes after max age elapsed 20 | await wait(MAX_AGE + JITTER_FACTOR) 21 | expect(await cache.get()).toEqual('bar') 22 | expect(valueFactory).toHaveBeenCalledTimes(2) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/utils/test/ENSName.test.ts: -------------------------------------------------------------------------------- 1 | import { toENSName } from '../src/ENSName' 2 | 3 | describe('toENSName', () => { 4 | it.each(['noperiod', '.domain'])('throws on invalid ENS name "%s"', (str) => { 5 | expect(() => toENSName(str)).toThrow() 6 | }) 7 | 8 | it('does not throw on valid ENS name', () => { 9 | expect(() => toENSName('valid.eth')).not.toThrow() 10 | }) 11 | 12 | it('returned ENS name is in lowercase', () => { 13 | expect(toENSName('VALID.eTh')).toEqual('valid.eth') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/utils/test/Gate.test.ts: -------------------------------------------------------------------------------- 1 | import { Gate } from '../src/Gate' 2 | import { wait } from '../src/wait' 3 | import { withTimeout } from '../src/withTimeout' 4 | 5 | describe('Gate', () => { 6 | 7 | it('happy path', async () => { 8 | const gate = new Gate(false) 9 | expect(gate.isOpen()).toBe(false) 10 | await expect(() => withTimeout(gate.waitUntilOpen(), 100)).rejects.toThrow('timed out') 11 | await Promise.all([ 12 | (async () => { 13 | await wait(50) 14 | gate.open() 15 | })(), 16 | withTimeout(gate.waitUntilOpen(), 100) 17 | ]) 18 | expect(gate.isOpen()).toBe(true) 19 | gate.close() 20 | expect(gate.isOpen()).toBe(false) 21 | }) 22 | 23 | }) 24 | -------------------------------------------------------------------------------- /packages/utils/test/Multimap.test.ts: -------------------------------------------------------------------------------- 1 | import { Multimap } from '../src/Multimap' 2 | 3 | describe('Multimap', () => { 4 | 5 | it('happy path', () => { 6 | const map: Multimap = new Multimap() 7 | map.add('foo', 5) 8 | map.add('bar', 5) 9 | map.add('bar', 8) 10 | map.add('temp', 5) 11 | map.addAll('foo', [4, 6, 7, 8, 9]) 12 | map.remove('foo', 8) 13 | map.removeAll('foo', [6, 9]) 14 | map.remove('foo', 123) 15 | map.remove('temp', 5) 16 | expect(map.has('foo', 5)).toBe(true) 17 | expect(map.has('bar', 8)).toBe(true) 18 | expect(map.has('bar', 5)).toBe(true) 19 | expect(map.has('foo', 9)).toBe(false) 20 | expect(map.has('foo', 456)).toBe(false) 21 | expect([...map.keys()]).toIncludeSameMembers(['foo', 'bar']) 22 | expect([...map.values()]).toIncludeSameMembers([5, 4, 7, 5, 8]) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/utils/test/executeSafePromise.test.ts: -------------------------------------------------------------------------------- 1 | import { executeSafePromise } from '../src/executeSafePromise' 2 | import { wait } from '../src/wait' 3 | 4 | describe('executeSafePromise', () => { 5 | it('success', async () => { 6 | const result = await executeSafePromise(async () => { 7 | await wait(10) 8 | return 123 9 | }) 10 | expect(result).toBe(123) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /packages/utils/test/ipv4ToNumber.test.ts: -------------------------------------------------------------------------------- 1 | import { ipv4ToNumber, numberToIpv4 } from '../src/ipv4ToNumber' 2 | 3 | describe('ipv4ToNumber', () => { 4 | 5 | it('happy path', () => { 6 | const stringValue = '192.168.1.1' 7 | const numberValue = 3232235777 8 | expect(ipv4ToNumber(stringValue)).toEqual(numberValue) 9 | expect(numberToIpv4(numberValue)).toEqual(stringValue) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /packages/utils/test/toEthereumAddressOrENSName.test.ts: -------------------------------------------------------------------------------- 1 | import { toEthereumAddressOrENSName } from '../src/toEthereumAddressOrENSName' 2 | 3 | describe('toEthereumAddressOrENSName', () => { 4 | it('returns ethereum address (lowercased) given ethereum address', () => { 5 | expect(toEthereumAddressOrENSName('0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')) 6 | .toEqual('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') 7 | }) 8 | 9 | it('returns ens name (lowercased) given ens name', () => { 10 | expect(toEthereumAddressOrENSName('VALID.eTh')).toEqual('valid.eth') 11 | }) 12 | 13 | it('throws given invalid value', () => { 14 | expect(() => toEthereumAddressOrENSName('invalid')).toThrow() 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/utils/test/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest-extended' 2 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.browser.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDirs": ["src", "test"], 6 | "noImplicitOverride": false 7 | }, 8 | "include": [ 9 | "src/**/*", 10 | "test/**/*" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.jest.json", 3 | "include": [ 4 | "src/**/*", 5 | "test/**/*" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.jest.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.node.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "src/**/*" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /release-git-tags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | VERSION=$1 5 | 6 | if [[ "$1" == "" ]]; then 7 | echo 'usage: release-git-tags.sh ' 8 | exit 1 9 | fi 10 | 11 | # Exit early if version is wrong 12 | if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-(beta|rc)\.[0-9]+)?$ ]]; then 13 | echo "Error: Invalid version" 14 | exit 1 15 | fi 16 | 17 | TAG=v$1 18 | 19 | # Exit early if tags already exist 20 | if git rev-parse "$TAG" >/dev/null 2>&1; then 21 | echo "Error: Tag '$TAG' already exists." 22 | exit 1 23 | fi 24 | 25 | git commit -m "release: $TAG" 26 | git tag $TAG 27 | git push --atomic origin main $TAG 28 | -------------------------------------------------------------------------------- /tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.node.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "isolatedModules": true, 6 | "types": ["node", "jest"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 20 – Streamr", 4 | "extends": "@tsconfig/node20/tsconfig.json", 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "composite": true, 8 | "declaration": true, 9 | "noImplicitOverride": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "types": ["node"], 13 | "incremental": true, 14 | "sourceMap": true, 15 | "stripInternal": true, 16 | "useUnknownInCatchVariables": false, 17 | "strictBindCallApply": true 18 | }, 19 | "lib": [ 20 | "DOM" 21 | ] 22 | } 23 | --------------------------------------------------------------------------------