From f8c4a606b7354daff0390c963863c7d3a9c489f9 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 8 Nov 2023 20:46:15 -0800 Subject: [PATCH 01/54] [WIP] Event streaming --- package-lock.json | 188 ++++++++---------- .../sdk/src/build/build-subscribe-events.js | 23 +++ .../src/build/build-subscribe-events.test.js | 26 +++ packages/sdk/src/build/build-subscription.js | 8 + .../sdk/src/build/build-subscription.test.js | 20 ++ packages/sdk/src/decode/decode.js | 2 + packages/sdk/src/interaction/interaction.js | 116 ++++++----- packages/sdk/src/response/response.js | 3 +- packages/transport-http/src/http-request.js | 9 +- packages/transport-http/src/send-http.js | 3 + .../src/send-subscribe-events.js | 73 +++++++ .../src/send-subscribe-events.test.js | 102 ++++++++++ packages/transport-http/src/subscribe-ws.js | 36 ++++ packages/transport-http/src/utils.js | 7 + 14 files changed, 450 insertions(+), 166 deletions(-) create mode 100644 packages/sdk/src/build/build-subscribe-events.js create mode 100644 packages/sdk/src/build/build-subscribe-events.test.js create mode 100644 packages/sdk/src/build/build-subscription.js create mode 100644 packages/sdk/src/build/build-subscription.test.js create mode 100644 packages/transport-http/src/send-subscribe-events.js create mode 100644 packages/transport-http/src/send-subscribe-events.test.js create mode 100644 packages/transport-http/src/subscribe-ws.js create mode 100644 packages/transport-http/src/utils.js diff --git a/package-lock.json b/package-lock.json index a99a790d8..a1597fc80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19575,19 +19575,19 @@ }, "packages/config": { "name": "@onflow/config", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.3.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-logger": "^1.3.0-typescript.0", + "@onflow/util-actor": "^1.3.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-logger": "^1.3.0", "eslint": "^8.34.0", "eslint-plugin-jsdoc": "^40.0.0" }, "devDependencies": { "@babel/preset-typescript": "^7.22.11", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/estree": "^1.0.1", "@types/jest": "^29.5.4", "@typescript-eslint/eslint-plugin": "^6.5.0", @@ -19705,27 +19705,27 @@ }, "packages/fcl": { "name": "@onflow/fcl", - "version": "1.8.0-typescript.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.0-typescript.0", + "@onflow/config": "^1.2.0", "@onflow/interaction": "0.0.11", - "@onflow/rlp": "^1.2.0-typescript.0", - "@onflow/sdk": "^1.3.0-typescript.0", - "@onflow/types": "^1.2.0-typescript.0", - "@onflow/util-actor": "^1.3.0-typescript.0", - "@onflow/util-address": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-logger": "^1.3.0-typescript.0", + "@onflow/rlp": "^1.2.0", + "@onflow/sdk": "^1.3.0", + "@onflow/types": "^1.2.0", + "@onflow/util-actor": "^1.3.0", + "@onflow/util-address": "^1.2.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-logger": "^1.3.0", "@onflow/util-semver": "^1.0.0", - "@onflow/util-template": "^1.2.0-typescript.0", - "@onflow/util-uid": "^1.2.0-typescript.0", + "@onflow/util-template": "^1.2.0", + "@onflow/util-uid": "^1.2.0", "cross-fetch": "^3.1.6" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/typedefs": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/typedefs": "^1.2.0", "@types/estree": "^1.0.1", "@types/node": "^18.13.0", "eslint": "^8.35.0", @@ -19761,7 +19761,7 @@ }, "packages/fcl-bundle": { "name": "@onflow/fcl-bundle", - "version": "1.4.0-typescript.0", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@babel/plugin-transform-runtime": "^7.18.2", @@ -19791,25 +19791,25 @@ }, "packages/fcl-wc": { "name": "@onflow/fcl-wc", - "version": "5.0.0-typescript.0", + "version": "5.0.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.9", - "@onflow/config": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-logger": "^1.3.0-typescript.0", + "@onflow/config": "^1.2.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-logger": "^1.3.0", "@walletconnect/modal": "^2.4.7", "@walletconnect/sign-client": "^2.8.1", "@walletconnect/types": "^2.8.1", "@walletconnect/utils": "^2.8.1" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "better-sqlite3": "^7.6.2", "jest": "^29.5.0" }, "peerDependencies": { - "@onflow/fcl": "^1.8.0-typescript.0" + "@onflow/fcl": "^1.8.0" } }, "packages/fcl/node_modules/eslint-plugin-jsdoc": { @@ -20348,7 +20348,7 @@ }, "packages/rlp": { "name": "@onflow/rlp", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "MPL-2.0", "dependencies": { "@babel/runtime": "^7.18.6", @@ -20356,7 +20356,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -20454,25 +20454,25 @@ }, "packages/sdk": { "name": "@onflow/sdk", - "version": "1.3.0-typescript.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.0-typescript.0", - "@onflow/rlp": "^1.2.0-typescript.0", - "@onflow/transport-http": "^1.8.0-typescript.0", - "@onflow/util-actor": "^1.3.0-typescript.0", - "@onflow/util-address": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-logger": "^1.3.0-typescript.0", - "@onflow/util-template": "^1.2.0-typescript.0", + "@onflow/config": "^1.2.0", + "@onflow/rlp": "^1.2.0", + "@onflow/transport-http": "^1.8.0", + "@onflow/util-actor": "^1.3.0", + "@onflow/util-address": "^1.2.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-logger": "^1.3.0", + "@onflow/util-template": "^1.2.0", "deepmerge": "^4.2.2", "sha3": "^2.1.4", "uuid": "^9.0.1" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/typedefs": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/typedefs": "^1.2.0", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", "jest": "^29.5.0", @@ -20560,21 +20560,21 @@ }, "packages/transport-grpc": { "name": "@onflow/transport-grpc", - "version": "1.3.0-typescript.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", "@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web-node-http-transport": "^0.14.0", "@onflow/protobuf": "^1.2.1", - "@onflow/rlp": "^1.2.0-typescript.0", - "@onflow/util-address": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-template": "^1.2.0-typescript.0" + "@onflow/rlp": "^1.2.0", + "@onflow/util-address": "^1.2.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-template": "^1.2.0" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/sdk": "^1.3.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/sdk": "^1.3.0", "jest": "^29.5.0" } }, @@ -20602,34 +20602,34 @@ }, "packages/transport-http": { "name": "@onflow/transport-http", - "version": "1.8.0-typescript.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-address": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0", - "@onflow/util-logger": "^1.3.0-typescript.0", - "@onflow/util-template": "^1.2.0-typescript.0", + "@onflow/util-address": "^1.2.0", + "@onflow/util-invariant": "^1.2.0", + "@onflow/util-logger": "^1.3.0", + "@onflow/util-template": "^1.2.0", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/rlp": "^1.2.0-typescript.0", - "@onflow/sdk": "^1.3.0-typescript.0", - "@onflow/types": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/rlp": "^1.2.0", + "@onflow/sdk": "^1.3.0", + "@onflow/types": "^1.2.0", "jest": "^29.5.0" } }, "packages/typedefs": { "name": "@onflow/typedefs", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/node": "^18.13.0", "eslint": "^8.33.0", "eslint-plugin-jsdoc": "^39.7.5", @@ -20652,15 +20652,15 @@ }, "packages/types": { "name": "@onflow/types", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.0-typescript.0" + "@onflow/util-logger": "^1.3.0" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -20682,21 +20682,6 @@ "node": ">=16" } }, - "packages/types/node_modules/@onflow/util-logger": { - "version": "1.3.0", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.18.6" - }, - "peerDependencies": { - "@onflow/util-config": ">1.1.1" - }, - "peerDependenciesMeta": { - "@onflow/util-config": { - "optional": true - } - } - }, "packages/types/node_modules/comment-parser": { "version": "1.4.0", "dev": true, @@ -20773,7 +20758,7 @@ }, "packages/util-actor": { "name": "@onflow/util-actor", - "version": "1.3.0-typescript.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", @@ -20781,7 +20766,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -20879,15 +20864,15 @@ }, "packages/util-address": { "name": "@onflow/util-address", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/types": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/types": "^1.2.0", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", @@ -20999,17 +20984,17 @@ }, "packages/util-encode-key": { "name": "@onflow/util-encode-key", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/rlp": "^1.2.0-typescript.0", - "@onflow/util-invariant": "^1.2.0-typescript.0" + "@onflow/rlp": "^1.2.0", + "@onflow/util-invariant": "^1.2.0" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/types": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/types": "^1.2.0", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", @@ -21121,15 +21106,15 @@ }, "packages/util-invariant": { "name": "@onflow/util-invariant", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", - "@onflow/types": "^1.2.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", + "@onflow/types": "^1.2.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -21227,14 +21212,14 @@ }, "packages/util-logger": { "name": "@onflow/util-logger", - "version": "1.3.0-typescript.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -21352,15 +21337,15 @@ }, "packages/util-template": { "name": "@onflow/util-template", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.0-typescript.0" + "@onflow/util-logger": "^1.3.0" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -21382,21 +21367,6 @@ "node": ">=16" } }, - "packages/util-template/node_modules/@onflow/util-logger": { - "version": "1.3.0", - "license": "Apache-2.0", - "dependencies": { - "@babel/runtime": "^7.18.6" - }, - "peerDependencies": { - "@onflow/util-config": ">1.1.1" - }, - "peerDependenciesMeta": { - "@onflow/util-config": { - "optional": true - } - } - }, "packages/util-template/node_modules/comment-parser": { "version": "1.4.0", "dev": true, @@ -21473,14 +21443,14 @@ }, "packages/util-uid": { "name": "@onflow/util-uid", - "version": "1.2.0-typescript.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.18.6" }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.0-typescript.0", + "@onflow/fcl-bundle": "^1.4.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/sdk/src/build/build-subscribe-events.js b/packages/sdk/src/build/build-subscribe-events.js new file mode 100644 index 000000000..1f21693c3 --- /dev/null +++ b/packages/sdk/src/build/build-subscribe-events.js @@ -0,0 +1,23 @@ +import {pipe, Ok, makeSubscribeEvents} from "../interaction/interaction.js" + +export function subscribeEvents({ + startBlockId, + startHeight, + eventTypes, + addresses, + contracts, + heartbeatInterval, +}) { + return pipe([ + makeSubscribeEvents, + ix => { + ix.subscribeEvents.startBlockId = startBlockId + ix.subscribeEvents.startHeight = startHeight + ix.subscribeEvents.eventTypes = eventTypes + ix.subscribeEvents.addresses = addresses + ix.subscribeEvents.contracts = contracts + ix.subscribeEvents.heartbeatInterval = heartbeatInterval + return Ok(ix) + }, + ]) +} diff --git a/packages/sdk/src/build/build-subscribe-events.test.js b/packages/sdk/src/build/build-subscribe-events.test.js new file mode 100644 index 000000000..3d4dec385 --- /dev/null +++ b/packages/sdk/src/build/build-subscribe-events.test.js @@ -0,0 +1,26 @@ +import {interaction} from "../interaction/interaction.js" +import {subscribeEvents} from "./build-subscribe-events.js" + +describe("Subscribe Events", () => { + test("Subscribe Events", async () => { + let ix = await subscribeEvents({ + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + })(interaction()) + + expect(ix.subscribeEvents.startBlockId).toBe("abc123") + expect(ix.subscribeEvents.startHeight).toBe(1) + expect(ix.subscribeEvents.eventTypes).toEqual([ + "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", + ]) + expect(ix.subscribeEvents.addresses).toEqual(["0x1", "0x2"]) + expect(ix.subscribeEvents.contracts).toEqual([ + "A.7e60df042a9c0868.FlowToken", + ]) + expect(ix.subscribeEvents.heartbeatInterval).toBe(1000) + }) +}) diff --git a/packages/sdk/src/build/build-subscription.js b/packages/sdk/src/build/build-subscription.js new file mode 100644 index 000000000..63f65b8d8 --- /dev/null +++ b/packages/sdk/src/build/build-subscription.js @@ -0,0 +1,8 @@ +export function subscription({onData, onError, onComplete}) { + return ix => { + ix.subscription.onData = onData + ix.subscription.onError = onError + ix.subscription.onComplete = onComplete + return ix + } +} diff --git a/packages/sdk/src/build/build-subscription.test.js b/packages/sdk/src/build/build-subscription.test.js new file mode 100644 index 000000000..a13d65303 --- /dev/null +++ b/packages/sdk/src/build/build-subscription.test.js @@ -0,0 +1,20 @@ +import {interaction} from "../interaction/interaction.js" +import {subscription} from "./build-subscription.js" + +describe("Build Subscription", () => { + test("build subscription", async () => { + const onData = () => {} + const onError = () => {} + const onComplete = () => {} + + let ix = await subscription({ + onData, + onError, + onComplete, + })(interaction()) + + expect(ix.subscription.onData).toBe(onData) + expect(ix.subscription.onError).toBe(onError) + expect(ix.subscription.onComplete).toBe(onComplete) + }) +}) diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index 0d1199876..c005ea987 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -223,6 +223,8 @@ export const decodeResponse = async (response, customDecoders = {}) => { return { chainId: chainIdMap[response.networkParameters.chainId], } + } else if (response.unsubscribeCallback) { + return response.unsubscribeCallback } return null diff --git a/packages/sdk/src/interaction/interaction.js b/packages/sdk/src/interaction/interaction.js index 703ba18f6..518c5504c 100644 --- a/packages/sdk/src/interaction/interaction.js +++ b/packages/sdk/src/interaction/interaction.js @@ -7,6 +7,7 @@ export const TRANSACTION /* */ = "TRANSACTION" export const GET_TRANSACTION_STATUS /* */ = "GET_TRANSACTION_STATUS" export const GET_ACCOUNT /* */ = "GET_ACCOUNT" export const GET_EVENTS /* */ = "GET_EVENTS" +export const SUBSCRIBE_EVENTS /* */ = "SUBSCRIBE_EVENTS" export const PING /* */ = "PING" export const GET_TRANSACTION /* */ = "GET_TRANSACTION" export const GET_BLOCK /* */ = "GET_BLOCK" @@ -89,6 +90,14 @@ const IX = `{ "end":null, "blockIds":[] }, + "subscribeEvents": { + "startBlockId":null, + "startHeight":null, + "eventTypes":null, + "addresses":null, + "contracts":null, + "heartbeatInterval":null + }, "transaction": { "id":null }, @@ -102,6 +111,11 @@ const IX = `{ }, "collection": { "id":null + }, + "subscription": { + "onData":null, + "onError":null, + "onComplete":null } }` @@ -147,52 +161,54 @@ const prepAccountKeyId = acct => { } } -export const prepAccount = (acct, opts = {}) => ix => { - invariant( - typeof acct === "function" || typeof acct === "object", - "prepAccount must be passed an authorization function or an account object" - ) - invariant(opts.role != null, "Account must have a role") - - const ACCOUNT = JSON.parse(ACCT) - const role = opts.role - const tempId = uuidv4() - - if (acct.authorization && isFn(acct.authorization)) - acct = {resolve: acct.authorization} - if (!acct.authorization && isFn(acct)) acct = {resolve: acct} - - const resolve = acct.resolve - if (resolve) - acct.resolve = (acct, ...rest) => - [resolve, prepAccountKeyId].reduce( - async (d, fn) => fn(await d, ...rest), - acct - ) - acct = prepAccountKeyId(acct) - - ix.accounts[tempId] = { - ...ACCOUNT, - tempId, - ...acct, - role: { - ...ACCOUNT.role, - ...(typeof acct.role === "object" ? acct.role : {}), - [role]: true, - }, - } - - if (role === AUTHORIZER) { - ix.authorizations.push(tempId) - } else if (role === PAYER) { - ix.payer.push(tempId) - } else { - ix[role] = tempId +export const prepAccount = + (acct, opts = {}) => + ix => { + invariant( + typeof acct === "function" || typeof acct === "object", + "prepAccount must be passed an authorization function or an account object" + ) + invariant(opts.role != null, "Account must have a role") + + const ACCOUNT = JSON.parse(ACCT) + const role = opts.role + const tempId = uuidv4() + + if (acct.authorization && isFn(acct.authorization)) + acct = {resolve: acct.authorization} + if (!acct.authorization && isFn(acct)) acct = {resolve: acct} + + const resolve = acct.resolve + if (resolve) + acct.resolve = (acct, ...rest) => + [resolve, prepAccountKeyId].reduce( + async (d, fn) => fn(await d, ...rest), + acct + ) + acct = prepAccountKeyId(acct) + + ix.accounts[tempId] = { + ...ACCOUNT, + tempId, + ...acct, + role: { + ...ACCOUNT.role, + ...(typeof acct.role === "object" ? acct.role : {}), + [role]: true, + }, + } + + if (role === AUTHORIZER) { + ix.authorizations.push(tempId) + } else if (role === PAYER) { + ix.payer.push(tempId) + } else { + ix[role] = tempId + } + + return ix } - return ix -} - export const makeArgument = arg => ix => { let tempId = uuidv4() ix.message.arguments.push(tempId) @@ -217,6 +233,7 @@ export const makeGetTransactionStatus /* */ = makeIx(GET_TRANSACTION_STATUS) export const makeGetTransaction /* */ = makeIx(GET_TRANSACTION) export const makeGetAccount /* */ = makeIx(GET_ACCOUNT) export const makeGetEvents /* */ = makeIx(GET_EVENTS) +export const makeSubscribeEvents /* */ = makeIx(SUBSCRIBE_EVENTS) export const makePing /* */ = makeIx(PING) export const makeGetBlock /* */ = makeIx(GET_BLOCK) export const makeGetBlockHeader /* */ = makeIx(GET_BLOCK_HEADER) @@ -232,6 +249,7 @@ export const isGetTransactionStatus /* */ = is(GET_TRANSACTION_STATUS) export const isGetTransaction /* */ = is(GET_TRANSACTION) export const isGetAccount /* */ = is(GET_ACCOUNT) export const isGetEvents /* */ = is(GET_EVENTS) +export const isSubscribeEvents /* */ = is(SUBSCRIBE_EVENTS) export const isPing /* */ = is(PING) export const isGetBlock /* */ = is(GET_BLOCK) export const isGetBlockHeader /* */ = is(GET_BLOCK_HEADER) @@ -287,10 +305,12 @@ export const put = (key, value) => ix => { return Ok(ix) } -export const update = (key, fn = identity) => ix => { - ix.assigns[key] = fn(ix.assigns[key], ix) - return Ok(ix) -} +export const update = + (key, fn = identity) => + ix => { + ix.assigns[key] = fn(ix.assigns[key], ix) + return Ok(ix) + } export const destroy = key => ix => { delete ix.assigns[key] diff --git a/packages/sdk/src/response/response.js b/packages/sdk/src/response/response.js index e5e19e385..2d5a774c0 100644 --- a/packages/sdk/src/response/response.js +++ b/packages/sdk/src/response/response.js @@ -10,7 +10,8 @@ const DEFAULT_RESPONSE = `{ "blockHeader":null, "latestBlock":null, "collection":null, - "networkParameters":null + "networkParameters":null, + "unsubscribeCallback":null, }` export const response = () => JSON.parse(DEFAULT_RESPONSE) diff --git a/packages/transport-http/src/http-request.js b/packages/transport-http/src/http-request.js index 04805d20a..397d1d841 100644 --- a/packages/transport-http/src/http-request.js +++ b/packages/transport-http/src/http-request.js @@ -1,5 +1,6 @@ import * as logger from "@onflow/util-logger" import fetchTransport from "cross-fetch" +import {safeParseJSON} from "./utils" const AbortController = globalThis.AbortController || require("abort-controller") @@ -161,11 +162,3 @@ export async function httpRequest({ // Keep retrying request until server available or max attempts exceeded return await requestLoop() } - -function safeParseJSON(data) { - try { - return JSON.parse(data) - } catch { - return null - } -} diff --git a/packages/transport-http/src/send-http.js b/packages/transport-http/src/send-http.js index ce26a5acf..f4334f88e 100644 --- a/packages/transport-http/src/send-http.js +++ b/packages/transport-http/src/send-http.js @@ -5,6 +5,7 @@ import {sendGetTransaction} from "./send-get-transaction.js" import {sendExecuteScript} from "./send-execute-script.js" import {sendGetAccount} from "./send-get-account.js" import {sendGetEvents} from "./send-get-events.js" +import {sendSubscribeEvents} from "./send-subscribe-events.js" import {sendGetBlock} from "./send-get-block.js" import {sendGetBlockHeader} from "./send-get-block-header.js" import {sendGetCollection} from "./send-get-collection.js" @@ -34,6 +35,8 @@ export const send = async (ix, context = {}, opts = {}) => { return opts.sendGetAccount ? opts.sendGetAccount(ix, context, opts) : sendGetAccount(ix, context, opts) case context.ix.isGetEvents(ix): return opts.sendGetEvents ? opts.sendGetEvents(ix, context, opts) : sendGetEvents(ix, context, opts) + case context.ix.isSubscribeEvents(ix): + return opts.sendSubscribeEvents ? opts.sendSubscribeEvents(ix, context, opts) : sendSubscribeEvents(ix, context, opts) case context.ix.isGetBlock(ix): return opts.sendGetBlock ? opts.sendGetBlock(ix, context, opts) : sendGetBlock(ix, context, opts) case context.ix.isGetBlockHeader(ix): diff --git a/packages/transport-http/src/send-subscribe-events.js b/packages/transport-http/src/send-subscribe-events.js new file mode 100644 index 000000000..4e3f27aad --- /dev/null +++ b/packages/transport-http/src/send-subscribe-events.js @@ -0,0 +1,73 @@ +import {invariant} from "@onflow/util-invariant" +import {subscribeWs as defaultSubscribeWs} from "./subscribe-ws" + +function constructData(ix, context, data) { + let ret = context.response() + ret.tag = ix.tag + + ret.events = data.events + ? data.events.map(event => ({ + blockId: data.BlockId, + blockHeight: Number(data.Height), + blockTimestamp: data.Timestamp, + type: event.Type, + transactionId: event.TransactionId, + transactionIndex: Number(event.TransactionIndex), + eventIndex: Number(event.EventIndex), + payload: JSON.parse( + context.Buffer.from(event.Payload, "base64").toString() + ), + })) + : [] + + return ret +} + +function constructResponse(ix, context, callback) { + let ret = context.response() + ret.tag = ix.tag + + ret.unsubscribeCallback = callback + + return ret +} + +export async function sendSubscribeEvents(ix, context = {}, opts = {}) { + invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`) + invariant( + context.response, + `SDK Send Get Events Error: context.response must be defined.` + ) + invariant( + context.Buffer, + `SDK Send Get Events Error: context.Buffer must be defined.` + ) + + ix = await ix + + const subscribeWs = opts.subscribeWs || defaultSubscribeWs + const {onData, onError, onComplete} = ix.subscription + + const unsubscribe = subscribeWs({ + hostname: opts.node, + path: `/v1/subscribe_events`, + params: { + start_block_id: ix.subscribeEvents.startBlockId, + start_height: ix.subscribeEvents.startHeight, + event_types: ix.subscribeEvents.eventTypes, + addresses: ix.subscribeEvents.addresses, + contracts: ix.subscribeEvents.contracts, + heartbeat_interval: ix.subscribeEvents.heartbeatInterval, + }, + onData: handleData, + onError, + onComplete, + }) + + function handleData(data) { + const resp = constructData(ix, context, data) + onData(resp) + } + + return constructResponse(ix, context, unsubscribe) +} diff --git a/packages/transport-http/src/send-subscribe-events.test.js b/packages/transport-http/src/send-subscribe-events.test.js new file mode 100644 index 000000000..078db3d08 --- /dev/null +++ b/packages/transport-http/src/send-subscribe-events.test.js @@ -0,0 +1,102 @@ +import {sendSubscribeEvents} from "./send-subscribe-events" +import {Buffer} from "@onflow/rlp" +import { + build, + subscribeEvents, + subscription, + resolve, + response as responseADT, +} from "@onflow/sdk" + +describe("Subscribe Events", () => { + let subscribeWsMock + + let onData + let onError + let onComplete + + let unsubscribe + let response + + beforeEach(async () => { + subscribeWsMock = jest.fn() + + onData = jest.fn() + onError = jest.fn() + onComplete = jest.fn() + + unsubscribe = jest.fn() + + subscribeWsMock.mockReturnValue({ + unsubscribeCallback: unsubscribe, + }) + + const response = await sendSubscribeEvents( + await resolve( + await build([ + subscribeEvents({ + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + }), + subscription({ + onData, + onError, + onComplete, + }), + ]) + ), + { + response: responseADT, + Buffer, + }, + { + subscribeWs: subscribeWsMock, + node: "localhost", + } + ) + }) + + test("calls subscribeWs with correct params", async () => { + expect(subscribeWsMock.mock.calls.length).toEqual(1) + + const httpRequestMockArgs = httpRequestMock.mock.calls[0] + + expect(httpRequestMockArgs.length).toEqual(1) + + const valueSent = httpRequestMock.mock.calls[0][0] + + expect(valueSent.hostname).toBe("localhost") + expect(valueSent.path).toBe("/v1/subscribe_events") + expect(valueSent.params).toEqual({ + start_block_id: "abc123", + start_height: 1, + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1000, + }) + }) + + test("calls onData with response ADT formatted data", async () => { + const data = {foo: "bar"} + + // Similate WS message + subscribeWsMock.mock.calls[0][0].onData(data) + + expect(onData.mock.calls.length).toEqual(1) + expect(onData.mock.calls[0][0]).toEqual({ + tag: "SubscribeEvents", + data: data, + }) + }) + + test("calling response.unsubscribeCallback calls subscribeWs unsubscribe", async () => { + response.unsubscribeCallback() + + expect(unsubscribe.mock.calls.length).toEqual(1) + }) +}) diff --git a/packages/transport-http/src/subscribe-ws.js b/packages/transport-http/src/subscribe-ws.js new file mode 100644 index 000000000..bd6ce893f --- /dev/null +++ b/packages/transport-http/src/subscribe-ws.js @@ -0,0 +1,36 @@ +import {safeParseJSON} from "./utils" + +// TODO: Implement retries +export function subscribeWs({ + hostname, + path, + params, + onData, + onClose, + onError, + retryLimit = 5, + retryIntervalMs = 1000, +}) { + const url = new URL(path, hostname) + url.search = new URLSearchParams(params) + var ws = new WebSocket(url) + + ws.onmessage = function (e) { + const data = safeParseJSON(e.data) + if (data) { + onData(data) + } + } + + ws.onclose = function (e) { + onClose(e) + } + + ws.onerror = function (err) { + onError(err) + } + + return () => { + ws.close() + } +} diff --git a/packages/transport-http/src/utils.js b/packages/transport-http/src/utils.js new file mode 100644 index 000000000..75fc84ace --- /dev/null +++ b/packages/transport-http/src/utils.js @@ -0,0 +1,7 @@ +export function safeParseJSON(data) { + try { + return JSON.parse(data) + } catch { + return null + } +} From bc260f6ef0779d25fb23e8e095e4fb0a41ac810b Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 20 Nov 2023 17:08:02 -0800 Subject: [PATCH 02/54] Add pubsub & fcl.events implementation --- package-lock.json | 1674 +++++++++-------- packages/fcl/src/events/index-experimental.ts | 333 ++++ packages/fcl/src/events/index.ts | 116 ++ .../generate-template-id.js | 2 +- .../generate-template-interface-id.js | 2 +- packages/sdk/src/interaction/interaction.js | 5 - packages/sdk/src/sdk.ts | 1 + packages/sdk/tsconfig.json | 3 +- packages/transport-http/package.json | 1 + .../src/send-subscribe-events.js | 23 +- .../src/send-subscribe-events.test.js | 9 - packages/transport-http/src/subscribe-ws.js | 36 - packages/transport-http/src/subscribe-ws.ts | 46 + packages/util-pubsub/.babelrc | 7 + packages/util-pubsub/package.json | 30 + packages/util-pubsub/src/data-stream.test.ts | 65 + packages/util-pubsub/src/data-stream.ts | 36 + packages/util-pubsub/src/index.ts | 2 + packages/util-pubsub/src/pub-sub.test.ts | 88 + packages/util-pubsub/src/pub-sub.ts | 89 + packages/util-pubsub/tsconfig.json | 11 + 21 files changed, 1765 insertions(+), 814 deletions(-) create mode 100644 packages/fcl/src/events/index-experimental.ts create mode 100644 packages/fcl/src/events/index.ts delete mode 100644 packages/transport-http/src/subscribe-ws.js create mode 100644 packages/transport-http/src/subscribe-ws.ts create mode 100644 packages/util-pubsub/.babelrc create mode 100644 packages/util-pubsub/package.json create mode 100644 packages/util-pubsub/src/data-stream.test.ts create mode 100644 packages/util-pubsub/src/data-stream.ts create mode 100644 packages/util-pubsub/src/index.ts create mode 100644 packages/util-pubsub/src/pub-sub.test.ts create mode 100644 packages/util-pubsub/src/pub-sub.ts create mode 100644 packages/util-pubsub/tsconfig.json diff --git a/package-lock.json b/package-lock.json index a1597fc80..c0ac2db4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1546,10 +1546,11 @@ "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.21.0", - "license": "MIT", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -1608,8 +1609,9 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true }, "node_modules/@changesets/apply-release-plan": { "version": "6.1.4", @@ -1942,30 +1944,6 @@ "prettier": "^2.7.1" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "dev": true, @@ -2229,15 +2207,16 @@ } }, "node_modules/@jest/console": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -2246,8 +2225,9 @@ }, "node_modules/@jest/console/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2260,8 +2240,9 @@ }, "node_modules/@jest/console/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2275,8 +2256,9 @@ }, "node_modules/@jest/console/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2286,21 +2268,24 @@ }, "node_modules/@jest/console/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@jest/console/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jest/console/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2309,36 +2294,37 @@ } }, "node_modules/@jest/core": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -2356,8 +2342,9 @@ }, "node_modules/@jest/core/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2370,8 +2357,9 @@ }, "node_modules/@jest/core/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2385,8 +2373,9 @@ }, "node_modules/@jest/core/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2396,84 +2385,24 @@ }, "node_modules/@jest/core/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/core/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@jest/core/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/@jest/core/node_modules/jest-config": { - "version": "29.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, "node_modules/@jest/core/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2482,83 +2411,89 @@ } }, "node_modules/@jest/environment": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "license": "MIT", "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "license": "MIT", "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, - "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", @@ -2566,13 +2501,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -2592,8 +2527,9 @@ }, "node_modules/@jest/reporters/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2606,8 +2542,9 @@ }, "node_modules/@jest/reporters/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2621,8 +2558,9 @@ }, "node_modules/@jest/reporters/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2632,13 +2570,15 @@ }, "node_modules/@jest/reporters/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@jest/reporters/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2656,16 +2596,61 @@ }, "node_modules/@jest/reporters/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@jest/reporters/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2674,22 +2659,24 @@ } }, "node_modules/@jest/schemas": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/source-map": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", + "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" }, @@ -2698,12 +2685,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -2712,13 +2700,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/test-result": "^29.5.0", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -2726,21 +2715,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -2820,11 +2810,12 @@ } }, "node_modules/@jest/types": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -2949,11 +2940,12 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "license": "MIT", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lerna/child-process": { @@ -4661,6 +4653,10 @@ "resolved": "packages/util-logger", "link": true }, + "node_modules/@onflow/util-pubsub": { + "resolved": "packages/util-pubsub", + "link": true + }, "node_modules/@onflow/util-semver": { "resolved": "packages/util-semver", "link": true @@ -4899,9 +4895,10 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "dev": true, - "license": "MIT" + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true }, "node_modules/@sinonjs/commons": { "version": "3.0.0", @@ -5055,34 +5052,6 @@ "node": ">= 10" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/@tufjs/canonical-json": { "version": "1.0.0", "dev": true, @@ -5185,9 +5154,10 @@ "license": "MIT" }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5271,11 +5241,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "dev": true, - "license": "MIT" - }, "node_modules/@types/resolve": { "version": "1.20.2", "license": "MIT" @@ -6243,8 +6208,9 @@ }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -6278,13 +6244,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/argparse": { "version": "1.0.10", "dev": true, @@ -6386,14 +6345,15 @@ } }, "node_modules/babel-jest": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/transform": "^29.5.0", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -6485,9 +6445,10 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -6561,11 +6522,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, - "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -6796,8 +6758,9 @@ }, "node_modules/bser": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -6983,8 +6946,9 @@ }, "node_modules/char-regex": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -7025,9 +6989,10 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "dev": true, - "license": "MIT" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true }, "node_modules/clean-stack": { "version": "2.2.0", @@ -7122,17 +7087,19 @@ }, "node_modules/co": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, - "license": "MIT", "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "dev": true, - "license": "MIT" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true }, "node_modules/color-convert": { "version": "1.9.3", @@ -7930,21 +7897,105 @@ "node": ">=10" } }, - "node_modules/create-require": { - "version": "1.1.1", + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/cross-fetch": { - "version": "3.1.6", - "license": "MIT", "dependencies": { - "node-fetch": "^2.6.11" - } - }, - "node_modules/cross-spawn": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/create-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.6", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.11" + } + }, + "node_modules/cross-spawn": { "version": "7.0.3", "dev": true, "license": "MIT", @@ -8255,16 +8306,18 @@ }, "node_modules/detect-newline": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/diff-sequences": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -8381,8 +8434,9 @@ }, "node_modules/emittery": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -9055,6 +9109,8 @@ }, "node_modules/exit": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -9069,15 +9125,16 @@ } }, "node_modules/expect": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -9161,8 +9218,9 @@ }, "node_modules/fb-watchman": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } @@ -10150,13 +10208,6 @@ "dev": true, "license": "MIT" }, - "node_modules/growly": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/handlebars": { "version": "4.7.7", "dev": true, @@ -10307,8 +10358,9 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true }, "node_modules/http-cache-semantics": { "version": "4.1.1", @@ -10823,8 +10875,9 @@ }, "node_modules/is-generator-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -11146,30 +11199,75 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/istanbul-lib-report/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11179,8 +11277,9 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -11191,9 +11290,10 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -11301,14 +11401,15 @@ } }, "node_modules/jest": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.5.0" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -11326,11 +11427,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "license": "MIT", "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -11339,8 +11442,9 @@ }, "node_modules/jest-changed-files/node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -11352,27 +11456,28 @@ } }, "node_modules/jest-circus": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -11383,8 +11488,9 @@ }, "node_modules/jest-circus/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -11397,8 +11503,9 @@ }, "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11412,8 +11519,9 @@ }, "node_modules/jest-circus/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -11423,21 +11531,38 @@ }, "node_modules/jest-circus/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-circus/node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", "dev": true, - "license": "MIT" + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/jest-circus/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-circus/node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -11450,8 +11575,9 @@ }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11460,21 +11586,21 @@ } }, "node_modules/jest-cli": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -11494,8 +11620,9 @@ }, "node_modules/jest-cli/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -11508,8 +11635,9 @@ }, "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11523,8 +11651,9 @@ }, "node_modules/jest-cli/node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -11536,8 +11665,9 @@ }, "node_modules/jest-cli/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -11547,61 +11677,84 @@ }, "node_modules/jest-cli/node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=8" + } }, - "node_modules/jest-cli/node_modules/glob": { - "version": "7.2.3", + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=12" } }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/jest-cli/node_modules/jest-config": { - "version": "29.5.0", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -11621,51 +11774,106 @@ } } }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-cli/node_modules/yargs-parser": { - "version": "21.1.1", + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "ISC", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=12" + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jest-diff": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11673,8 +11881,9 @@ }, "node_modules/jest-diff/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -11687,8 +11896,9 @@ }, "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11702,8 +11912,9 @@ }, "node_modules/jest-diff/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -11713,21 +11924,24 @@ }, "node_modules/jest-diff/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11736,9 +11950,10 @@ } }, "node_modules/jest-docblock": { - "version": "29.4.3", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" }, @@ -11747,15 +11962,16 @@ } }, "node_modules/jest-each": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11763,8 +11979,9 @@ }, "node_modules/jest-each/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -11777,8 +11994,9 @@ }, "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11792,8 +12010,9 @@ }, "node_modules/jest-each/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -11803,21 +12022,24 @@ }, "node_modules/jest-each/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-each/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-each/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11852,16 +12074,17 @@ } }, "node_modules/jest-environment-node": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11877,27 +12100,29 @@ } }, "node_modules/jest-get-type": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -11909,26 +12134,28 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, - "license": "MIT", "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11999,17 +12226,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -12082,13 +12310,14 @@ } }, "node_modules/jest-mock": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.5.0" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12096,8 +12325,9 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -12111,24 +12341,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -12138,12 +12370,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "license": "MIT", "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12151,8 +12384,9 @@ }, "node_modules/jest-resolve/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12165,8 +12399,9 @@ }, "node_modules/jest-resolve/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12180,8 +12415,9 @@ }, "node_modules/jest-resolve/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12191,21 +12427,24 @@ }, "node_modules/jest-resolve/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-resolve/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-resolve/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12214,29 +12453,30 @@ } }, "node_modules/jest-runner": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -12246,8 +12486,9 @@ }, "node_modules/jest-runner/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12260,8 +12501,9 @@ }, "node_modules/jest-runner/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12275,8 +12517,9 @@ }, "node_modules/jest-runner/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12286,21 +12529,24 @@ }, "node_modules/jest-runner/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-runner/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-runner/node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -12313,8 +12559,9 @@ }, "node_modules/jest-runner/node_modules/source-map-support": { "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -12322,8 +12569,9 @@ }, "node_modules/jest-runner/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12332,30 +12580,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -12365,8 +12614,9 @@ }, "node_modules/jest-runtime/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12379,8 +12629,9 @@ }, "node_modules/jest-runtime/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12394,8 +12645,9 @@ }, "node_modules/jest-runtime/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12405,13 +12657,15 @@ }, "node_modules/jest-runtime/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-runtime/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -12429,16 +12683,18 @@ }, "node_modules/jest-runtime/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-runtime/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12447,33 +12703,31 @@ } }, "node_modules/jest-snapshot": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.5.0", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12481,8 +12735,9 @@ }, "node_modules/jest-snapshot/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12495,8 +12750,9 @@ }, "node_modules/jest-snapshot/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12510,8 +12766,9 @@ }, "node_modules/jest-snapshot/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12521,21 +12778,24 @@ }, "node_modules/jest-snapshot/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-snapshot/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-snapshot/node_modules/lru-cache": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -12544,9 +12804,10 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.1", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -12559,8 +12820,9 @@ }, "node_modules/jest-snapshot/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12569,11 +12831,12 @@ } }, "node_modules/jest-util": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -12649,16 +12912,17 @@ } }, "node_modules/jest-validate": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.5.0" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12666,8 +12930,9 @@ }, "node_modules/jest-validate/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12680,8 +12945,9 @@ }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -12691,8 +12957,9 @@ }, "node_modules/jest-validate/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12706,8 +12973,9 @@ }, "node_modules/jest-validate/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12717,21 +12985,24 @@ }, "node_modules/jest-validate/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-validate/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-validate/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12740,17 +13011,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -12759,8 +13031,9 @@ }, "node_modules/jest-watcher/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12773,8 +13046,9 @@ }, "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12788,8 +13062,9 @@ }, "node_modules/jest-watcher/node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12799,21 +13074,24 @@ }, "node_modules/jest-watcher/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/jest-watcher/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-watcher/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12822,12 +13100,13 @@ } }, "node_modules/jest-worker": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -12837,16 +13116,18 @@ }, "node_modules/jest-worker/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -13058,8 +13339,9 @@ }, "node_modules/kleur": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -13487,8 +13769,9 @@ }, "node_modules/leven": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -14134,8 +14417,9 @@ }, "node_modules/makeerror": { "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -14819,52 +15103,9 @@ }, "node_modules/node-int64": { "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-notifier": { - "version": "8.0.2", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - } - }, - "node_modules/node-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-notifier/node_modules/semver": { - "version": "7.5.1", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true }, "node_modules/node-releases": { "version": "2.0.10", @@ -14925,8 +15166,9 @@ }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16472,11 +16714,12 @@ } }, "node_modules/pretty-format": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -16555,8 +16798,9 @@ }, "node_modules/prompts": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "license": "MIT", "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -16620,7 +16864,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, "funding": [ { @@ -16631,8 +16877,7 @@ "type": "opencollective", "url": "https://opencollective.com/fast-check" } - ], - "license": "MIT" + ] }, "node_modules/q": { "version": "1.5.1", @@ -17126,8 +17371,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "license": "MIT" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regenerator-transform": { "version": "0.15.1", @@ -17235,8 +17481,9 @@ }, "node_modules/resolve.exports": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -17546,13 +17793,6 @@ "node": ">=8" } }, - "node_modules/shellwords": { - "version": "0.1.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/side-channel": { "version": "1.0.4", "dev": true, @@ -17632,8 +17872,9 @@ }, "node_modules/sisteransi": { "version": "1.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true }, "node_modules/slash": { "version": "3.0.0", @@ -18016,8 +18257,9 @@ }, "node_modules/string-length": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "license": "MIT", "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -18382,8 +18624,9 @@ }, "node_modules/tmpl": { "version": "1.0.5", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true }, "node_modules/to-fast-properties": { "version": "2.0.0", @@ -18538,60 +18781,6 @@ "node": ">=12" } }, - "node_modules/ts-node": { - "version": "10.9.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/ts-protoc-gen": { "version": "0.12.0", "dev": true, @@ -19040,26 +19229,26 @@ "dev": true, "license": "MIT" }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", "dev": true, - "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "dev": true, @@ -19117,8 +19306,9 @@ }, "node_modules/walker": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } @@ -19552,16 +19742,6 @@ "node": ">=10" } }, - "node_modules/yn": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, @@ -20609,6 +20789,7 @@ "@onflow/util-address": "^1.2.0", "@onflow/util-invariant": "^1.2.0", "@onflow/util-logger": "^1.3.0", + "@onflow/util-pubsub": "^1.0.0", "@onflow/util-template": "^1.2.0", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6" @@ -21323,6 +21504,17 @@ "node": ">=10" } }, + "packages/util-pubsub": { + "name": "@onflow/util-pubsub", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.23.2" + }, + "devDependencies": { + "jest": "^29.7.0" + } + }, "packages/util-semver": { "name": "@onflow/util-semver", "version": "1.0.0", diff --git a/packages/fcl/src/events/index-experimental.ts b/packages/fcl/src/events/index-experimental.ts new file mode 100644 index 000000000..e0c189bd0 --- /dev/null +++ b/packages/fcl/src/events/index-experimental.ts @@ -0,0 +1,333 @@ +import {spawn, subscriber, SUBSCRIBE, UNSUBSCRIBE} from "@onflow/util-actor" +import { + config, + block, + getEventsAtBlockHeightRange, + send, + decode, + subscribeEvents, + resolve, +} from "@onflow/sdk" +import { + Subscribable, + Subscription, + Subscriber, + DataStream, +} from "@onflow/util-pubsub" + +/* +ix.subscribeEvents.startBlockId = startBlockId +ix.subscribeEvents.startHeight = startHeight +ix.subscribeEvents.eventTypes = eventTypes +ix.subscribeEvents.addresses = addresses +ix.subscribeEvents.contracts = contracts +ix.subscribeEvents.heartbeatInterval = heartbeatInterval +*/ +type EventTypeFilter = { + eventTypes?: string[] | string + addresses?: string[] | string + contracts?: string[] | string +} + +type NormalizedEventTypeFilter = { + eventTypes: string[] + addresses: string[] + contracts: string[] +} + +const RATE = 10000 +const UPDATED = "UPDATED" +const TICK = "TICK" +const HIGH_WATER_MARK = "hwm" + +const scheduleTick = async ctx => { + return setTimeout( + () => ctx.sendSelf(TICK), + await config().get("fcl.eventPollRate", RATE) + ) +} + +function mergeFilters( + a: NormalizedEventTypeFilter, + b: NormalizedEventTypeFilter +): NormalizedEventTypeFilter { + return { + eventTypes: Array.from(new Set([...a.eventTypes, ...b.eventTypes])), + addresses: Array.from(new Set([...a.addresses, ...b.addresses])), + contracts: Array.from(new Set([...a.contracts, ...b.contracts])), + } +} + +function normalizeEventTypeFilter(filterOrType?: EventTypeFilter | string) { + // Normalize the filter to arrays + let filter: NormalizedEventTypeFilter + if (typeof filterOrType === "string") { + return { + eventTypes: [filterOrType], + addresses: [], + contracts: [], + } + } else if (filterOrType == null) { + return { + eventTypes: [], + addresses: [], + contracts: [], + } + } else { + const {eventTypes, addresses, contracts} = filterOrType + filter = { + eventTypes: Array.isArray(eventTypes) ? eventTypes : [eventTypes], + addresses: Array.isArray(addresses) ? addresses : [addresses], + contracts: Array.isArray(contracts) ? contracts : [contracts], + } + } + + // If any of the filters are empty, we need to add a wildcard for tracking + Object.entries(filter).forEach(([key, identifiers]) => { + if (identifiers.length === 0) filter[key].push(WILD_CARD_IDENTIFIER) + }) + + return filter +} + +const WILD_CARD_IDENTIFIER = "*" + +class FilterCounter { + private counts: Map = new Map() + + constructor() {} + + private get(key: string) { + return this.counts.get(key) ?? 0 + } + + increment(key: string) { + const count = this.get(key) + 1 + this.counts.set(key, count) + } + + decrement(key: string) { + const count = this.get(key) - 1 + if (count === 0) this.counts.delete(key) + else this.counts.set(key, count) + } + + getFiltersIdentifiers() { + if (this.counts.get(WILD_CARD_IDENTIFIER) != null) return [] + return Array.from(this.counts.keys()) + } +} + +class FilterManager { + private state: Record = { + eventTypes: new FilterCounter(), + addresses: new FilterCounter(), + contracts: new FilterCounter(), + } + + constructor() {} + + get filter(): NormalizedEventTypeFilter { + return { + eventTypes: this.state.eventTypes.getFiltersIdentifiers(), + addresses: this.state.addresses.getFiltersIdentifiers(), + contracts: this.state.contracts.getFiltersIdentifiers(), + } + } + + addFilter(filter: NormalizedEventTypeFilter) { + Object.keys(filter).forEach(key => { + const identifiers = filter[key as keyof NormalizedEventTypeFilter] + identifiers.forEach(identifier => { + this.state[key as keyof NormalizedEventTypeFilter].increment(identifier) + }) + }) + } + + removeFilter(filter: NormalizedEventTypeFilter) { + Object.keys(filter).forEach(key => { + const identifiers = filter[key as keyof NormalizedEventTypeFilter] + identifiers.forEach(identifier => { + this.state[key as keyof NormalizedEventTypeFilter].decrement(identifier) + }) + }) + } +} + +type Event = { + type: string + address: string + contract: string + height: number + data: any +} + +/** + * The event subscription pool is used to create a single event stream for all subscribers. + * It determines the minimum set of filters needed to satisfy all subscribers, manages the + * event stream, and dispatches events to subscribers. + */ +class EventSubscriptionPool { + // List of subscribers + private subscribers: [Subscriber, earliestStreamId: number][] = [] + // For each normalized filter key they is a map of indentifier to count + private filterManager = new FilterManager() + + // Track old event streams so we can close them when they are no longer needed + // Streams are allowed to persist until they reach a block a block that is + // processed by a later stream + private streams: { + stream: DataStream + lowWaterMark: number + highWaterMark: number + }[] = [] + private streamId = 0 + // handle new events queued while transitioning to a new event stream + private queuedEvents: Event[] = [] + + subscribe( + filterOrType: EventTypeFilter | string, + cb: (event: Event, error: any) => void + ): Subscription { + // Add the filter to the filter manager + const subscriberFilter = normalizeEventTypeFilter(filterOrType) + this.filterManager.addFilter(subscriberFilter) + + function onEvent(event: Event) { + if (subscriberFilter.eventTypes.length > 0) { + if ( + !subscriberFilter.eventTypes.includes(event.type) && + !subscriberFilter.eventTypes.includes(WILD_CARD_IDENTIFIER) + ) + return + } + if (subscriberFilter.addresses.length > 0) { + if ( + !subscriberFilter.addresses.includes(event.address) && + !subscriberFilter.addresses.includes(WILD_CARD_IDENTIFIER) + ) + return + } + if (subscriberFilter.contracts.length > 0) { + if ( + !subscriberFilter.contracts.includes(event.contract) && + !subscriberFilter.contracts.includes(WILD_CARD_IDENTIFIER) + ) + return + } + cb(event, null) + } + + function onError(error: any) { + cb(null, error) + } + + const subscriber = {next: onEvent, error: onError} + this.subscribers.push([subscriber, this.streamId]) + + return { + unsubscribe: () => { + const index = this.subscribers.findIndex( + ([_subscriber, _]) => _subscriber === subscriber + ) + if (index > -1) { + this.subscribers.splice(index, 1) + this.filterManager.removeFilter(subscriberFilter) + } + }, + } + } + + private refreshSocket() { + // If there are no subscribers, close the event stream + if (this.subscribers.length === 0) { + this.streams.forEach(({stream}) => stream?.close()) + this.streams = [] + return + } + + const filter = this.filterManager.filter + + // Close the current event stream + // We need a grace period to ensure there is no gap in events + const oldStreamId = this.streamId + setTimeout(() => { + this.removeStream(oldStreamId) + }, 5000) + this.streamId++ + + const currentStreamId = this.streamId + // Subscribe to the new event stream + const newStream = resolve(send([subscribeEvents(filter)])) + this.eventStream.subscribe( + event => { + if (this.highWaterMark.blockHeight >= event.height) { + if (this.highWaterMark.streamId > currentStreamId) { + } + } + + this.highWaterMark = [event.height, this.streamId] + this.subscribers.forEach(([subscriber, earliestStreamId]) => { + if (earliestStreamId <= this.highWaterMark[1]) { + subscriber.next(event) + } + }) + }, + error => { + this.subscribers.forEach(subscriber => subscriber.error(error)) + this.refreshSocket() + }, + () => { + this.refreshSocket() + } + ) + } + + private removeStream(streamId: number) { + const index = this.streams.findIndex( + ([, streamId]) => streamId === streamId + ) + if (index > -1) { + this.streams[index]?.[0]?.close() + this.streams.splice(index, 1) + } + } +} + +const EVENT_SUBSCRIPTION_POOL = new EventSubscriptionPool() + +/** + * @typedef {import("@onflow/typedefs").Event} Event + */ + +/** + * @typedef {object} SubscribeObject + * @property {Function} subscribe - The subscribe function. + */ + +/** + * @callback SubscriptionCallback + * @returns {Event} + */ + +/** + * @description - Subscribe to events + * @param {string} key - A valid event name + * @returns {SubscribeObject} + * + * @example + * import * as fcl from "@onflow/fcl" + * fcl.events(eventName).subscribe((event) => console.log(event)) + */ +export function events(key) { + return { + /** + * @description - Subscribe to events + * @param {Function} callback - The callback function + * @returns {SubscriptionCallback} + */ + subscribe: callback => { + return EVENT_SUBSCRIPTION_POOL.subscribe(key, callback) + }, + } +} diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts new file mode 100644 index 000000000..2fe073e19 --- /dev/null +++ b/packages/fcl/src/events/index.ts @@ -0,0 +1,116 @@ +import {config, send, decode, subscribeEvents} from "@onflow/sdk" +import {DataStream} from "@onflow/util-pubsub" +import {Event} from "@onflow/typedefs" + +type EventTypeFilter = { + eventTypes?: string[] | string + addresses?: string[] | string + contracts?: string[] | string +} + +type NormalizedEventTypeFilter = { + eventTypes: string[] + addresses: string[] + contracts: string[] +} + +const RATE = 10000 +const UPDATED = "UPDATED" +const TICK = "TICK" +const HIGH_WATER_MARK = "hwm" + +const scheduleTick = async ctx => { + return setTimeout( + () => ctx.sendSelf(TICK), + await config().get("fcl.eventPollRate", RATE) + ) +} + +function normalizeEventTypeFilter( + filterOrType?: EventTypeFilter | string +): NormalizedEventTypeFilter { + // Normalize the filter to arrays + let filter: NormalizedEventTypeFilter + if (typeof filterOrType === "string") { + return { + eventTypes: [filterOrType], + addresses: [], + contracts: [], + } + } else if (filterOrType == null) { + return { + eventTypes: [], + addresses: [], + contracts: [], + } + } else { + const {eventTypes, addresses, contracts} = filterOrType + return { + eventTypes: Array.isArray(eventTypes) + ? eventTypes + : eventTypes + ? [eventTypes] + : [], + addresses: Array.isArray(addresses) + ? addresses + : addresses + ? [addresses] + : [], + contracts: Array.isArray(contracts) + ? contracts + : contracts + ? [contracts] + : [], + } + } +} + +/** + * @typedef {import("@onflow/typedefs").Event} Event + */ + +/** + * @typedef {object} SubscribeObject + * @property {Function} subscribe - The subscribe function. + */ + +/** + * @callback SubscriptionCallback + * @returns {Event} + */ + +/** + * @description - Subscribe to events + * @param filterOrType - The filter or type of events to subscribe to + * + * @example + * import * as fcl from "@onflow/fcl" + * fcl.events(eventName).subscribe((event) => console.log(event)) + */ +export function events(filterOrType?: EventTypeFilter | string) { + const filter = normalizeEventTypeFilter(filterOrType) + + return { + subscribe: ( + callback: (event: Event | null, error: Error | null) => void + ) => { + const streamPromise: Promise> = send([ + subscribeEvents(filter), + ]).then(decode) + + // Subscribe to the stream using the callback + streamPromise.then(stream => + stream.subscribe( + event => callback(event, null), + error => { + callback(null, error) + } + ) + ) + + return () => { + streamPromise.then(stream => stream.close()) + } + }, + } +} diff --git a/packages/fcl/src/interaction-template-utils/generate-template-id.js b/packages/fcl/src/interaction-template-utils/generate-template-id.js index f72f121b7..453db190d 100644 --- a/packages/fcl/src/interaction-template-utils/generate-template-id.js +++ b/packages/fcl/src/interaction-template-utils/generate-template-id.js @@ -5,7 +5,7 @@ import {normalizeInteractionTemplate} from "../normalizers/interaction-template/ /** * @description Generates Interaction Template ID for a given Interaction Template - * + * * @param {object} params * @param {object} params.template - Interaction Template * @returns {Promise} - Interaction Template ID diff --git a/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js b/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js index 0a75932f8..3958f3bd5 100644 --- a/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js +++ b/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js @@ -5,7 +5,7 @@ import {normalizeInteractionTemplateInterface} from "../normalizers/interaction- /** * @description Generates Interaction Template Interface ID for a given Interaction Template Interface - * + * * @param {object} params * @param {object} params.templateInterface - Interaction Template Interface * @returns {Promise} - Interaction Template Interface ID diff --git a/packages/sdk/src/interaction/interaction.js b/packages/sdk/src/interaction/interaction.js index 518c5504c..25e177e3d 100644 --- a/packages/sdk/src/interaction/interaction.js +++ b/packages/sdk/src/interaction/interaction.js @@ -111,11 +111,6 @@ const IX = `{ }, "collection": { "id":null - }, - "subscription": { - "onData":null, - "onError":null, - "onComplete":null } }` diff --git a/packages/sdk/src/sdk.ts b/packages/sdk/src/sdk.ts index 9a4decc99..6f9162498 100644 --- a/packages/sdk/src/sdk.ts +++ b/packages/sdk/src/sdk.ts @@ -67,6 +67,7 @@ export {transaction} from "./build/build-transaction.js" export {validator} from "./build/build-validator.js" export {invariant} from "./build/build-invariant.js" export {voucherIntercept} from "./build/build-voucher-intercept.js" +export {subscribeEvents} from "./build/build-subscribe-events" // Resolvers export {resolveCadence} from "./resolve/resolve-cadence.js" diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json index ac6917898..825f35bfb 100644 --- a/packages/sdk/tsconfig.json +++ b/packages/sdk/tsconfig.json @@ -6,6 +6,7 @@ // Types should go into this directory. // Removing this would place the .d.ts files // next to the .js files - "outDir": "types" + "outDir": "types", + "baseUrl": "." } } diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index d8eb9a1ac..a67925898 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -36,6 +36,7 @@ "@onflow/util-address": "^1.2.0", "@onflow/util-invariant": "^1.2.0", "@onflow/util-logger": "^1.3.0", + "@onflow/util-pubsub": "^1.0.0", "@onflow/util-template": "^1.2.0", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6" diff --git a/packages/transport-http/src/send-subscribe-events.js b/packages/transport-http/src/send-subscribe-events.js index 4e3f27aad..cd40bcb3a 100644 --- a/packages/transport-http/src/send-subscribe-events.js +++ b/packages/transport-http/src/send-subscribe-events.js @@ -23,15 +23,6 @@ function constructData(ix, context, data) { return ret } -function constructResponse(ix, context, callback) { - let ret = context.response() - ret.tag = ix.tag - - ret.unsubscribeCallback = callback - - return ret -} - export async function sendSubscribeEvents(ix, context = {}, opts = {}) { invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`) invariant( @@ -46,9 +37,8 @@ export async function sendSubscribeEvents(ix, context = {}, opts = {}) { ix = await ix const subscribeWs = opts.subscribeWs || defaultSubscribeWs - const {onData, onError, onComplete} = ix.subscription - const unsubscribe = subscribeWs({ + const socketStream = subscribeWs({ hostname: opts.node, path: `/v1/subscribe_events`, params: { @@ -59,15 +49,8 @@ export async function sendSubscribeEvents(ix, context = {}, opts = {}) { contracts: ix.subscribeEvents.contracts, heartbeat_interval: ix.subscribeEvents.heartbeatInterval, }, - onData: handleData, - onError, - onComplete, }) - function handleData(data) { - const resp = constructData(ix, context, data) - onData(resp) - } - - return constructResponse(ix, context, unsubscribe) + // Map the data to the response object + return socketStream.map(data => constructData(ix, context, data)) } diff --git a/packages/transport-http/src/send-subscribe-events.test.js b/packages/transport-http/src/send-subscribe-events.test.js index 078db3d08..2b68bf409 100644 --- a/packages/transport-http/src/send-subscribe-events.test.js +++ b/packages/transport-http/src/send-subscribe-events.test.js @@ -11,20 +11,11 @@ import { describe("Subscribe Events", () => { let subscribeWsMock - let onData - let onError - let onComplete - let unsubscribe let response beforeEach(async () => { subscribeWsMock = jest.fn() - - onData = jest.fn() - onError = jest.fn() - onComplete = jest.fn() - unsubscribe = jest.fn() subscribeWsMock.mockReturnValue({ diff --git a/packages/transport-http/src/subscribe-ws.js b/packages/transport-http/src/subscribe-ws.js deleted file mode 100644 index bd6ce893f..000000000 --- a/packages/transport-http/src/subscribe-ws.js +++ /dev/null @@ -1,36 +0,0 @@ -import {safeParseJSON} from "./utils" - -// TODO: Implement retries -export function subscribeWs({ - hostname, - path, - params, - onData, - onClose, - onError, - retryLimit = 5, - retryIntervalMs = 1000, -}) { - const url = new URL(path, hostname) - url.search = new URLSearchParams(params) - var ws = new WebSocket(url) - - ws.onmessage = function (e) { - const data = safeParseJSON(e.data) - if (data) { - onData(data) - } - } - - ws.onclose = function (e) { - onClose(e) - } - - ws.onerror = function (err) { - onError(err) - } - - return () => { - ws.close() - } -} diff --git a/packages/transport-http/src/subscribe-ws.ts b/packages/transport-http/src/subscribe-ws.ts new file mode 100644 index 000000000..9ec1180d4 --- /dev/null +++ b/packages/transport-http/src/subscribe-ws.ts @@ -0,0 +1,46 @@ +import {safeParseJSON} from "./utils" +import {PubSub, DataStream} from "@onflow/util-pubsub" + +// TODO: Implement retries +export function subscribeWs({ + hostname, + path, + params, + retryLimit = 5, + retryIntervalMs = 1000, +}: { + hostname: string + path: string + params?: Record + retryLimit?: number + retryIntervalMs?: number +}): DataStream { + // Build a data stream + const pubSub = new PubSub() + const dataStream = new DataStream(pubSub, closeConnection) + function closeConnection() { + ws.close() + } + + // Build a websocket connection + const url = new URL(path, hostname) + url.search = new URLSearchParams(params).toString() + const ws = new WebSocket(url) + + ws.onmessage = function (e) { + const data = safeParseJSON(e.data) + if (data) { + pubSub.next(data) + } + } + + ws.onclose = function () { + pubSub.complete() + } + + ws.onerror = function (e) { + pubSub.error(e) + } + + return dataStream +} diff --git a/packages/util-pubsub/.babelrc b/packages/util-pubsub/.babelrc new file mode 100644 index 000000000..67fc2886b --- /dev/null +++ b/packages/util-pubsub/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": [ + [ + "@babel/preset-env" + ] + ] +} diff --git a/packages/util-pubsub/package.json b/packages/util-pubsub/package.json new file mode 100644 index 000000000..3866a1f19 --- /dev/null +++ b/packages/util-pubsub/package.json @@ -0,0 +1,30 @@ +{ + "name": "@onflow/util-pubsub", + "version": "1.0.0", + "description": "Lightweight utility for creating pubsub model", + "main": "dist/index.js", + "scripts": { + "prepublishOnly": "npm test && npm run build", + "test": "jest", + "build": "fcl-bundle", + "test:watch": "jest --watch", + "start": "fcl-bundle --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/onflow/fcl-js.git" + }, + "author": "Flow Blockchain", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/onflow/fcl-js/issues" + }, + "homepage": "https://github.com/onflow/fcl-js#readme", + "devDependencies": { + "jest": "^29.7.0" + }, + "dependencies": { + "@babel/runtime": "^7.23.2" + }, + "source": "src/index.ts" +} diff --git a/packages/util-pubsub/src/data-stream.test.ts b/packages/util-pubsub/src/data-stream.test.ts new file mode 100644 index 000000000..7b07a9c21 --- /dev/null +++ b/packages/util-pubsub/src/data-stream.test.ts @@ -0,0 +1,65 @@ +import {DataStream} from "./data-stream" +describe("data-stream", () => { + test("should subscribe to a stream", () => { + const mockPubSub = { + subscribe: jest.fn(), + } + const mockCloseFn = jest.fn() + const dataStream = new DataStream(mockPubSub, mockCloseFn) + const mockHandlers = { + next: jest.fn(), + error: jest.fn(), + close: jest.fn(), + } + dataStream.subscribe( + mockHandlers.next, + mockHandlers.error, + mockHandlers.close + ) + expect(mockPubSub.subscribe).toHaveBeenCalledTimes(1) + expect(mockPubSub.subscribe).toHaveBeenCalledWith( + mockHandlers.next, + mockHandlers.error, + mockHandlers.close + ) + }) + + test("should close a stream", () => { + const mockPubSub = { + subscribe: jest.fn(), + } + const mockCloseFn = jest.fn() + const dataStream = new DataStream(mockPubSub, mockCloseFn) + dataStream.close() + expect(mockCloseFn).toHaveBeenCalledTimes(1) + }) + + test("should map a stream", () => { + const mockPubSub = { + subscribe: jest.fn(), + } + const mockCloseFn = jest.fn() + const dataStream = new DataStream(mockPubSub, mockCloseFn) + const mockHandlers = { + next: jest.fn(), + error: jest.fn(), + close: jest.fn(), + } + const mappedDataStream = dataStream.map(value => value + 1) + mappedDataStream.subscribe( + mockHandlers.next, + mockHandlers.error, + mockHandlers.close + ) + expect(mockPubSub.subscribe).toHaveBeenCalledTimes(1) + expect(mockPubSub.subscribe).toHaveBeenCalledWith( + expect.any(Function), + mockHandlers.error, + mockHandlers.close + ) + const mockNext = mockPubSub.subscribe.mock.calls[0][0] + mockNext(1) + expect(mockHandlers.next).toHaveBeenCalledTimes(1) + expect(mockHandlers.next).toHaveBeenCalledWith(2) + }) +}) diff --git a/packages/util-pubsub/src/data-stream.ts b/packages/util-pubsub/src/data-stream.ts new file mode 100644 index 000000000..c0e2cf400 --- /dev/null +++ b/packages/util-pubsub/src/data-stream.ts @@ -0,0 +1,36 @@ +import {Subscribable, Subscription} from "./pub-sub" + +/** + * Used to create a stream of data from a PubSub instance with a callback for the receiver to close the stream. + */ +export class DataStream implements Subscribable { + constructor(private stream: Subscribable, private closeFn: () => void) {} + + subscribe( + next?: (value: T) => void, + error?: (err: any) => void, + close?: () => void + ): Subscription { + return this.stream.subscribe(next, error, close) + } + + close(): void { + this.closeFn() + } + + /** + * Map the data stream to a new stream of data. + * @param fn The function to map the data. + * @returns A new DataStream instance. + */ + map(fn: (value: T) => R): DataStream { + return new DataStream( + { + subscribe: (next, error, close) => { + return this.subscribe(value => next && next(fn(value)), error, close) + }, + }, + this.closeFn + ) + } +} diff --git a/packages/util-pubsub/src/index.ts b/packages/util-pubsub/src/index.ts new file mode 100644 index 000000000..fef90fdff --- /dev/null +++ b/packages/util-pubsub/src/index.ts @@ -0,0 +1,2 @@ +export * from "./data-stream" +export * from "./pub-sub" diff --git a/packages/util-pubsub/src/pub-sub.test.ts b/packages/util-pubsub/src/pub-sub.test.ts new file mode 100644 index 000000000..2b4d4cdc4 --- /dev/null +++ b/packages/util-pubsub/src/pub-sub.test.ts @@ -0,0 +1,88 @@ +import {PubSub} from "./pub-sub" + +function flushEvents() { + return new Promise(resolve => setTimeout(resolve, 0)) +} + +describe("util-pubsub", () => { + it("should publish messages asynchronously", async () => { + const pubsub = new PubSub() + const messages: string[] = [] + const subscription = pubsub.subscribe(message => messages.push(message)) + pubsub.next("foo") + pubsub.next("bar") + subscription.unsubscribe() + pubsub.next("baz") + + await flushEvents() + expect(messages).toEqual(["foo", "bar"]) + }) + + it("should publish messages synchronously", () => { + const pubsub = new PubSub() + const messages: string[] = [] + const subscription = pubsub.subscribe(message => messages.push(message)) + pubsub.next("foo", true) + pubsub.next("bar", true) + subscription.unsubscribe() + pubsub.next("baz", true) + expect(messages).toEqual(["foo", "bar"]) + }) + + it("should publish errors", async () => { + const pubsub = new PubSub() + const errors: any[] = [] + const subscription = pubsub.subscribe(undefined, error => + errors.push(error) + ) + pubsub.error("foo") + pubsub.error("bar") + subscription.unsubscribe() + pubsub.error("baz") + + await flushEvents() + expect(errors).toEqual(["foo", "bar"]) + }) + + it("should publish errors synchronously", () => { + const pubsub = new PubSub() + const errors: any[] = [] + const subscription = pubsub.subscribe(undefined, error => + errors.push(error) + ) + pubsub.error("foo", true) + pubsub.error("bar", true) + subscription.unsubscribe() + pubsub.error("baz", true) + + expect(errors).toEqual(["foo", "bar"]) + }) + + it("should publish completion", async () => { + const pubsub = new PubSub() + const completions: any[] = [] + const subscription = pubsub.subscribe(undefined, undefined, () => + completions.push(true) + ) + jest.spyOn(subscription, "unsubscribe") + pubsub.complete(true) + + await flushEvents() + + expect(completions).toEqual([true, true]) + expect(subscription.unsubscribe).toHaveBeenCalledTimes(1) + }) + + it("should publish completion synchronously", () => { + const pubsub = new PubSub() + const completions: any[] = [] + const subscription = pubsub.subscribe(undefined, undefined, () => + completions.push(true) + ) + jest.spyOn(subscription, "unsubscribe") + pubsub.complete(true) + + expect(completions).toEqual([true, true]) + expect(subscription.unsubscribe).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/util-pubsub/src/pub-sub.ts b/packages/util-pubsub/src/pub-sub.ts new file mode 100644 index 000000000..b83f0dade --- /dev/null +++ b/packages/util-pubsub/src/pub-sub.ts @@ -0,0 +1,89 @@ +export type Operator = Subscribable> = ( + source: Subscribable +) => S +export interface Subscriber { + next?: (value: T) => void + error?: (err: any) => void + complete?: () => void +} + +export interface Subscription { + unsubscribe: () => void +} + +export interface Subscribable { + subscribe: ( + next?: (value: T) => void, + error?: (err: any) => void, + complete?: () => void + ) => Subscription +} + +/** + * Non-blocking pub-sub implementation. Observers are notified after the event loop flushes. + */ +export class PubSub implements Subscribable { + private observers: Subscriber[] = [] + + subscribe( + next?: (value: T) => void, + error?: (err: any) => void, + complete?: () => void + ): Subscription { + const observer: Subscriber = {next, error, complete} + this.observers.push(observer) + + return { + unsubscribe: () => { + const index = this.observers.indexOf(observer) + if (index > -1) { + this.observers.splice(index, 1) + } + }, + } + } + + next(data: T, sync = false) { + this.observers.forEach(observer => + this.publish("next", observer, sync, data) + ) + } + + error(err: any, sync = false) { + this.observers.forEach(observer => + this.publish("error", observer, sync, err) + ) + this.observers = [] + } + + /** + * + * @param sync + */ + complete(sync = false) { + this.observers.forEach(observer => this.publish("complete", observer, sync)) + this.observers = [] + } + + /** + * Returns the number of subscribers connected to the PubSub instance. + */ + get subscriberCount() { + return this.observers.length + } + + private publish>( + channel: V, + observer: Subscriber, + sync: boolean, + ...args: Parameters[V]>> + ) { + const deliverMessage = () => { + if (!this.observers.includes(observer)) return + observer[channel]?.apply(observer, args as any) + } + + if (sync) return deliverMessage() + setTimeout(deliverMessage, 0) + } +} diff --git a/packages/util-pubsub/tsconfig.json b/packages/util-pubsub/tsconfig.json new file mode 100644 index 000000000..88be905eb --- /dev/null +++ b/packages/util-pubsub/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig", + // Change this to match your project + "include": ["src/**/*"], + "compilerOptions": { + // Types should go into this directory. + // Removing this would place the .d.ts files + // next to the .js files + "outDir": "types", + } +} \ No newline at end of file From a3e45beb29888a111c9a844b759a3467e847281f Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 20 Nov 2023 19:56:01 -0800 Subject: [PATCH 03/54] Renamings and cleanup --- packages/fcl/src/events/index-experimental.ts | 333 ------------------ packages/fcl/src/events/index.js | 91 ----- packages/fcl/src/events/index.ts | 53 +-- packages/sdk/src/response/response.js | 2 +- ...-events.js => connect-subscribe-events.js} | 8 +- .../src/{subscribe-ws.ts => connect-ws.ts} | 2 +- packages/transport-http/src/send-http.js | 4 +- 7 files changed, 24 insertions(+), 469 deletions(-) delete mode 100644 packages/fcl/src/events/index-experimental.ts delete mode 100644 packages/fcl/src/events/index.js rename packages/transport-http/src/{send-subscribe-events.js => connect-subscribe-events.js} (86%) rename packages/transport-http/src/{subscribe-ws.ts => connect-ws.ts} (96%) diff --git a/packages/fcl/src/events/index-experimental.ts b/packages/fcl/src/events/index-experimental.ts deleted file mode 100644 index e0c189bd0..000000000 --- a/packages/fcl/src/events/index-experimental.ts +++ /dev/null @@ -1,333 +0,0 @@ -import {spawn, subscriber, SUBSCRIBE, UNSUBSCRIBE} from "@onflow/util-actor" -import { - config, - block, - getEventsAtBlockHeightRange, - send, - decode, - subscribeEvents, - resolve, -} from "@onflow/sdk" -import { - Subscribable, - Subscription, - Subscriber, - DataStream, -} from "@onflow/util-pubsub" - -/* -ix.subscribeEvents.startBlockId = startBlockId -ix.subscribeEvents.startHeight = startHeight -ix.subscribeEvents.eventTypes = eventTypes -ix.subscribeEvents.addresses = addresses -ix.subscribeEvents.contracts = contracts -ix.subscribeEvents.heartbeatInterval = heartbeatInterval -*/ -type EventTypeFilter = { - eventTypes?: string[] | string - addresses?: string[] | string - contracts?: string[] | string -} - -type NormalizedEventTypeFilter = { - eventTypes: string[] - addresses: string[] - contracts: string[] -} - -const RATE = 10000 -const UPDATED = "UPDATED" -const TICK = "TICK" -const HIGH_WATER_MARK = "hwm" - -const scheduleTick = async ctx => { - return setTimeout( - () => ctx.sendSelf(TICK), - await config().get("fcl.eventPollRate", RATE) - ) -} - -function mergeFilters( - a: NormalizedEventTypeFilter, - b: NormalizedEventTypeFilter -): NormalizedEventTypeFilter { - return { - eventTypes: Array.from(new Set([...a.eventTypes, ...b.eventTypes])), - addresses: Array.from(new Set([...a.addresses, ...b.addresses])), - contracts: Array.from(new Set([...a.contracts, ...b.contracts])), - } -} - -function normalizeEventTypeFilter(filterOrType?: EventTypeFilter | string) { - // Normalize the filter to arrays - let filter: NormalizedEventTypeFilter - if (typeof filterOrType === "string") { - return { - eventTypes: [filterOrType], - addresses: [], - contracts: [], - } - } else if (filterOrType == null) { - return { - eventTypes: [], - addresses: [], - contracts: [], - } - } else { - const {eventTypes, addresses, contracts} = filterOrType - filter = { - eventTypes: Array.isArray(eventTypes) ? eventTypes : [eventTypes], - addresses: Array.isArray(addresses) ? addresses : [addresses], - contracts: Array.isArray(contracts) ? contracts : [contracts], - } - } - - // If any of the filters are empty, we need to add a wildcard for tracking - Object.entries(filter).forEach(([key, identifiers]) => { - if (identifiers.length === 0) filter[key].push(WILD_CARD_IDENTIFIER) - }) - - return filter -} - -const WILD_CARD_IDENTIFIER = "*" - -class FilterCounter { - private counts: Map = new Map() - - constructor() {} - - private get(key: string) { - return this.counts.get(key) ?? 0 - } - - increment(key: string) { - const count = this.get(key) + 1 - this.counts.set(key, count) - } - - decrement(key: string) { - const count = this.get(key) - 1 - if (count === 0) this.counts.delete(key) - else this.counts.set(key, count) - } - - getFiltersIdentifiers() { - if (this.counts.get(WILD_CARD_IDENTIFIER) != null) return [] - return Array.from(this.counts.keys()) - } -} - -class FilterManager { - private state: Record = { - eventTypes: new FilterCounter(), - addresses: new FilterCounter(), - contracts: new FilterCounter(), - } - - constructor() {} - - get filter(): NormalizedEventTypeFilter { - return { - eventTypes: this.state.eventTypes.getFiltersIdentifiers(), - addresses: this.state.addresses.getFiltersIdentifiers(), - contracts: this.state.contracts.getFiltersIdentifiers(), - } - } - - addFilter(filter: NormalizedEventTypeFilter) { - Object.keys(filter).forEach(key => { - const identifiers = filter[key as keyof NormalizedEventTypeFilter] - identifiers.forEach(identifier => { - this.state[key as keyof NormalizedEventTypeFilter].increment(identifier) - }) - }) - } - - removeFilter(filter: NormalizedEventTypeFilter) { - Object.keys(filter).forEach(key => { - const identifiers = filter[key as keyof NormalizedEventTypeFilter] - identifiers.forEach(identifier => { - this.state[key as keyof NormalizedEventTypeFilter].decrement(identifier) - }) - }) - } -} - -type Event = { - type: string - address: string - contract: string - height: number - data: any -} - -/** - * The event subscription pool is used to create a single event stream for all subscribers. - * It determines the minimum set of filters needed to satisfy all subscribers, manages the - * event stream, and dispatches events to subscribers. - */ -class EventSubscriptionPool { - // List of subscribers - private subscribers: [Subscriber, earliestStreamId: number][] = [] - // For each normalized filter key they is a map of indentifier to count - private filterManager = new FilterManager() - - // Track old event streams so we can close them when they are no longer needed - // Streams are allowed to persist until they reach a block a block that is - // processed by a later stream - private streams: { - stream: DataStream - lowWaterMark: number - highWaterMark: number - }[] = [] - private streamId = 0 - // handle new events queued while transitioning to a new event stream - private queuedEvents: Event[] = [] - - subscribe( - filterOrType: EventTypeFilter | string, - cb: (event: Event, error: any) => void - ): Subscription { - // Add the filter to the filter manager - const subscriberFilter = normalizeEventTypeFilter(filterOrType) - this.filterManager.addFilter(subscriberFilter) - - function onEvent(event: Event) { - if (subscriberFilter.eventTypes.length > 0) { - if ( - !subscriberFilter.eventTypes.includes(event.type) && - !subscriberFilter.eventTypes.includes(WILD_CARD_IDENTIFIER) - ) - return - } - if (subscriberFilter.addresses.length > 0) { - if ( - !subscriberFilter.addresses.includes(event.address) && - !subscriberFilter.addresses.includes(WILD_CARD_IDENTIFIER) - ) - return - } - if (subscriberFilter.contracts.length > 0) { - if ( - !subscriberFilter.contracts.includes(event.contract) && - !subscriberFilter.contracts.includes(WILD_CARD_IDENTIFIER) - ) - return - } - cb(event, null) - } - - function onError(error: any) { - cb(null, error) - } - - const subscriber = {next: onEvent, error: onError} - this.subscribers.push([subscriber, this.streamId]) - - return { - unsubscribe: () => { - const index = this.subscribers.findIndex( - ([_subscriber, _]) => _subscriber === subscriber - ) - if (index > -1) { - this.subscribers.splice(index, 1) - this.filterManager.removeFilter(subscriberFilter) - } - }, - } - } - - private refreshSocket() { - // If there are no subscribers, close the event stream - if (this.subscribers.length === 0) { - this.streams.forEach(({stream}) => stream?.close()) - this.streams = [] - return - } - - const filter = this.filterManager.filter - - // Close the current event stream - // We need a grace period to ensure there is no gap in events - const oldStreamId = this.streamId - setTimeout(() => { - this.removeStream(oldStreamId) - }, 5000) - this.streamId++ - - const currentStreamId = this.streamId - // Subscribe to the new event stream - const newStream = resolve(send([subscribeEvents(filter)])) - this.eventStream.subscribe( - event => { - if (this.highWaterMark.blockHeight >= event.height) { - if (this.highWaterMark.streamId > currentStreamId) { - } - } - - this.highWaterMark = [event.height, this.streamId] - this.subscribers.forEach(([subscriber, earliestStreamId]) => { - if (earliestStreamId <= this.highWaterMark[1]) { - subscriber.next(event) - } - }) - }, - error => { - this.subscribers.forEach(subscriber => subscriber.error(error)) - this.refreshSocket() - }, - () => { - this.refreshSocket() - } - ) - } - - private removeStream(streamId: number) { - const index = this.streams.findIndex( - ([, streamId]) => streamId === streamId - ) - if (index > -1) { - this.streams[index]?.[0]?.close() - this.streams.splice(index, 1) - } - } -} - -const EVENT_SUBSCRIPTION_POOL = new EventSubscriptionPool() - -/** - * @typedef {import("@onflow/typedefs").Event} Event - */ - -/** - * @typedef {object} SubscribeObject - * @property {Function} subscribe - The subscribe function. - */ - -/** - * @callback SubscriptionCallback - * @returns {Event} - */ - -/** - * @description - Subscribe to events - * @param {string} key - A valid event name - * @returns {SubscribeObject} - * - * @example - * import * as fcl from "@onflow/fcl" - * fcl.events(eventName).subscribe((event) => console.log(event)) - */ -export function events(key) { - return { - /** - * @description - Subscribe to events - * @param {Function} callback - The callback function - * @returns {SubscriptionCallback} - */ - subscribe: callback => { - return EVENT_SUBSCRIPTION_POOL.subscribe(key, callback) - }, - } -} diff --git a/packages/fcl/src/events/index.js b/packages/fcl/src/events/index.js deleted file mode 100644 index d47976430..000000000 --- a/packages/fcl/src/events/index.js +++ /dev/null @@ -1,91 +0,0 @@ -import {spawn, subscriber, SUBSCRIBE, UNSUBSCRIBE} from "@onflow/util-actor" -import { - config, - block, - getEventsAtBlockHeightRange, - send, - decode, -} from "@onflow/sdk" - -const RATE = 10000 -const UPDATED = "UPDATED" -const TICK = "TICK" -const HIGH_WATER_MARK = "hwm" - -const scheduleTick = async ctx => { - return setTimeout( - () => ctx.sendSelf(TICK), - await config().get("fcl.eventPollRate", RATE) - ) -} - -const HANDLERS = { - [TICK]: async ctx => { - if (!ctx.hasSubs()) return - let hwm = ctx.get(HIGH_WATER_MARK) - if (hwm == null) { - ctx.put(HIGH_WATER_MARK, await block()) - ctx.put(TICK, await scheduleTick(ctx)) - } else { - let next = await block() - ctx.put(HIGH_WATER_MARK, next) - if (hwm.height < next.height) { - const data = await send([ - getEventsAtBlockHeightRange(ctx.self(), hwm.height + 1, next.height), - ]).then(decode) - for (let d of data) ctx.broadcast(UPDATED, d.data) - } - ctx.put(TICK, await scheduleTick(ctx)) - } - }, - [SUBSCRIBE]: async (ctx, letter) => { - if (!ctx.hasSubs()) { - ctx.put(TICK, await scheduleTick(ctx)) - } - ctx.subscribe(letter.from) - }, - [UNSUBSCRIBE]: (ctx, letter) => { - ctx.unsubscribe(letter.from) - if (!ctx.hasSubs()) { - clearTimeout(ctx.get(TICK)) - ctx.delete(TICK) - ctx.delete(HIGH_WATER_MARK) - } - }, -} - -const spawnEvents = key => spawn(HANDLERS, key) - -/** - * @typedef {import("@onflow/typedefs").Event} Event - */ - -/** - * @typedef {object} SubscribeObject - * @property {Function} subscribe - The subscribe function. - */ - -/** - * @callback SubscriptionCallback - * @returns {Event} - */ - -/** - * @description - Subscribe to events - * @param {string} key - A valid event name - * @returns {SubscribeObject} - * - * @example - * import * as fcl from "@onflow/fcl" - * fcl.events(eventName).subscribe((event) => console.log(event)) - */ -export function events(key) { - return { - /** - * @description - Subscribe to events - * @param {Function} callback - The callback function - * @returns {SubscriptionCallback} - */ - subscribe: callback => subscriber(key, spawnEvents, callback), - } -} diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 2fe073e19..24b12795d 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -1,4 +1,4 @@ -import {config, send, decode, subscribeEvents} from "@onflow/sdk" +import {send, decode, subscribeEvents} from "@onflow/sdk" import {DataStream} from "@onflow/util-pubsub" import {Event} from "@onflow/typedefs" @@ -14,26 +14,13 @@ type NormalizedEventTypeFilter = { contracts: string[] } -const RATE = 10000 -const UPDATED = "UPDATED" -const TICK = "TICK" -const HIGH_WATER_MARK = "hwm" - -const scheduleTick = async ctx => { - return setTimeout( - () => ctx.sendSelf(TICK), - await config().get("fcl.eventPollRate", RATE) - ) -} - function normalizeEventTypeFilter( filterOrType?: EventTypeFilter | string ): NormalizedEventTypeFilter { // Normalize the filter to arrays - let filter: NormalizedEventTypeFilter if (typeof filterOrType === "string") { return { - eventTypes: [filterOrType], + eventTypes: filterOrType ? [filterOrType] : [], addresses: [], contracts: [], } @@ -44,23 +31,15 @@ function normalizeEventTypeFilter( contracts: [], } } else { - const {eventTypes, addresses, contracts} = filterOrType + let {eventTypes, addresses, contracts} = filterOrType + eventTypes = eventTypes || [] + addresses = addresses || [] + contracts = contracts || [] + return { - eventTypes: Array.isArray(eventTypes) - ? eventTypes - : eventTypes - ? [eventTypes] - : [], - addresses: Array.isArray(addresses) - ? addresses - : addresses - ? [addresses] - : [], - contracts: Array.isArray(contracts) - ? contracts - : contracts - ? [contracts] - : [], + eventTypes: Array.isArray(eventTypes) ? eventTypes : [eventTypes], + addresses: Array.isArray(addresses) ? addresses : [addresses], + contracts: Array.isArray(contracts) ? contracts : [contracts], } } } @@ -100,12 +79,12 @@ export function events(filterOrType?: EventTypeFilter | string) { // Subscribe to the stream using the callback streamPromise.then(stream => - stream.subscribe( - event => callback(event, null), - error => { - callback(null, error) - } - ) + stream + .map(data => decode(data) as Event) + .subscribe( + event => callback(event, null), + error => callback(null, error) + ) ) return () => { diff --git a/packages/sdk/src/response/response.js b/packages/sdk/src/response/response.js index 2d5a774c0..237696cf2 100644 --- a/packages/sdk/src/response/response.js +++ b/packages/sdk/src/response/response.js @@ -11,7 +11,7 @@ const DEFAULT_RESPONSE = `{ "latestBlock":null, "collection":null, "networkParameters":null, - "unsubscribeCallback":null, + "dataStream":null }` export const response = () => JSON.parse(DEFAULT_RESPONSE) diff --git a/packages/transport-http/src/send-subscribe-events.js b/packages/transport-http/src/connect-subscribe-events.js similarity index 86% rename from packages/transport-http/src/send-subscribe-events.js rename to packages/transport-http/src/connect-subscribe-events.js index cd40bcb3a..cf742b642 100644 --- a/packages/transport-http/src/send-subscribe-events.js +++ b/packages/transport-http/src/connect-subscribe-events.js @@ -1,5 +1,5 @@ import {invariant} from "@onflow/util-invariant" -import {subscribeWs as defaultSubscribeWs} from "./subscribe-ws" +import {connectWs as defaultConnectWs} from "./connect-ws" function constructData(ix, context, data) { let ret = context.response() @@ -23,7 +23,7 @@ function constructData(ix, context, data) { return ret } -export async function sendSubscribeEvents(ix, context = {}, opts = {}) { +export async function connectSubscribeEvents(ix, context = {}, opts = {}) { invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`) invariant( context.response, @@ -36,9 +36,9 @@ export async function sendSubscribeEvents(ix, context = {}, opts = {}) { ix = await ix - const subscribeWs = opts.subscribeWs || defaultSubscribeWs + const connectWs = opts.connectWs || defaultConnectWs - const socketStream = subscribeWs({ + const socketStream = connectWs({ hostname: opts.node, path: `/v1/subscribe_events`, params: { diff --git a/packages/transport-http/src/subscribe-ws.ts b/packages/transport-http/src/connect-ws.ts similarity index 96% rename from packages/transport-http/src/subscribe-ws.ts rename to packages/transport-http/src/connect-ws.ts index 9ec1180d4..5fe64fba5 100644 --- a/packages/transport-http/src/subscribe-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -2,7 +2,7 @@ import {safeParseJSON} from "./utils" import {PubSub, DataStream} from "@onflow/util-pubsub" // TODO: Implement retries -export function subscribeWs({ +export function connectWs({ hostname, path, params, diff --git a/packages/transport-http/src/send-http.js b/packages/transport-http/src/send-http.js index f4334f88e..8d1669cc6 100644 --- a/packages/transport-http/src/send-http.js +++ b/packages/transport-http/src/send-http.js @@ -5,7 +5,7 @@ import {sendGetTransaction} from "./send-get-transaction.js" import {sendExecuteScript} from "./send-execute-script.js" import {sendGetAccount} from "./send-get-account.js" import {sendGetEvents} from "./send-get-events.js" -import {sendSubscribeEvents} from "./send-subscribe-events.js" +import {connectSubscribeEvents} from "./connect-subscribe-events.js" import {sendGetBlock} from "./send-get-block.js" import {sendGetBlockHeader} from "./send-get-block-header.js" import {sendGetCollection} from "./send-get-collection.js" @@ -36,7 +36,7 @@ export const send = async (ix, context = {}, opts = {}) => { case context.ix.isGetEvents(ix): return opts.sendGetEvents ? opts.sendGetEvents(ix, context, opts) : sendGetEvents(ix, context, opts) case context.ix.isSubscribeEvents(ix): - return opts.sendSubscribeEvents ? opts.sendSubscribeEvents(ix, context, opts) : sendSubscribeEvents(ix, context, opts) + return opts.connectSubscribeEvents ? opts.connectSubscribeEvents(ix, context, opts) : connectSubscribeEvents(ix, context, opts) case context.ix.isGetBlock(ix): return opts.sendGetBlock ? opts.sendGetBlock(ix, context, opts) : sendGetBlock(ix, context, opts) case context.ix.isGetBlockHeader(ix): From 3ea8feb91317bbcfb0dd09a107f6e65e4cfbe364 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 21 Nov 2023 16:53:43 -0800 Subject: [PATCH 04/54] General cleanup --- packages/fcl/.babelrc | 8 +- packages/fcl/jordan.test.js | 18 +++++ packages/fcl/src/events/index.ts | 42 +++++----- packages/sdk/src/build/build-subscription.js | 8 -- .../sdk/src/build/build-subscription.test.js | 20 ----- packages/sdk/src/decode/decode.js | 4 +- packages/transport-http/package.json | 2 +- .../src/connect-subscribe-events.js | 31 +++++-- packages/transport-http/src/connect-ws.ts | 26 +++++- packages/transport-http/src/sdk-send-http.js | 12 --- packages/transport-http/src/sdk-send-http.ts | 13 +++ .../src/send-subscribe-events.test.js | 2 +- packages/util-pubsub/src/data-stream.ts | 10 ++- packages/util-pubsub/src/pub-sub.test.ts | 24 +++--- packages/util-pubsub/src/pub-sub.ts | 80 ++++++++++++------- 15 files changed, 183 insertions(+), 117 deletions(-) create mode 100644 packages/fcl/jordan.test.js delete mode 100644 packages/sdk/src/build/build-subscription.js delete mode 100644 packages/sdk/src/build/build-subscription.test.js delete mode 100644 packages/transport-http/src/sdk-send-http.js create mode 100644 packages/transport-http/src/sdk-send-http.ts diff --git a/packages/fcl/.babelrc b/packages/fcl/.babelrc index 67fc2886b..1e3225d50 100644 --- a/packages/fcl/.babelrc +++ b/packages/fcl/.babelrc @@ -1,7 +1,11 @@ { "presets": [ [ - "@babel/preset-env" - ] + "@babel/preset-env", + { + "useBuiltIns": false + } + ], + "@babel/preset-typescript" ] } diff --git a/packages/fcl/jordan.test.js b/packages/fcl/jordan.test.js new file mode 100644 index 000000000..7002ef16c --- /dev/null +++ b/packages/fcl/jordan.test.js @@ -0,0 +1,18 @@ +// TODO: REMOVE ME + +import * as fcl from "./src/fcl" + +fcl.config({ + "accessNode.api": "https://rest-testnet.onflow.org", +}) + +fcl.events().subscribe(e => console.log(e)) + +jest.setTimeout(100000) + +describe("fcl", () => { + test("dummy delay", async () => { + await new Promise(resolve => setTimeout(resolve, 90000)) + expect(false).toBe(true) + }) +}) diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 24b12795d..60bf629b2 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -9,38 +9,38 @@ type EventTypeFilter = { } type NormalizedEventTypeFilter = { - eventTypes: string[] - addresses: string[] - contracts: string[] + eventTypes?: string[] + addresses?: string[] + contracts?: string[] } function normalizeEventTypeFilter( filterOrType?: EventTypeFilter | string ): NormalizedEventTypeFilter { // Normalize the filter to arrays - if (typeof filterOrType === "string") { + if (typeof filterOrType === "string" && filterOrType !== "") { return { - eventTypes: filterOrType ? [filterOrType] : [], - addresses: [], - contracts: [], - } - } else if (filterOrType == null) { - return { - eventTypes: [], - addresses: [], - contracts: [], + eventTypes: [filterOrType], } + } else if (filterOrType == null || filterOrType === "") { + return {} } else { let {eventTypes, addresses, contracts} = filterOrType - eventTypes = eventTypes || [] - addresses = addresses || [] - contracts = contracts || [] + let result: NormalizedEventTypeFilter = {} - return { - eventTypes: Array.isArray(eventTypes) ? eventTypes : [eventTypes], - addresses: Array.isArray(addresses) ? addresses : [addresses], - contracts: Array.isArray(contracts) ? contracts : [contracts], + if (eventTypes != null) { + result.eventTypes = Array.isArray(eventTypes) ? eventTypes : [eventTypes] + } + + if (addresses != null) { + result.addresses = Array.isArray(addresses) ? addresses : [addresses] + } + + if (contracts != null) { + result.contracts = Array.isArray(contracts) ? contracts : [contracts] } + + return result } } @@ -80,7 +80,7 @@ export function events(filterOrType?: EventTypeFilter | string) { // Subscribe to the stream using the callback streamPromise.then(stream => stream - .map(data => decode(data) as Event) + .map(data => decode(data) as Promise) .subscribe( event => callback(event, null), error => callback(null, error) diff --git a/packages/sdk/src/build/build-subscription.js b/packages/sdk/src/build/build-subscription.js deleted file mode 100644 index 63f65b8d8..000000000 --- a/packages/sdk/src/build/build-subscription.js +++ /dev/null @@ -1,8 +0,0 @@ -export function subscription({onData, onError, onComplete}) { - return ix => { - ix.subscription.onData = onData - ix.subscription.onError = onError - ix.subscription.onComplete = onComplete - return ix - } -} diff --git a/packages/sdk/src/build/build-subscription.test.js b/packages/sdk/src/build/build-subscription.test.js deleted file mode 100644 index a13d65303..000000000 --- a/packages/sdk/src/build/build-subscription.test.js +++ /dev/null @@ -1,20 +0,0 @@ -import {interaction} from "../interaction/interaction.js" -import {subscription} from "./build-subscription.js" - -describe("Build Subscription", () => { - test("build subscription", async () => { - const onData = () => {} - const onError = () => {} - const onComplete = () => {} - - let ix = await subscription({ - onData, - onError, - onComplete, - })(interaction()) - - expect(ix.subscription.onData).toBe(onData) - expect(ix.subscription.onError).toBe(onError) - expect(ix.subscription.onComplete).toBe(onComplete) - }) -}) diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index c005ea987..a62285c66 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -223,8 +223,8 @@ export const decodeResponse = async (response, customDecoders = {}) => { return { chainId: chainIdMap[response.networkParameters.chainId], } - } else if (response.unsubscribeCallback) { - return response.unsubscribeCallback + } else if (response.dataStream) { + return response.dataStream } return null diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index a67925898..4694a2f83 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -19,7 +19,7 @@ "@onflow/types": "^1.2.0", "jest": "^29.5.0" }, - "source": "src/sdk-send-http.js", + "source": "src/sdk-send-http.ts", "main": "dist/sdk-send-http.js", "module": "dist/sdk-send-http.module.js", "unpkg": "dist/sdk-send-http.umd.js", diff --git a/packages/transport-http/src/connect-subscribe-events.js b/packages/transport-http/src/connect-subscribe-events.js index cf742b642..5ad269d14 100644 --- a/packages/transport-http/src/connect-subscribe-events.js +++ b/packages/transport-http/src/connect-subscribe-events.js @@ -2,16 +2,26 @@ import {invariant} from "@onflow/util-invariant" import {connectWs as defaultConnectWs} from "./connect-ws" function constructData(ix, context, data) { + // TODO REMOVE ME + // DUMMY PAYLOAD UNTIL ACCESS NODE BUG IS FIXED + if (data.Events) { + data.Events = data.Events.map(event => ({ + ...event, + Payload: + "eyJ2YWx1ZSI6eyJpZCI6IkEuOTEyZDU0NDBmN2UzNzY5ZS5GbG93RmVlcy5GZWVzRGVkdWN0ZWQiLCJmaWVsZHMiOlt7InZhbHVlIjp7InZhbHVlIjoiMC4wMDAwMDExOSIsInR5cGUiOiJVRml4NjQifSwibmFtZSI6ImFtb3VudCJ9LHsidmFsdWUiOnsidmFsdWUiOiIxLjAwMDAwMDAwIiwidHlwZSI6IlVGaXg2NCJ9LCJuYW1lIjoiaW5jbHVzaW9uRWZmb3J0In0seyJ2YWx1ZSI6eyJ2YWx1ZSI6IjAuMDAwMDAwMDQiLCJ0eXBlIjoiVUZpeDY0In0sIm5hbWUiOiJleGVjdXRpb25FZmZvcnQifV19LCJ0eXBlIjoiRXZlbnQifQo=", + })) + } + let ret = context.response() ret.tag = ix.tag - ret.events = data.events - ? data.events.map(event => ({ - blockId: data.BlockId, + ret.events = data.Events + ? data.Events.map(event => ({ + blockId: data.BlockID, blockHeight: Number(data.Height), blockTimestamp: data.Timestamp, type: event.Type, - transactionId: event.TransactionId, + transactionId: event.TransactionID, transactionIndex: Number(event.TransactionIndex), eventIndex: Number(event.EventIndex), payload: JSON.parse( @@ -23,6 +33,15 @@ function constructData(ix, context, data) { return ret } +function constructResponse(ix, context, stream) { + let ret = context.response() + ret.tag = ix.tag + + ret.dataStream = stream + + return ret +} + export async function connectSubscribeEvents(ix, context = {}, opts = {}) { invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`) invariant( @@ -52,5 +71,7 @@ export async function connectSubscribeEvents(ix, context = {}, opts = {}) { }) // Map the data to the response object - return socketStream.map(data => constructData(ix, context, data)) + const dataStream = socketStream.map(data => constructData(ix, context, data)) + + return constructResponse(ix, context, dataStream) } diff --git a/packages/transport-http/src/connect-ws.ts b/packages/transport-http/src/connect-ws.ts index 5fe64fba5..47c3d50a6 100644 --- a/packages/transport-http/src/connect-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -22,9 +22,8 @@ export function connectWs({ ws.close() } - // Build a websocket connection - const url = new URL(path, hostname) - url.search = new URLSearchParams(params).toString() + // Build a websocket connection with correct protocol & params + const url = buildConnectionUrl(hostname, path, params) const ws = new WebSocket(url) ws.onmessage = function (e) { @@ -44,3 +43,24 @@ export function connectWs({ return dataStream } + +function buildConnectionUrl( + hostname: string, + path: string, + params?: Record +) { + const url = new URL(path, hostname) + if (url.protocol === "https:") { + url.protocol = "wss:" + } else if (url.protocol === "http:") { + url.protocol = "ws:" + } + + Object.entries(params || {}).forEach(([key, value]) => { + if (value) { + url.searchParams.append(key, value) + } + }) + + return url.toString() +} diff --git a/packages/transport-http/src/sdk-send-http.js b/packages/transport-http/src/sdk-send-http.js deleted file mode 100644 index 35a8edf65..000000000 --- a/packages/transport-http/src/sdk-send-http.js +++ /dev/null @@ -1,12 +0,0 @@ -export {sendExecuteScript} from "./send-execute-script.js" -export {sendGetAccount} from "./send-get-account.js" -export {sendGetBlockHeader} from "./send-get-block-header.js" -export {sendGetBlock} from "./send-get-block.js" -export {sendGetCollection} from "./send-get-collection.js" -export {sendGetEvents} from "./send-get-events.js" -export {sendGetTransaction} from "./send-get-transaction.js" -export {sendGetTransactionStatus} from "./send-get-transaction-status.js" -export {sendPing} from "./send-ping.js" -export {sendTransaction} from "./send-transaction.js" -export {sendGetNetworkParameters} from "./send-get-network-parameters.js" -export {send} from "./send-http.js" diff --git a/packages/transport-http/src/sdk-send-http.ts b/packages/transport-http/src/sdk-send-http.ts new file mode 100644 index 000000000..4761e9c6b --- /dev/null +++ b/packages/transport-http/src/sdk-send-http.ts @@ -0,0 +1,13 @@ +export {sendExecuteScript} from "./send-execute-script" +export {sendGetAccount} from "./send-get-account" +export {sendGetBlockHeader} from "./send-get-block-header" +export {sendGetBlock} from "./send-get-block" +export {sendGetCollection} from "./send-get-collection" +export {sendGetEvents} from "./send-get-events" +export {sendGetTransaction} from "./send-get-transaction" +export {sendGetTransactionStatus} from "./send-get-transaction-status" +export {sendPing} from "./send-ping" +export {sendTransaction} from "./send-transaction" +export {sendGetNetworkParameters} from "./send-get-network-parameters" +export {connectSubscribeEvents} from "./connect-subscribe-events" +export {send} from "./send-http" diff --git a/packages/transport-http/src/send-subscribe-events.test.js b/packages/transport-http/src/send-subscribe-events.test.js index 2b68bf409..039fb50d6 100644 --- a/packages/transport-http/src/send-subscribe-events.test.js +++ b/packages/transport-http/src/send-subscribe-events.test.js @@ -1,4 +1,4 @@ -import {sendSubscribeEvents} from "./send-subscribe-events" +import {sendSubscribeEvents} from "./connect-subscribe-events" import {Buffer} from "@onflow/rlp" import { build, diff --git a/packages/util-pubsub/src/data-stream.ts b/packages/util-pubsub/src/data-stream.ts index c0e2cf400..48641c427 100644 --- a/packages/util-pubsub/src/data-stream.ts +++ b/packages/util-pubsub/src/data-stream.ts @@ -23,11 +23,17 @@ export class DataStream implements Subscribable { * @param fn The function to map the data. * @returns A new DataStream instance. */ - map(fn: (value: T) => R): DataStream { + map(fn: (value: T) => R | Promise): DataStream { return new DataStream( { subscribe: (next, error, close) => { - return this.subscribe(value => next && next(fn(value)), error, close) + return this.subscribe( + async value => { + next && next(await fn(value)) + }, + error, + close + ) }, }, this.closeFn diff --git a/packages/util-pubsub/src/pub-sub.test.ts b/packages/util-pubsub/src/pub-sub.test.ts index 2b4d4cdc4..58439704a 100644 --- a/packages/util-pubsub/src/pub-sub.test.ts +++ b/packages/util-pubsub/src/pub-sub.test.ts @@ -8,7 +8,9 @@ describe("util-pubsub", () => { it("should publish messages asynchronously", async () => { const pubsub = new PubSub() const messages: string[] = [] - const subscription = pubsub.subscribe(message => messages.push(message)) + const subscription = pubsub.subscribe(message => { + messages.push(message) + }) pubsub.next("foo") pubsub.next("bar") subscription.unsubscribe() @@ -21,7 +23,9 @@ describe("util-pubsub", () => { it("should publish messages synchronously", () => { const pubsub = new PubSub() const messages: string[] = [] - const subscription = pubsub.subscribe(message => messages.push(message)) + const subscription = pubsub.subscribe(message => { + messages.push(message) + }) pubsub.next("foo", true) pubsub.next("bar", true) subscription.unsubscribe() @@ -32,9 +36,9 @@ describe("util-pubsub", () => { it("should publish errors", async () => { const pubsub = new PubSub() const errors: any[] = [] - const subscription = pubsub.subscribe(undefined, error => + const subscription = pubsub.subscribe(undefined, error => { errors.push(error) - ) + }) pubsub.error("foo") pubsub.error("bar") subscription.unsubscribe() @@ -47,9 +51,9 @@ describe("util-pubsub", () => { it("should publish errors synchronously", () => { const pubsub = new PubSub() const errors: any[] = [] - const subscription = pubsub.subscribe(undefined, error => + const subscription = pubsub.subscribe(undefined, error => { errors.push(error) - ) + }) pubsub.error("foo", true) pubsub.error("bar", true) subscription.unsubscribe() @@ -61,9 +65,9 @@ describe("util-pubsub", () => { it("should publish completion", async () => { const pubsub = new PubSub() const completions: any[] = [] - const subscription = pubsub.subscribe(undefined, undefined, () => + const subscription = pubsub.subscribe(undefined, undefined, () => { completions.push(true) - ) + }) jest.spyOn(subscription, "unsubscribe") pubsub.complete(true) @@ -76,9 +80,9 @@ describe("util-pubsub", () => { it("should publish completion synchronously", () => { const pubsub = new PubSub() const completions: any[] = [] - const subscription = pubsub.subscribe(undefined, undefined, () => + const subscription = pubsub.subscribe(undefined, undefined, () => { completions.push(true) - ) + }) jest.spyOn(subscription, "unsubscribe") pubsub.complete(true) diff --git a/packages/util-pubsub/src/pub-sub.ts b/packages/util-pubsub/src/pub-sub.ts index b83f0dade..d0471d92b 100644 --- a/packages/util-pubsub/src/pub-sub.ts +++ b/packages/util-pubsub/src/pub-sub.ts @@ -1,10 +1,49 @@ export type Operator = Subscribable> = ( source: Subscribable ) => S -export interface Subscriber { - next?: (value: T) => void - error?: (err: any) => void - complete?: () => void +export class Subscriber { + private nextMutex = Promise.resolve() + private errorMutex = Promise.resolve() + private completeMutex = Promise.resolve() + + constructor( + private readonly _next?: (value: T) => void | Promise, + private readonly _error?: (err: any) => void | Promise, + private readonly _complete?: () => void | Promise + ) {} + + next(value: T) { + setTimeout(() => { + this.nextMutex = deliverMessage() + }, 0) + + const deliverMessage = async () => { + await this.nextMutex + if (this._next) await this._next(value) + } + } + + error(err: any) { + setTimeout(() => { + this.errorMutex = deliverMessage() + }, 0) + + const deliverMessage = async () => { + await this.errorMutex + if (this._error) await this._error(err) + } + } + + complete() { + setTimeout(() => { + this.completeMutex = deliverMessage() + }, 0) + + const deliverMessage = async () => { + await this.completeMutex + if (this._complete) await this._complete() + } + } } export interface Subscription { @@ -26,11 +65,11 @@ export class PubSub implements Subscribable { private observers: Subscriber[] = [] subscribe( - next?: (value: T) => void, - error?: (err: any) => void, - complete?: () => void + next?: (value: T) => void | Promise, + error?: (err: any) => void | Promise, + complete?: () => void | Promise ): Subscription { - const observer: Subscriber = {next, error, complete} + const observer: Subscriber = new Subscriber(next, error, complete) this.observers.push(observer) return { @@ -44,15 +83,11 @@ export class PubSub implements Subscribable { } next(data: T, sync = false) { - this.observers.forEach(observer => - this.publish("next", observer, sync, data) - ) + this.observers.forEach(observer => observer.next(data)) } error(err: any, sync = false) { - this.observers.forEach(observer => - this.publish("error", observer, sync, err) - ) + this.observers.forEach(observer => observer.error(err)) this.observers = [] } @@ -61,7 +96,7 @@ export class PubSub implements Subscribable { * @param sync */ complete(sync = false) { - this.observers.forEach(observer => this.publish("complete", observer, sync)) + this.observers.forEach(observer => observer.complete()) this.observers = [] } @@ -71,19 +106,4 @@ export class PubSub implements Subscribable { get subscriberCount() { return this.observers.length } - - private publish>( - channel: V, - observer: Subscriber, - sync: boolean, - ...args: Parameters[V]>> - ) { - const deliverMessage = () => { - if (!this.observers.includes(observer)) return - observer[channel]?.apply(observer, args as any) - } - - if (sync) return deliverMessage() - setTimeout(deliverMessage, 0) - } } From 910ceb42c6ebb2de614c1e2e5544cf6d18bd50d1 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 22 Nov 2023 16:52:53 -0800 Subject: [PATCH 05/54] Switch to event emitters --- package-lock.json | 36 ++++++- packages/fcl/src/events/index.ts | 20 ++-- packages/sdk/src/decode/decode.js | 57 ++++++++++- packages/sdk/src/response/response.js | 3 +- packages/transport-http/package.json | 5 +- .../src/connect-subscribe-events.test.js | 96 +++++++++++++++++++ ...-events.js => connect-subscribe-events.ts} | 64 +++++++++++-- packages/transport-http/src/connect-ws.ts | 46 +++++---- .../src/send-subscribe-events.test.js | 93 ------------------ packages/transport-http/tsconfig.json | 4 +- packages/typedefs/src/index.ts | 12 +++ packages/util-pubsub/tsconfig.json | 4 +- tsconfig.json | 3 +- 13 files changed, 302 insertions(+), 141 deletions(-) create mode 100644 packages/transport-http/src/connect-subscribe-events.test.js rename packages/transport-http/src/{connect-subscribe-events.js => connect-subscribe-events.ts} (54%) delete mode 100644 packages/transport-http/src/send-subscribe-events.test.js diff --git a/package-lock.json b/package-lock.json index c0ac2db4f..5a4fbc182 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9080,7 +9080,8 @@ }, "node_modules/events": { "version": "3.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "engines": { "node": ">=0.8.x" } @@ -11167,6 +11168,14 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "dev": true, @@ -20792,7 +20801,10 @@ "@onflow/util-pubsub": "^1.0.0", "@onflow/util-template": "^1.2.0", "abort-controller": "^3.0.0", - "cross-fetch": "^3.1.6" + "cross-fetch": "^3.1.6", + "events": "^3.3.0", + "isomorphic-ws": "^5.0.0", + "ws": "^8.14.2" }, "devDependencies": { "@onflow/fcl-bundle": "^1.4.0", @@ -20802,6 +20814,26 @@ "jest": "^29.5.0" } }, + "packages/transport-http/node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "packages/typedefs": { "name": "@onflow/typedefs", "version": "1.2.0", diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 60bf629b2..24ebe890c 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -73,19 +73,21 @@ export function events(filterOrType?: EventTypeFilter | string) { subscribe: ( callback: (event: Event | null, error: Error | null) => void ) => { - const streamPromise: Promise> = send([ - subscribeEvents(filter), - ]).then(decode) + const streamPromise: Promise = send([subscribeEvents(filter)]).then( + decode + ) // Subscribe to the stream using the callback + function onEvents(data: any) { + callback(data, null) + } + function onError(error: Error) { + callback(null, error) + } streamPromise.then(stream => - stream - .map(data => decode(data) as Promise) - .subscribe( - event => callback(event, null), - error => callback(null, error) - ) + stream.on("events", onEvents).on("error", onError) ) + streamPromise.then(console.log) return () => { streamPromise.then(stream => stream.close()) diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index a62285c66..6d61fad4d 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -1,4 +1,5 @@ -import {log, LEVELS} from "@onflow/util-logger" +import {log} from "@onflow/util-logger" +import EventEmitter from "events" const latestBlockDeprecationNotice = () => { log.deprecate({ @@ -224,8 +225,60 @@ export const decodeResponse = async (response, customDecoders = {}) => { chainId: chainIdMap[response.networkParameters.chainId], } } else if (response.dataStream) { - return response.dataStream + return decodeStream(response.dataStream, customDecoders) + } else if (response.heartbeat) { + return response.heartbeat } return null } + +// This function pipes a generic stream of data into a granular stream of decoded data +export function decodeStream(stream, customDecoders) { + const newStream = new EventEmitter() + let topicMutex = {} + + // Data is separated by topic & the decoded data is emitted in order by topic + // The mutex ensures that the data is emitted in order and avoids race conditions when decoding + stream.on("data", async data => { + const topics = Object.keys(data).filter(Boolean) + + for (const channel of topics) { + const currentMutex = topicMutex[channel] || Promise.resolve() + topicMutex[channel] = currentMutex.then(async () => { + const partialResponse = { + [channel]: data[channel], + } + const decoded = await decodeResponse(partialResponse, customDecoders) + newStream.emit(channel, decoded) + }) + } + }) + + // Error event is emitted as they are received + stream.on("error", error => { + newStream.emit("error", error) + }) + + // Close event is emitted the way it is received + stream.on("close", () => { + newStream.emit("close") + }) + + // Open event is emitted the way it is received + stream.on("open", () => { + newStream.emit("open") + }) + + return { + on: (channel, callback) => { + newStream.on(channel, callback) + }, + off: (channel, callback) => { + newStream.off(channel, callback) + }, + close: () => { + stream.close() + }, + } +} diff --git a/packages/sdk/src/response/response.js b/packages/sdk/src/response/response.js index 237696cf2..bfe33705b 100644 --- a/packages/sdk/src/response/response.js +++ b/packages/sdk/src/response/response.js @@ -11,7 +11,8 @@ const DEFAULT_RESPONSE = `{ "latestBlock":null, "collection":null, "networkParameters":null, - "dataStream":null + "dataStream":null, + "heartbeat":null }` export const response = () => JSON.parse(DEFAULT_RESPONSE) diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index 4694a2f83..6321cae15 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -39,6 +39,9 @@ "@onflow/util-pubsub": "^1.0.0", "@onflow/util-template": "^1.2.0", "abort-controller": "^3.0.0", - "cross-fetch": "^3.1.6" + "cross-fetch": "^3.1.6", + "events": "^3.3.0", + "isomorphic-ws": "^5.0.0", + "ws": "^8.14.2" } } diff --git a/packages/transport-http/src/connect-subscribe-events.test.js b/packages/transport-http/src/connect-subscribe-events.test.js new file mode 100644 index 000000000..7afd7ebad --- /dev/null +++ b/packages/transport-http/src/connect-subscribe-events.test.js @@ -0,0 +1,96 @@ +import {sendSubscribeEvents} from "./connect-subscribe-events" +import {Buffer} from "@onflow/rlp" +import { + build, + subscribeEvents, + subscription, + resolve, + response as responseADT, +} from "@onflow/sdk" +import {DataStream} from "@onflow/util-pubsub" + +describe("Subscribe Events", () => { + let mockStream + let subscribeWsMock + let unsubscribeMock + + beforeEach(async () => { + mockStream = new DataStream() + subscribeWsMock = jest.fn(() => mockStream) + unsubscribeMock = jest.fn() + + subscribeWsMock.mockReturnValue({ + unsubscribeCallback: unsubscribeMock, + }) + }) + + test("should initiate socket conection with correct params", async () => { + await sendSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + }, + }, + { + response: responseADT, + Buffer, + }, + { + subscribeWs: subscribeWsMock, + node: "localhost", + } + ) + + expect(subscribeWsMock).toHaveBeenCalledWith({ + hostname: "localhost", + path: "/v1/subscribe_events", + params: { + start_block_id: "abc123", + start_height: 1, + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1000, + }, + }) + }) + + test("should return a stream that pushes events when received from socket", async () => { + const response = await sendSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + }, + }, + { + response: responseADT, + Buffer, + }, + { + subscribeWs: subscribeWsMock, + node: "localhost", + } + ) + + response.subscribe(e => { + expect(e).toEqual({ + tag: "SUBSCRIBE_EVENTS", + dataStream: mockStream, + }) + }) + + mockStream.next("hello") + }) +}) diff --git a/packages/transport-http/src/connect-subscribe-events.js b/packages/transport-http/src/connect-subscribe-events.ts similarity index 54% rename from packages/transport-http/src/connect-subscribe-events.js rename to packages/transport-http/src/connect-subscribe-events.ts index 5ad269d14..66b1745f7 100644 --- a/packages/transport-http/src/connect-subscribe-events.js +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -1,11 +1,20 @@ import {invariant} from "@onflow/util-invariant" import {connectWs as defaultConnectWs} from "./connect-ws" +import {EventEmitter} from "events" +import {StreamConnection} from "@onflow/typedefs" -function constructData(ix, context, data) { +type SubscribeEventsStream = StreamConnection<{ + data: { + events: any[] + heartbeat: any + } +}> + +function constructData(ix: any, context: any, data: any) { // TODO REMOVE ME // DUMMY PAYLOAD UNTIL ACCESS NODE BUG IS FIXED if (data.Events) { - data.Events = data.Events.map(event => ({ + data.Events = data.Events.map((event: any) => ({ ...event, Payload: "eyJ2YWx1ZSI6eyJpZCI6IkEuOTEyZDU0NDBmN2UzNzY5ZS5GbG93RmVlcy5GZWVzRGVkdWN0ZWQiLCJmaWVsZHMiOlt7InZhbHVlIjp7InZhbHVlIjoiMC4wMDAwMDExOSIsInR5cGUiOiJVRml4NjQifSwibmFtZSI6ImFtb3VudCJ9LHsidmFsdWUiOnsidmFsdWUiOiIxLjAwMDAwMDAwIiwidHlwZSI6IlVGaXg2NCJ9LCJuYW1lIjoiaW5jbHVzaW9uRWZmb3J0In0seyJ2YWx1ZSI6eyJ2YWx1ZSI6IjAuMDAwMDAwMDQiLCJ0eXBlIjoiVUZpeDY0In0sIm5hbWUiOiJleGVjdXRpb25FZmZvcnQifV19LCJ0eXBlIjoiRXZlbnQifQo=", @@ -16,7 +25,7 @@ function constructData(ix, context, data) { ret.tag = ix.tag ret.events = data.Events - ? data.Events.map(event => ({ + ? data.Events.map((event: any) => ({ blockId: data.BlockID, blockHeight: Number(data.Height), blockTimestamp: data.Timestamp, @@ -29,11 +38,16 @@ function constructData(ix, context, data) { ), })) : [] + ret.heartbeat = { + blockId: data.BlockID, + blockHeight: Number(data.Height), + blockTimestamp: data.Timestamp, + } return ret } -function constructResponse(ix, context, stream) { +function constructResponse(ix: any, context: any, stream: any) { let ret = context.response() ret.tag = ix.tag @@ -42,7 +56,11 @@ function constructResponse(ix, context, stream) { return ret } -export async function connectSubscribeEvents(ix, context = {}, opts = {}) { +export async function connectSubscribeEvents( + ix: any, + context: any = {}, + opts: any = {} +) { invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`) invariant( context.response, @@ -55,9 +73,9 @@ export async function connectSubscribeEvents(ix, context = {}, opts = {}) { ix = await ix - const connectWs = opts.connectWs || defaultConnectWs + const connectWs: typeof defaultConnectWs = opts.connectWs || defaultConnectWs - const socketStream = connectWs({ + const connection = connectWs({ hostname: opts.node, path: `/v1/subscribe_events`, params: { @@ -70,8 +88,34 @@ export async function connectSubscribeEvents(ix, context = {}, opts = {}) { }, }) - // Map the data to the response object - const dataStream = socketStream.map(data => constructData(ix, context, data)) + // Map the connection to a formatted response stream + const formatterEmitter = new EventEmitter() + connection.on("data", (data: any) => { + formatterEmitter.emit("data", constructData(ix, context, data)) + }) + connection.on("error", (error: Error) => { + formatterEmitter.emit("error", error) + }) + connection.on("close", () => { + formatterEmitter.emit("close") + }) + connection.on("open", () => { + formatterEmitter.emit("open") + }) + + const responseStream: SubscribeEventsStream = { + on(event: "data" | "error" | "close" | "open", listener: any) { + formatterEmitter.on(event, listener) + return this + }, + off(event: "data" | "error" | "close" | "open", listener: any) { + formatterEmitter.off(event, listener) + return this + }, + close() { + connection.close() + }, + } - return constructResponse(ix, context, dataStream) + return constructResponse(ix, context, responseStream) } diff --git a/packages/transport-http/src/connect-ws.ts b/packages/transport-http/src/connect-ws.ts index 47c3d50a6..49fff63d0 100644 --- a/packages/transport-http/src/connect-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -1,47 +1,57 @@ +import {EventEmitter} from "events" import {safeParseJSON} from "./utils" -import {PubSub, DataStream} from "@onflow/util-pubsub" +import {StreamConnection} from "@onflow/typedefs" + +type WebSocketConnection = StreamConnection<{ + data: T +}> -// TODO: Implement retries export function connectWs({ hostname, path, params, - retryLimit = 5, - retryIntervalMs = 1000, }: { hostname: string path: string params?: Record - retryLimit?: number - retryIntervalMs?: number -}): DataStream { - // Build a data stream - const pubSub = new PubSub() - const dataStream = new DataStream(pubSub, closeConnection) - function closeConnection() { - ws.close() - } - +}): WebSocketConnection { // Build a websocket connection with correct protocol & params const url = buildConnectionUrl(hostname, path, params) const ws = new WebSocket(url) + const emitter = new EventEmitter() ws.onmessage = function (e) { const data = safeParseJSON(e.data) if (data) { - pubSub.next(data) + emitter.emit("data", data) } } + ws.onopen = function () { + emitter.emit("open") + } + ws.onclose = function () { - pubSub.complete() + emitter.emit("close") } ws.onerror = function (e) { - pubSub.error(e) + emitter.emit("error", e) } - return dataStream + return { + on(event: "data" | "close" | "error" | "open", listener: any) { + emitter.on(event, listener) + return this + }, + off(event: "data" | "close" | "error" | "open", listener: any) { + emitter.off(event, listener) + return this + }, + close() { + ws.close() + }, + } } function buildConnectionUrl( diff --git a/packages/transport-http/src/send-subscribe-events.test.js b/packages/transport-http/src/send-subscribe-events.test.js deleted file mode 100644 index 039fb50d6..000000000 --- a/packages/transport-http/src/send-subscribe-events.test.js +++ /dev/null @@ -1,93 +0,0 @@ -import {sendSubscribeEvents} from "./connect-subscribe-events" -import {Buffer} from "@onflow/rlp" -import { - build, - subscribeEvents, - subscription, - resolve, - response as responseADT, -} from "@onflow/sdk" - -describe("Subscribe Events", () => { - let subscribeWsMock - - let unsubscribe - let response - - beforeEach(async () => { - subscribeWsMock = jest.fn() - unsubscribe = jest.fn() - - subscribeWsMock.mockReturnValue({ - unsubscribeCallback: unsubscribe, - }) - - const response = await sendSubscribeEvents( - await resolve( - await build([ - subscribeEvents({ - startBlockId: "abc123", - startHeight: 1, - eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeatInterval: 1000, - }), - subscription({ - onData, - onError, - onComplete, - }), - ]) - ), - { - response: responseADT, - Buffer, - }, - { - subscribeWs: subscribeWsMock, - node: "localhost", - } - ) - }) - - test("calls subscribeWs with correct params", async () => { - expect(subscribeWsMock.mock.calls.length).toEqual(1) - - const httpRequestMockArgs = httpRequestMock.mock.calls[0] - - expect(httpRequestMockArgs.length).toEqual(1) - - const valueSent = httpRequestMock.mock.calls[0][0] - - expect(valueSent.hostname).toBe("localhost") - expect(valueSent.path).toBe("/v1/subscribe_events") - expect(valueSent.params).toEqual({ - start_block_id: "abc123", - start_height: 1, - event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeat_interval: 1000, - }) - }) - - test("calls onData with response ADT formatted data", async () => { - const data = {foo: "bar"} - - // Similate WS message - subscribeWsMock.mock.calls[0][0].onData(data) - - expect(onData.mock.calls.length).toEqual(1) - expect(onData.mock.calls[0][0]).toEqual({ - tag: "SubscribeEvents", - data: data, - }) - }) - - test("calling response.unsubscribeCallback calls subscribeWs unsubscribe", async () => { - response.unsubscribeCallback() - - expect(unsubscribe.mock.calls.length).toEqual(1) - }) -}) diff --git a/packages/transport-http/tsconfig.json b/packages/transport-http/tsconfig.json index 88be905eb..ac6917898 100644 --- a/packages/transport-http/tsconfig.json +++ b/packages/transport-http/tsconfig.json @@ -6,6 +6,6 @@ // Types should go into this directory. // Removing this would place the .d.ts files // next to the .js files - "outDir": "types", + "outDir": "types" } -} \ No newline at end of file +} diff --git a/packages/typedefs/src/index.ts b/packages/typedefs/src/index.ts index 5971369c2..964b0a716 100644 --- a/packages/typedefs/src/index.ts +++ b/packages/typedefs/src/index.ts @@ -288,3 +288,15 @@ export type Provider = { */ name: string } + +export interface StreamConnection { + on(event: keyof Events, listener: (data: Events[keyof Events]) => void): this + on(event: "close", listener: () => void): this + on(event: "error", listener: (err: any) => void): this + on(event: "open", listener: () => void): this + off(event: keyof Events, listener: (data: Events[keyof Events]) => void): this + off(event: "close", listener: () => void): this + off(event: "error", listener: (err: any) => void): this + off(event: "open", listener: () => void): this + close(): void +} diff --git a/packages/util-pubsub/tsconfig.json b/packages/util-pubsub/tsconfig.json index 88be905eb..ac6917898 100644 --- a/packages/util-pubsub/tsconfig.json +++ b/packages/util-pubsub/tsconfig.json @@ -6,6 +6,6 @@ // Types should go into this directory. // Removing this would place the .d.ts files // next to the .js files - "outDir": "types", + "outDir": "types" } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 244c48d2e..6dfe35b14 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ // go to js file when using IDE functions like // "Go to Definition" in VSCode "declarationMap": false, - "strict": true + "strict": true, + "allowSyntheticDefaultImports": true } } From eb11442a4ae604472e0b41a59d8d4f9c7d8bc824 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 02:48:40 -0800 Subject: [PATCH 06/54] Update tests & code to use emitter --- package-lock.json | 21 ++- packages/fcl/package.json | 1 + packages/fcl/src/events/index.test.ts | 53 ++++++ packages/fcl/src/events/index.ts | 67 ++----- packages/sdk/src/decode/decode-stream.test.ts | 106 ++++++++++++ packages/sdk/src/decode/decode-stream.ts | 71 ++++++++ packages/sdk/src/decode/decode.js | 163 ++++++------------ packages/sdk/src/decode/decode.test.js | 35 +++- packages/sdk/src/decode/sdk-decode.js | 11 +- .../src/connect-subscribe-events.test.js | 10 +- .../src/connect-subscribe-events.ts | 33 ++-- packages/typedefs/src/index.ts | 29 +++- packages/util-pubsub/.babelrc | 7 - packages/util-pubsub/package.json | 30 ---- packages/util-pubsub/src/data-stream.test.ts | 65 ------- packages/util-pubsub/src/data-stream.ts | 42 ----- packages/util-pubsub/src/index.ts | 2 - packages/util-pubsub/src/pub-sub.test.ts | 92 ---------- packages/util-pubsub/src/pub-sub.ts | 109 ------------ packages/util-pubsub/tsconfig.json | 11 -- 20 files changed, 408 insertions(+), 550 deletions(-) create mode 100644 packages/fcl/src/events/index.test.ts create mode 100644 packages/sdk/src/decode/decode-stream.test.ts create mode 100644 packages/sdk/src/decode/decode-stream.ts delete mode 100644 packages/util-pubsub/.babelrc delete mode 100644 packages/util-pubsub/package.json delete mode 100644 packages/util-pubsub/src/data-stream.test.ts delete mode 100644 packages/util-pubsub/src/data-stream.ts delete mode 100644 packages/util-pubsub/src/index.ts delete mode 100644 packages/util-pubsub/src/pub-sub.test.ts delete mode 100644 packages/util-pubsub/src/pub-sub.ts delete mode 100644 packages/util-pubsub/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 5a4fbc182..c2887ece9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4661,6 +4661,10 @@ "resolved": "packages/util-semver", "link": true }, + "node_modules/@onflow/util-stream": { + "resolved": "packages/util-stream", + "link": true + }, "node_modules/@onflow/util-template": { "resolved": "packages/util-template", "link": true @@ -5192,9 +5196,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.7", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.7.tgz", - "integrity": "sha512-HLyetab6KVPSiF+7pFcUyMeLsx25LDNDemw9mGsJBkai/oouwrjTycocSDYopMEwFhN2Y4s9oPyOCZNofgSt2g==", + "version": "29.5.10", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz", + "integrity": "sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -19916,6 +19920,7 @@ "@onflow/fcl-bundle": "^1.4.0", "@onflow/typedefs": "^1.2.0", "@types/estree": "^1.0.1", + "@types/jest": "^29.5.10", "@types/node": "^18.13.0", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", @@ -21559,6 +21564,16 @@ "jest": "^29.5.0" } }, + "packages/util-stream": { + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.23.2" + }, + "devDependencies": { + "jest": "^29.7.0" + } + }, "packages/util-template": { "name": "@onflow/util-template", "version": "1.2.0", diff --git a/packages/fcl/package.json b/packages/fcl/package.json index e2ea15be4..d389e9d83 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -22,6 +22,7 @@ "@onflow/fcl-bundle": "^1.4.0", "@onflow/typedefs": "^1.2.0", "@types/estree": "^1.0.1", + "@types/jest": "^29.5.10", "@types/node": "^18.13.0", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", diff --git a/packages/fcl/src/events/index.test.ts b/packages/fcl/src/events/index.test.ts new file mode 100644 index 000000000..e05473434 --- /dev/null +++ b/packages/fcl/src/events/index.test.ts @@ -0,0 +1,53 @@ +import {events} from "." +import * as sdk from "@onflow/sdk" + +test("events", () => { + let sendSpy + let decodeSpy + let subscribeEventsSpy + + beforeEach(() => { + const mockSubscribeEventsIx = {} + const mockEventsStream: SubscribeEventsStream = {} + + sendSpy = jest.spyOn(sdk, "send") + decodeSpy = jest.spyOn(sdk, "decode") + subscribeEventsSpy = jest.spyOn(sdk, "subscribeEvents") + + sendSpy.mockReturnValue() + decodeSpy.mockReturnValue() + subscribeEventsSpy.mockReturnValue(mockSubscribeEventsIx) + }) + + afterEach(() => { + sendSpy.mockRestore() + decodeSpy.mockRestore() + subscribeEventsSpy.mockRestore() + }) + + test("should call send with subscribeEvents", () => { + const filter = {eventTypes: ["A"]} + sdk.events(filter) + expect(sendSpy).toHaveBeenCalledWith([sdk.subscribeEvents(filter)]) + }) + + test("subscribe should pipe the events to the callback", async () => { + const filter = {eventTypes: ["A"]} + const callback = jest.fn() + const stream = { + on: jest.fn(), + } + const streamPromise = Promise.resolve(stream) + sendSpy.mockReturnValueOnce(streamPromise) + decodeSpy.mockReturnValueOnce(stream) + events(filter).subscribe(callback) + await streamPromise + expect(stream.on).toHaveBeenCalledWith("events", expect.any(Function)) + const onEvents = stream.on.mock.calls[0][1] + const event = {type: "A"} + onEvents(event) + expect(callback).toHaveBeenCalledWith(event, null) + }) + + test("subscribe should pipe the errors to the callback", async () => {}) +}) diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 24ebe890c..67509f01e 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -1,48 +1,5 @@ import {send, decode, subscribeEvents} from "@onflow/sdk" -import {DataStream} from "@onflow/util-pubsub" -import {Event} from "@onflow/typedefs" - -type EventTypeFilter = { - eventTypes?: string[] | string - addresses?: string[] | string - contracts?: string[] | string -} - -type NormalizedEventTypeFilter = { - eventTypes?: string[] - addresses?: string[] - contracts?: string[] -} - -function normalizeEventTypeFilter( - filterOrType?: EventTypeFilter | string -): NormalizedEventTypeFilter { - // Normalize the filter to arrays - if (typeof filterOrType === "string" && filterOrType !== "") { - return { - eventTypes: [filterOrType], - } - } else if (filterOrType == null || filterOrType === "") { - return {} - } else { - let {eventTypes, addresses, contracts} = filterOrType - let result: NormalizedEventTypeFilter = {} - - if (eventTypes != null) { - result.eventTypes = Array.isArray(eventTypes) ? eventTypes : [eventTypes] - } - - if (addresses != null) { - result.addresses = Array.isArray(addresses) ? addresses : [addresses] - } - - if (contracts != null) { - result.contracts = Array.isArray(contracts) ? contracts : [contracts] - } - - return result - } -} +import {Event, EventFilter, EventStream} from "@onflow/typedefs" /** * @typedef {import("@onflow/typedefs").Event} Event @@ -66,20 +23,25 @@ function normalizeEventTypeFilter( * import * as fcl from "@onflow/fcl" * fcl.events(eventName).subscribe((event) => console.log(event)) */ -export function events(filterOrType?: EventTypeFilter | string) { - const filter = normalizeEventTypeFilter(filterOrType) +export function events(filterOrType?: EventFilter | string) { + let filter: EventFilter + if (typeof filterOrType === "string") { + filter = {eventTypes: [filterOrType]} + } else { + filter = filterOrType || {} + } return { subscribe: ( - callback: (event: Event | null, error: Error | null) => void + callback: (events: Event | null, error: Error | null) => void ) => { - const streamPromise: Promise = send([subscribeEvents(filter)]).then( - decode - ) + const streamPromise: Promise = send([ + subscribeEvents(filter), + ]).then(decode) // Subscribe to the stream using the callback - function onEvents(data: any) { - callback(data, null) + function onEvents(data: Event[]) { + data.forEach(event => callback(event, null)) } function onError(error: Error) { callback(null, error) @@ -87,7 +49,6 @@ export function events(filterOrType?: EventTypeFilter | string) { streamPromise.then(stream => stream.on("events", onEvents).on("error", onError) ) - streamPromise.then(console.log) return () => { streamPromise.then(stream => stream.close()) diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts new file mode 100644 index 000000000..e970bb1af --- /dev/null +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -0,0 +1,106 @@ +import {EventEmitter} from "stream" +import {makeDecodeStream} from "./decode-stream" +import {StreamConnection} from "@onflow/typedefs" + +describe("decode stream", () => { + let mockDecodeResponse: jest.Mock + let decodeStream: ReturnType + let mockStream: StreamConnection<{data: any}> + let emitter: EventEmitter + + beforeEach(() => { + mockDecodeResponse = jest.fn() + decodeStream = makeDecodeStream(mockDecodeResponse) + emitter = new EventEmitter() + mockStream = { + on: jest.fn((event, callback) => { + emitter.on(event, callback) + }) as any, + off: jest.fn((event, callback) => { + emitter.off(event, callback) + }) as any, + close: jest.fn(), + } + }) + + test("data is mapped to decoded data per channel for non-null values", async () => { + const originalData = { + dummy: {foo: "bar"}, + other: {foo: "baz"}, + nullExample: null, + } + const decodedData = { + dummy: {foo2: "bar2"}, + other: {foo2: "baz2"}, + } + mockDecodeResponse.mockImplementation(response => { + if (response.dummy) { + return decodedData.dummy + } else if (response.other) { + return decodedData.other + } else { + throw new Error("unexpected response") + } + }) + + const decodedStream = decodeStream(mockStream) + const dummyCallback = jest.fn(data => { + expect(data).toEqual(decodedData.dummy) + }) + const otherCallback = jest.fn(data => { + expect(data).toEqual(decodedData.other) + }) + const nullCallback = jest.fn() + decodedStream.on("dummy", dummyCallback) + decodedStream.on("other", otherCallback) + decodedStream.on("nullExample", nullCallback) + + emitter.emit("data", originalData) + + // wait for next tick + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(mockDecodeResponse).toHaveBeenCalledWith({ + dummy: {foo: "bar"}, + }) + expect(mockDecodeResponse).toHaveBeenCalledWith({ + other: {foo: "baz"}, + }) + + expect(dummyCallback).toHaveBeenCalled() + expect(otherCallback).toHaveBeenCalled() + expect(nullCallback).not.toHaveBeenCalled() + }) + + test("data is emitted in order", async () => { + const incomingData = [{foo: "one"}, {bar: "two"}] + mockDecodeResponse.mockImplementation(async response => { + if (response.foo) { + await new Promise(resolve => setTimeout(resolve, 100)) + return response.foo + } else if (response.bar) { + return response.bar + } else { + throw new Error("unexpected response") + } + }) + + const decodedStream = decodeStream(mockStream) + const cb = jest.fn() + decodedStream.on("foo", msg => { + cb("foo", msg) + }) + decodedStream.on("bar", msg => { + cb("bar", msg) + }) + + emitter.emit("data", incomingData[0]) + emitter.emit("data", incomingData[1]) + + // Wait for data to be processed + await new Promise(resolve => setTimeout(resolve, 200)) + + expect(cb).toHaveBeenNthCalledWith(1, "foo", "one") + expect(cb).toHaveBeenNthCalledWith(2, "bar", "two") + }) +}) diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts new file mode 100644 index 000000000..6409fd3a1 --- /dev/null +++ b/packages/sdk/src/decode/decode-stream.ts @@ -0,0 +1,71 @@ +import EventEmitter from "events" +import {StreamConnection} from "@onflow/typedefs" +import type {makeDecodeResponse} from "./decode" + +type DecodeResponseFn = ReturnType + +// This function pipes a generic stream of data into a granular stream of decoded data +export const makeDecodeStream = + (decodeResponse: DecodeResponseFn) => + (stream: StreamConnection<{data: any}>) => { + const newStream = new EventEmitter() + let queue: Promise[] = [] + + // Data is separated by topic & the decoded data is emitted in order + // All topics for a given message will be emitted synchronously before moving on to the next message + // The streamReady promise ensures that the data is emitted in order and avoids race conditions when decoding + stream.on("data", async data => { + const topics = Object.keys(data).filter( + key => data[key] != null && key !== "tag" + ) + + let newDataPromise = Promise.all( + topics.map(async channel => { + const partialResponse = { + [channel]: data[channel], + } + const message = await decodeResponse(partialResponse) + return { + channel, + message, + } + }) + ) + queue.push(newDataPromise) + + // Wait for the previous data to be emitted before emitting the new data + await queue[0] + queue.shift() + + // Emit the new data + const newData = await newDataPromise + newData.forEach(({channel, message}) => { + newStream.emit(channel, message) + }) + }) + + // Relay events from the original stream + // These events are delivered in order as well so that the stream will + // not emit more data after it has announced a contradictory state + function relayEvent(event: any) { + stream.on(event, async (message: any) => { + await queue.at(-1) + newStream.emit(event, message) + }) + } + relayEvent("open") + relayEvent("close") + relayEvent("error") + + return { + on: (channel: string, callback: any) => { + newStream.on(channel, callback) + }, + off: (channel: string, callback: any) => { + newStream.off(channel, callback) + }, + close: () => { + stream.close() + }, + } + } diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index 6d61fad4d..d6718a1a9 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -1,5 +1,4 @@ import {log} from "@onflow/util-logger" -import EventEmitter from "events" const latestBlockDeprecationNotice = () => { log.deprecate({ @@ -166,15 +165,35 @@ export const decode = async ( return recurseDecode(decodeInstructions, decoders, stack) } -export const decodeResponse = async (response, customDecoders = {}) => { - if (response.encodedData) { - return decode(response.encodedData, customDecoders) - } else if (response.transactionStatus) { - return { - ...response.transactionStatus, - events: await Promise.all( - response.transactionStatus.events.map(async function decodeEvents(e) { +export const makeDecodeResponse = + (decodeStream, customDecoders = {}) => + async response => { + if (response.encodedData) { + return decode(response.encodedData, customDecoders) + } else if (response.transactionStatus) { + return { + ...response.transactionStatus, + events: await Promise.all( + response.transactionStatus.events.map(async function decodeEvents(e) { + return { + type: e.type, + transactionId: e.transactionId, + transactionIndex: e.transactionIndex, + eventIndex: e.eventIndex, + data: await decode(e.payload, customDecoders), + } + }) + ), + } + } else if (response.transaction) { + return response.transaction + } else if (response.events) { + return await Promise.all( + response.events.map(async function decodeEvents(e) { return { + blockId: e.blockId, + blockHeight: e.blockHeight, + blockTimestamp: e.blockTimestamp, type: e.type, transactionId: e.transactionId, transactionIndex: e.transactionIndex, @@ -182,103 +201,35 @@ export const decodeResponse = async (response, customDecoders = {}) => { data: await decode(e.payload, customDecoders), } }) - ), + ) + } else if (response.account) { + return response.account + } else if (response.block) { + return response.block + } else if (response.blockHeader) { + return response.blockHeader + } else if (response.latestBlock) { + latestBlockDeprecationNotice() + return response.latestBlock + } else if (response.transactionId) { + return response.transactionId + } else if (response.collection) { + return response.collection + } else if (response.networkParameters) { + const chainIdMap = { + "flow-testnet": "testnet", + "flow-mainnet": "mainnet", + "flow-emulator": "local", + } + + return { + chainId: chainIdMap[response.networkParameters.chainId], + } + } else if (response.dataStream) { + return decodeStream(response.dataStream) + } else if (response.heartbeat) { + return response.heartbeat } - } else if (response.transaction) { - return response.transaction - } else if (response.events) { - return await Promise.all( - response.events.map(async function decodeEvents(e) { - return { - blockId: e.blockId, - blockHeight: e.blockHeight, - blockTimestamp: e.blockTimestamp, - type: e.type, - transactionId: e.transactionId, - transactionIndex: e.transactionIndex, - eventIndex: e.eventIndex, - data: await decode(e.payload, customDecoders), - } - }) - ) - } else if (response.account) { - return response.account - } else if (response.block) { - return response.block - } else if (response.blockHeader) { - return response.blockHeader - } else if (response.latestBlock) { - latestBlockDeprecationNotice() - return response.latestBlock - } else if (response.transactionId) { - return response.transactionId - } else if (response.collection) { - return response.collection - } else if (response.networkParameters) { - const chainIdMap = { - "flow-testnet": "testnet", - "flow-mainnet": "mainnet", - "flow-emulator": "local", - } - - return { - chainId: chainIdMap[response.networkParameters.chainId], - } - } else if (response.dataStream) { - return decodeStream(response.dataStream, customDecoders) - } else if (response.heartbeat) { - return response.heartbeat - } - - return null -} - -// This function pipes a generic stream of data into a granular stream of decoded data -export function decodeStream(stream, customDecoders) { - const newStream = new EventEmitter() - let topicMutex = {} - - // Data is separated by topic & the decoded data is emitted in order by topic - // The mutex ensures that the data is emitted in order and avoids race conditions when decoding - stream.on("data", async data => { - const topics = Object.keys(data).filter(Boolean) - - for (const channel of topics) { - const currentMutex = topicMutex[channel] || Promise.resolve() - topicMutex[channel] = currentMutex.then(async () => { - const partialResponse = { - [channel]: data[channel], - } - const decoded = await decodeResponse(partialResponse, customDecoders) - newStream.emit(channel, decoded) - }) - } - }) - - // Error event is emitted as they are received - stream.on("error", error => { - newStream.emit("error", error) - }) - - // Close event is emitted the way it is received - stream.on("close", () => { - newStream.emit("close") - }) - - // Open event is emitted the way it is received - stream.on("open", () => { - newStream.emit("open") - }) - return { - on: (channel, callback) => { - newStream.on(channel, callback) - }, - off: (channel, callback) => { - newStream.off(channel, callback) - }, - close: () => { - stream.close() - }, + return null } -} diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index df53fc346..9fbb8935d 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1,5 +1,5 @@ import * as root from "./decode.js" -import {decode, decodeResponse} from "./decode.js" +import {decode, makeDecodeResponse} from "./decode.js" import {Buffer} from "@onflow/rlp" it("exported interface contract", () => { @@ -25,6 +25,7 @@ it("decodeResponse", async () => { ), } + const decodeResponse = makeDecodeResponse(() => {}) const data = await decodeResponse(response) expect(data).toBe("7") }) @@ -1317,6 +1318,7 @@ describe("custom decoder tests", () => { describe("decode GetEvents tests", () => { it("decodes a GetEvents response correctly", async () => { + const decodeResponse = makeDecodeResponse(() => {}) const timestampISOString = new Date().toISOString() const getEventsResponse = { @@ -1351,6 +1353,7 @@ describe("decode GetEvents tests", () => { describe("decode GetTransactionStatus tests", () => { it("decodes a GetEvents response correctly", async () => { + const decodeResponse = makeDecodeResponse(() => {}) const getTransactionStatusResponse = { transactionStatus: { status: 4, @@ -1387,3 +1390,33 @@ describe("decode GetTransactionStatus tests", () => { }) }) }) + +describe("decode data stream tests", () => { + it("calls decodeStream to decode data streams", async () => { + let mockStream = {} + const streamResponse = { + dataStream: mockStream, + } + const decodeStream = jest.fn() + const decodeResponse = makeDecodeResponse(decodeStream) + const decoded = await decodeResponse(streamResponse) + + expect(decoded).toBe(mockStream) + expect(decodeStream).toHaveBeenCalledWith(mockStream) + }) +}) + +describe("decode heartbeat tests", () => { + it("decodes a heartbeat response correctly", async () => { + const decodeResponse = makeDecodeResponse(() => {}) + const heartbeatResponse = { + heartbeat: { + timestamp: 123456789, + }, + } + + expect(await decodeResponse(heartbeatResponse)).toStrictEqual({ + timestamp: 123456789, + }) + }) +}) diff --git a/packages/sdk/src/decode/sdk-decode.js b/packages/sdk/src/decode/sdk-decode.js index d28081be2..7d0c7bf9b 100644 --- a/packages/sdk/src/decode/sdk-decode.js +++ b/packages/sdk/src/decode/sdk-decode.js @@ -1,5 +1,6 @@ -import {decodeResponse} from "./decode.js" import {config} from "@onflow/config" +import {makeDecodeResponse} from "./decode" +import {makeDecodeStream} from "./decode-stream.js" export async function decode(response) { const decodersFromConfig = await config().where(/^decoder\./) @@ -10,5 +11,11 @@ export async function decode(response) { } ) - return decodeResponse(response, Object.fromEntries(decoders)) + const decodeResponse = makeDecodeResponse( + decodeStream, + Object.fromEntries(decoders) + ) + const decodeStream = makeDecodeStream(decodeResponse) + + return decodeResponse(response) } diff --git a/packages/transport-http/src/connect-subscribe-events.test.js b/packages/transport-http/src/connect-subscribe-events.test.js index 7afd7ebad..9f2502d1a 100644 --- a/packages/transport-http/src/connect-subscribe-events.test.js +++ b/packages/transport-http/src/connect-subscribe-events.test.js @@ -1,13 +1,7 @@ import {sendSubscribeEvents} from "./connect-subscribe-events" import {Buffer} from "@onflow/rlp" -import { - build, - subscribeEvents, - subscription, - resolve, - response as responseADT, -} from "@onflow/sdk" -import {DataStream} from "@onflow/util-pubsub" +import {response as responseADT} from "@onflow/sdk" +import {DataStream} from "../../util-stream/dist" describe("Subscribe Events", () => { let mockStream diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index 66b1745f7..e60da5ec3 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -3,7 +3,7 @@ import {connectWs as defaultConnectWs} from "./connect-ws" import {EventEmitter} from "events" import {StreamConnection} from "@onflow/typedefs" -type SubscribeEventsStream = StreamConnection<{ +type RawSubscribeEventsStream = StreamConnection<{ data: { events: any[] heartbeat: any @@ -24,20 +24,21 @@ function constructData(ix: any, context: any, data: any) { let ret = context.response() ret.tag = ix.tag - ret.events = data.Events - ? data.Events.map((event: any) => ({ - blockId: data.BlockID, - blockHeight: Number(data.Height), - blockTimestamp: data.Timestamp, - type: event.Type, - transactionId: event.TransactionID, - transactionIndex: Number(event.TransactionIndex), - eventIndex: Number(event.EventIndex), - payload: JSON.parse( - context.Buffer.from(event.Payload, "base64").toString() - ), - })) - : [] + ret.events = + data.Events?.length > 0 + ? data.Events.map((event: any) => ({ + blockId: data.BlockID, + blockHeight: Number(data.Height), + blockTimestamp: data.Timestamp, + type: event.Type, + transactionId: event.TransactionID, + transactionIndex: Number(event.TransactionIndex), + eventIndex: Number(event.EventIndex), + payload: JSON.parse( + context.Buffer.from(event.Payload, "base64").toString() + ), + })) + : null ret.heartbeat = { blockId: data.BlockID, blockHeight: Number(data.Height), @@ -103,7 +104,7 @@ export async function connectSubscribeEvents( formatterEmitter.emit("open") }) - const responseStream: SubscribeEventsStream = { + const responseStream: RawSubscribeEventsStream = { on(event: "data" | "error" | "close" | "open", listener: any) { formatterEmitter.on(event, listener) return this diff --git a/packages/typedefs/src/index.ts b/packages/typedefs/src/index.ts index 964b0a716..575f0b42c 100644 --- a/packages/typedefs/src/index.ts +++ b/packages/typedefs/src/index.ts @@ -289,14 +289,37 @@ export type Provider = { name: string } -export interface StreamConnection { - on(event: keyof Events, listener: (data: Events[keyof Events]) => void): this +export interface StreamConnection { + on( + channel: C, + listener: (data: ChannelMap[C]) => void + ): this on(event: "close", listener: () => void): this on(event: "error", listener: (err: any) => void): this on(event: "open", listener: () => void): this - off(event: keyof Events, listener: (data: Events[keyof Events]) => void): this + off( + event: C, + listener: (data: ChannelMap[C]) => void + ): this off(event: "close", listener: () => void): this off(event: "error", listener: (err: any) => void): this off(event: "open", listener: () => void): this close(): void } + +export interface EventFilter { + eventTypes?: string[] + addresses?: string[] + contracts?: string[] +} + +export interface EventStreamHeartbeat { + blockId: string + blockHeight: number + timestamp: string +} + +export type EventStream = StreamConnection<{ + events: Event[] + heartbeat: EventStreamHeartbeat +}> diff --git a/packages/util-pubsub/.babelrc b/packages/util-pubsub/.babelrc deleted file mode 100644 index 67fc2886b..000000000 --- a/packages/util-pubsub/.babelrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env" - ] - ] -} diff --git a/packages/util-pubsub/package.json b/packages/util-pubsub/package.json deleted file mode 100644 index 3866a1f19..000000000 --- a/packages/util-pubsub/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@onflow/util-pubsub", - "version": "1.0.0", - "description": "Lightweight utility for creating pubsub model", - "main": "dist/index.js", - "scripts": { - "prepublishOnly": "npm test && npm run build", - "test": "jest", - "build": "fcl-bundle", - "test:watch": "jest --watch", - "start": "fcl-bundle --watch" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/onflow/fcl-js.git" - }, - "author": "Flow Blockchain", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/onflow/fcl-js/issues" - }, - "homepage": "https://github.com/onflow/fcl-js#readme", - "devDependencies": { - "jest": "^29.7.0" - }, - "dependencies": { - "@babel/runtime": "^7.23.2" - }, - "source": "src/index.ts" -} diff --git a/packages/util-pubsub/src/data-stream.test.ts b/packages/util-pubsub/src/data-stream.test.ts deleted file mode 100644 index 7b07a9c21..000000000 --- a/packages/util-pubsub/src/data-stream.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {DataStream} from "./data-stream" -describe("data-stream", () => { - test("should subscribe to a stream", () => { - const mockPubSub = { - subscribe: jest.fn(), - } - const mockCloseFn = jest.fn() - const dataStream = new DataStream(mockPubSub, mockCloseFn) - const mockHandlers = { - next: jest.fn(), - error: jest.fn(), - close: jest.fn(), - } - dataStream.subscribe( - mockHandlers.next, - mockHandlers.error, - mockHandlers.close - ) - expect(mockPubSub.subscribe).toHaveBeenCalledTimes(1) - expect(mockPubSub.subscribe).toHaveBeenCalledWith( - mockHandlers.next, - mockHandlers.error, - mockHandlers.close - ) - }) - - test("should close a stream", () => { - const mockPubSub = { - subscribe: jest.fn(), - } - const mockCloseFn = jest.fn() - const dataStream = new DataStream(mockPubSub, mockCloseFn) - dataStream.close() - expect(mockCloseFn).toHaveBeenCalledTimes(1) - }) - - test("should map a stream", () => { - const mockPubSub = { - subscribe: jest.fn(), - } - const mockCloseFn = jest.fn() - const dataStream = new DataStream(mockPubSub, mockCloseFn) - const mockHandlers = { - next: jest.fn(), - error: jest.fn(), - close: jest.fn(), - } - const mappedDataStream = dataStream.map(value => value + 1) - mappedDataStream.subscribe( - mockHandlers.next, - mockHandlers.error, - mockHandlers.close - ) - expect(mockPubSub.subscribe).toHaveBeenCalledTimes(1) - expect(mockPubSub.subscribe).toHaveBeenCalledWith( - expect.any(Function), - mockHandlers.error, - mockHandlers.close - ) - const mockNext = mockPubSub.subscribe.mock.calls[0][0] - mockNext(1) - expect(mockHandlers.next).toHaveBeenCalledTimes(1) - expect(mockHandlers.next).toHaveBeenCalledWith(2) - }) -}) diff --git a/packages/util-pubsub/src/data-stream.ts b/packages/util-pubsub/src/data-stream.ts deleted file mode 100644 index 48641c427..000000000 --- a/packages/util-pubsub/src/data-stream.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {Subscribable, Subscription} from "./pub-sub" - -/** - * Used to create a stream of data from a PubSub instance with a callback for the receiver to close the stream. - */ -export class DataStream implements Subscribable { - constructor(private stream: Subscribable, private closeFn: () => void) {} - - subscribe( - next?: (value: T) => void, - error?: (err: any) => void, - close?: () => void - ): Subscription { - return this.stream.subscribe(next, error, close) - } - - close(): void { - this.closeFn() - } - - /** - * Map the data stream to a new stream of data. - * @param fn The function to map the data. - * @returns A new DataStream instance. - */ - map(fn: (value: T) => R | Promise): DataStream { - return new DataStream( - { - subscribe: (next, error, close) => { - return this.subscribe( - async value => { - next && next(await fn(value)) - }, - error, - close - ) - }, - }, - this.closeFn - ) - } -} diff --git a/packages/util-pubsub/src/index.ts b/packages/util-pubsub/src/index.ts deleted file mode 100644 index fef90fdff..000000000 --- a/packages/util-pubsub/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./data-stream" -export * from "./pub-sub" diff --git a/packages/util-pubsub/src/pub-sub.test.ts b/packages/util-pubsub/src/pub-sub.test.ts deleted file mode 100644 index 58439704a..000000000 --- a/packages/util-pubsub/src/pub-sub.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {PubSub} from "./pub-sub" - -function flushEvents() { - return new Promise(resolve => setTimeout(resolve, 0)) -} - -describe("util-pubsub", () => { - it("should publish messages asynchronously", async () => { - const pubsub = new PubSub() - const messages: string[] = [] - const subscription = pubsub.subscribe(message => { - messages.push(message) - }) - pubsub.next("foo") - pubsub.next("bar") - subscription.unsubscribe() - pubsub.next("baz") - - await flushEvents() - expect(messages).toEqual(["foo", "bar"]) - }) - - it("should publish messages synchronously", () => { - const pubsub = new PubSub() - const messages: string[] = [] - const subscription = pubsub.subscribe(message => { - messages.push(message) - }) - pubsub.next("foo", true) - pubsub.next("bar", true) - subscription.unsubscribe() - pubsub.next("baz", true) - expect(messages).toEqual(["foo", "bar"]) - }) - - it("should publish errors", async () => { - const pubsub = new PubSub() - const errors: any[] = [] - const subscription = pubsub.subscribe(undefined, error => { - errors.push(error) - }) - pubsub.error("foo") - pubsub.error("bar") - subscription.unsubscribe() - pubsub.error("baz") - - await flushEvents() - expect(errors).toEqual(["foo", "bar"]) - }) - - it("should publish errors synchronously", () => { - const pubsub = new PubSub() - const errors: any[] = [] - const subscription = pubsub.subscribe(undefined, error => { - errors.push(error) - }) - pubsub.error("foo", true) - pubsub.error("bar", true) - subscription.unsubscribe() - pubsub.error("baz", true) - - expect(errors).toEqual(["foo", "bar"]) - }) - - it("should publish completion", async () => { - const pubsub = new PubSub() - const completions: any[] = [] - const subscription = pubsub.subscribe(undefined, undefined, () => { - completions.push(true) - }) - jest.spyOn(subscription, "unsubscribe") - pubsub.complete(true) - - await flushEvents() - - expect(completions).toEqual([true, true]) - expect(subscription.unsubscribe).toHaveBeenCalledTimes(1) - }) - - it("should publish completion synchronously", () => { - const pubsub = new PubSub() - const completions: any[] = [] - const subscription = pubsub.subscribe(undefined, undefined, () => { - completions.push(true) - }) - jest.spyOn(subscription, "unsubscribe") - pubsub.complete(true) - - expect(completions).toEqual([true, true]) - expect(subscription.unsubscribe).toHaveBeenCalledTimes(1) - }) -}) diff --git a/packages/util-pubsub/src/pub-sub.ts b/packages/util-pubsub/src/pub-sub.ts deleted file mode 100644 index d0471d92b..000000000 --- a/packages/util-pubsub/src/pub-sub.ts +++ /dev/null @@ -1,109 +0,0 @@ -export type Operator = Subscribable> = ( - source: Subscribable -) => S -export class Subscriber { - private nextMutex = Promise.resolve() - private errorMutex = Promise.resolve() - private completeMutex = Promise.resolve() - - constructor( - private readonly _next?: (value: T) => void | Promise, - private readonly _error?: (err: any) => void | Promise, - private readonly _complete?: () => void | Promise - ) {} - - next(value: T) { - setTimeout(() => { - this.nextMutex = deliverMessage() - }, 0) - - const deliverMessage = async () => { - await this.nextMutex - if (this._next) await this._next(value) - } - } - - error(err: any) { - setTimeout(() => { - this.errorMutex = deliverMessage() - }, 0) - - const deliverMessage = async () => { - await this.errorMutex - if (this._error) await this._error(err) - } - } - - complete() { - setTimeout(() => { - this.completeMutex = deliverMessage() - }, 0) - - const deliverMessage = async () => { - await this.completeMutex - if (this._complete) await this._complete() - } - } -} - -export interface Subscription { - unsubscribe: () => void -} - -export interface Subscribable { - subscribe: ( - next?: (value: T) => void, - error?: (err: any) => void, - complete?: () => void - ) => Subscription -} - -/** - * Non-blocking pub-sub implementation. Observers are notified after the event loop flushes. - */ -export class PubSub implements Subscribable { - private observers: Subscriber[] = [] - - subscribe( - next?: (value: T) => void | Promise, - error?: (err: any) => void | Promise, - complete?: () => void | Promise - ): Subscription { - const observer: Subscriber = new Subscriber(next, error, complete) - this.observers.push(observer) - - return { - unsubscribe: () => { - const index = this.observers.indexOf(observer) - if (index > -1) { - this.observers.splice(index, 1) - } - }, - } - } - - next(data: T, sync = false) { - this.observers.forEach(observer => observer.next(data)) - } - - error(err: any, sync = false) { - this.observers.forEach(observer => observer.error(err)) - this.observers = [] - } - - /** - * - * @param sync - */ - complete(sync = false) { - this.observers.forEach(observer => observer.complete()) - this.observers = [] - } - - /** - * Returns the number of subscribers connected to the PubSub instance. - */ - get subscriberCount() { - return this.observers.length - } -} diff --git a/packages/util-pubsub/tsconfig.json b/packages/util-pubsub/tsconfig.json deleted file mode 100644 index ac6917898..000000000 --- a/packages/util-pubsub/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig", - // Change this to match your project - "include": ["src/**/*"], - "compilerOptions": { - // Types should go into this directory. - // Removing this would place the .d.ts files - // next to the .js files - "outDir": "types" - } -} From 973460a3ca96b34c47f0818b4d8f8fe73cc89a24 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 03:11:08 -0800 Subject: [PATCH 07/54] Add reconnect mechanism --- .../src/connect-subscribe-events.ts | 69 +++++++++++-------- packages/transport-http/src/connect-ws.ts | 13 +++- 2 files changed, 53 insertions(+), 29 deletions(-) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index e60da5ec3..e5a36ceb4 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -75,48 +75,63 @@ export async function connectSubscribeEvents( ix = await ix const connectWs: typeof defaultConnectWs = opts.connectWs || defaultConnectWs + const outputEmitter = new EventEmitter() + let close = () => {} + let lastBlockId: string | null = null - const connection = connectWs({ - hostname: opts.node, - path: `/v1/subscribe_events`, - params: { - start_block_id: ix.subscribeEvents.startBlockId, - start_height: ix.subscribeEvents.startHeight, + ;(function connect() { + const params: Record = { event_types: ix.subscribeEvents.eventTypes, addresses: ix.subscribeEvents.addresses, contracts: ix.subscribeEvents.contracts, heartbeat_interval: ix.subscribeEvents.heartbeatInterval, - }, - }) - - // Map the connection to a formatted response stream - const formatterEmitter = new EventEmitter() - connection.on("data", (data: any) => { - formatterEmitter.emit("data", constructData(ix, context, data)) - }) - connection.on("error", (error: Error) => { - formatterEmitter.emit("error", error) - }) - connection.on("close", () => { - formatterEmitter.emit("close") - }) - connection.on("open", () => { - formatterEmitter.emit("open") - }) + } + + // If the lastBlockId is set, use it to resume the stream + if (lastBlockId) { + params.start_block_id = lastBlockId + } else { + params.start_block_id = ix.subscribeEvents.startBlockId + params.start_height = ix.subscribeEvents.startHeight + } + + // Connect to the websocket + const connection = connectWs({ + hostname: opts.node, + path: `/v1/subscribe_events`, + params, + }) + + // Map the connection to a formatted response stream + connection.on("data", (data: any) => { + const responseData = constructData(ix, context, data) + lastBlockId = responseData.heartbeat.blockId + outputEmitter.emit("data", responseData) + }) + connection.on("error", (error: Error) => { + outputEmitter.emit("error", error) + }) + connection.on("close", () => { + connect() + }) + connection.on("open", () => { + outputEmitter.emit("open") + }) + close = () => connection.close() + })() const responseStream: RawSubscribeEventsStream = { on(event: "data" | "error" | "close" | "open", listener: any) { - formatterEmitter.on(event, listener) + outputEmitter.on(event, listener) return this }, off(event: "data" | "error" | "close" | "open", listener: any) { - formatterEmitter.off(event, listener) + outputEmitter.off(event, listener) return this }, close() { - connection.close() + close() }, } - return constructResponse(ix, context, responseStream) } diff --git a/packages/transport-http/src/connect-ws.ts b/packages/transport-http/src/connect-ws.ts index 49fff63d0..95ef93760 100644 --- a/packages/transport-http/src/connect-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -57,7 +57,10 @@ export function connectWs({ function buildConnectionUrl( hostname: string, path: string, - params?: Record + params?: Record< + string, + string | number | string[] | number[] | null | undefined + > ) { const url = new URL(path, hostname) if (url.protocol === "https:") { @@ -68,7 +71,13 @@ function buildConnectionUrl( Object.entries(params || {}).forEach(([key, value]) => { if (value) { - url.searchParams.append(key, value) + let formattedValue: string + if (Array.isArray(value)) { + formattedValue = value.join(",") + } else { + formattedValue = value.toString() + } + url.searchParams.append(key, formattedValue) } }) From 627b32ade66744ecbd9eecfaa0d4e095d8be3e5c Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 12:22:42 -0800 Subject: [PATCH 08/54] Update tests --- packages/fcl/src/events/index.test.ts | 84 +++++++++++++------ packages/sdk/src/decode/decode-stream.test.ts | 5 +- .../src/connect-subscribe-events.ts | 6 +- 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/packages/fcl/src/events/index.test.ts b/packages/fcl/src/events/index.test.ts index e05473434..070e64361 100644 --- a/packages/fcl/src/events/index.test.ts +++ b/packages/fcl/src/events/index.test.ts @@ -1,53 +1,89 @@ +import {EventStream} from "@onflow/typedefs" import {events} from "." import * as sdk from "@onflow/sdk" -test("events", () => { +describe("events", () => { let sendSpy let decodeSpy let subscribeEventsSpy + let mockEventsStream: EventStream beforeEach(() => { - const mockSubscribeEventsIx = {} - const mockEventsStream: SubscribeEventsStream = {} + mockEventsStream = { + on: jest.fn(() => mockEventsStream), + off: jest.fn(() => mockEventsStream), + close: jest.fn(), + } sendSpy = jest.spyOn(sdk, "send") decodeSpy = jest.spyOn(sdk, "decode") subscribeEventsSpy = jest.spyOn(sdk, "subscribeEvents") - sendSpy.mockReturnValue() - decodeSpy.mockReturnValue() - subscribeEventsSpy.mockReturnValue(mockSubscribeEventsIx) + sendSpy.mockReturnValue(Promise.resolve(mockEventsStream)) + decodeSpy.mockImplementation(async x => x) + subscribeEventsSpy.mockImplementation(async x => x) }) afterEach(() => { - sendSpy.mockRestore() - decodeSpy.mockRestore() - subscribeEventsSpy.mockRestore() + jest.clearAllMocks() }) - test("should call send with subscribeEvents", () => { + test("subscribe should call send with the subscribeEvents ix", () => { const filter = {eventTypes: ["A"]} - sdk.events(filter) + events(filter).subscribe(() => {}) expect(sendSpy).toHaveBeenCalledWith([sdk.subscribeEvents(filter)]) }) + test("should work with a string", () => { + events("A").subscribe(() => {}) + expect(sendSpy).toHaveBeenCalledWith([ + sdk.subscribeEvents({eventTypes: ["A"]}), + ]) + }) + + test("should work with empty args", () => { + events().subscribe(() => {}) + expect(sendSpy).toHaveBeenCalledWith([sdk.subscribeEvents({})]) + }) + test("subscribe should pipe the events to the callback", async () => { const filter = {eventTypes: ["A"]} const callback = jest.fn() - const stream = { - on: jest.fn(), - } - const streamPromise = Promise.resolve(stream) - sendSpy.mockReturnValueOnce(streamPromise) - decodeSpy.mockReturnValueOnce(stream) + + const mockEvents = [{type: "A"}, {type: "B"}] + + mockEventsStream.on.mockImplementation((event, cb) => { + if (event === "events") { + cb(mockEvents) + } + return mockEventsStream + }) + events(filter).subscribe(callback) - await streamPromise - expect(stream.on).toHaveBeenCalledWith("events", expect.any(Function)) - const onEvents = stream.on.mock.calls[0][1] - const event = {type: "A"} - onEvents(event) - expect(callback).toHaveBeenCalledWith(event, null) + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(callback.mock.calls).toEqual([ + [mockEvents[0], null], + [mockEvents[1], null], + ]) }) - test("subscribe should pipe the errors to the callback", async () => {}) + test("subscribe should pipe the errors to the callback", async () => { + const filter = {eventTypes: ["A"]} + const callback = jest.fn() + + const mockError = new Error("mock error") + + mockEventsStream.on.mockImplementation((event, cb) => { + if (event === "error") { + cb(mockError) + } + return mockEventsStream + }) + + events(filter).subscribe(callback) + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(callback.mock.calls).toEqual([[null, mockError]]) + }) }) diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts index e970bb1af..6fab065f1 100644 --- a/packages/sdk/src/decode/decode-stream.test.ts +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -4,13 +4,14 @@ import {StreamConnection} from "@onflow/typedefs" describe("decode stream", () => { let mockDecodeResponse: jest.Mock - let decodeStream: ReturnType + let decodeStream: any let mockStream: StreamConnection<{data: any}> let emitter: EventEmitter beforeEach(() => { mockDecodeResponse = jest.fn() - decodeStream = makeDecodeStream(mockDecodeResponse) + decodeStream = async (...args) => + makeDecodeStream(mockDecodeResponse)(...args) emitter = new EventEmitter() mockStream = { on: jest.fn((event, callback) => { diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index e5a36ceb4..2d7d9a1de 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -6,7 +6,11 @@ import {StreamConnection} from "@onflow/typedefs" type RawSubscribeEventsStream = StreamConnection<{ data: { events: any[] - heartbeat: any + heartbeat: { + blockId: string + blockHeight: number + blockTimestamp: number + } } }> From 142fd3c2df0e74d604db3ca6acff0fe3114ba43d Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 14:49:37 -0800 Subject: [PATCH 09/54] transport http tests --- packages/transport-http/.babelrc | 6 +- .../transport-http/src/connect-ws.test.ts | 126 ++++++++++++++++++ packages/transport-http/src/connect-ws.ts | 14 +- packages/transport-http/src/websocket.ts | 6 + packages/transport-http/tsconfig.json | 3 +- 5 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 packages/transport-http/src/connect-ws.test.ts create mode 100644 packages/transport-http/src/websocket.ts diff --git a/packages/transport-http/.babelrc b/packages/transport-http/.babelrc index 67fc2886b..d766c90b2 100644 --- a/packages/transport-http/.babelrc +++ b/packages/transport-http/.babelrc @@ -1,7 +1,3 @@ { - "presets": [ - [ - "@babel/preset-env" - ] - ] + "presets": [["@babel/preset-env"], "@babel/preset-typescript"] } diff --git a/packages/transport-http/src/connect-ws.test.ts b/packages/transport-http/src/connect-ws.test.ts new file mode 100644 index 000000000..1e55aa5b2 --- /dev/null +++ b/packages/transport-http/src/connect-ws.test.ts @@ -0,0 +1,126 @@ +import {buildConnectionUrl, connectWs} from "./connect-ws" +import * as WebSocketModule from "./websocket" + +describe("connectWs", () => { + describe("buildConnectionUrl", () => { + test("should build http url", () => { + const url = buildConnectionUrl("http://example.com", "/events", { + a: "b", + c: "d", + }) + expect(url).toEqual("ws://example.com/events?a=b&c=d") + }) + + test("should build https url", () => { + const url = buildConnectionUrl("https://example.com", "/events", { + a: "b", + c: "d", + }) + expect(url).toEqual("wss://example.com/events?a=b&c=d") + }) + + test("should preserve port", () => { + const url = buildConnectionUrl("http://example.com:8080", "/events", { + a: "b", + c: "d", + }) + expect(url).toEqual("ws://example.com:8080/events?a=b&c=d") + }) + + test("should build url with no params", () => { + const url = buildConnectionUrl("http://example.com", "/events") + expect(url).toEqual("ws://example.com/events") + }) + + test("should build url with no path", () => { + const url = buildConnectionUrl("http://example.com") + expect(url).toEqual("ws://example.com/") + }) + + test("should filter out null and undefined params", () => { + const url = buildConnectionUrl("http://example.com", "/events", { + a: "b", + c: null, + d: undefined, + }) + expect(url).toEqual("ws://example.com/events?a=b") + }) + + test("should build url with array params", () => { + const url = buildConnectionUrl("http://example.com", "/events", { + a: ["b", "c"], + b: [1, 2], + }) + expect(url).toEqual("ws://example.com/events?a=b%2Cc&b=1%2C2") + }) + }) + + describe("connectWs", () => { + let mockWs: any + let mockWebSocket: any + let connection: ReturnType + beforeEach(() => { + mockWs = { + onmessage: () => {}, + onopen: () => {}, + onclose: () => {}, + onerror: () => {}, + close: jest.fn(() => { + mockWs.onclose() + }), + } + mockWebSocket = jest.fn().mockReturnValue(mockWs) + jest.spyOn(WebSocketModule, "WebSocket").mockImplementation(mockWebSocket) + + connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + }) + }) + + test("should connect to the websocket", () => { + expect(mockWebSocket).toHaveBeenCalledWith("ws://example.com/events?a=b") + }) + + test("should emit data events", () => { + const mockListener = jest.fn() + connection.on("data", mockListener) + mockWs.onmessage({data: '{"foo": "bar"}'}) + expect(mockListener).toHaveBeenCalledWith({foo: "bar"}) + }) + + test("should emit open events", () => { + const mockListener = jest.fn() + connection.on("open", mockListener) + mockWs.onopen() + expect(mockListener).toHaveBeenCalled() + }) + + test("should emit close events", () => { + const mockListener = jest.fn() + connection.on("close", mockListener) + mockWs.onclose() + expect(mockListener).toHaveBeenCalled() + }) + + test("should emit error events", () => { + const mockListener = jest.fn() + connection.on("error", mockListener) + mockWs.onerror("error") + expect(mockListener).toHaveBeenCalledWith("error") + }) + + test("should remove listeners when closed", async () => { + const mockListener = jest.fn() + connection.on("data", mockListener) + connection.close() + + await new Promise(resolve => setTimeout(resolve, 0)) + + mockWs.onmessage({data: '{"foo": "bar"}'}) + + expect(mockListener).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/transport-http/src/connect-ws.ts b/packages/transport-http/src/connect-ws.ts index 95ef93760..99389aa25 100644 --- a/packages/transport-http/src/connect-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -1,6 +1,7 @@ import {EventEmitter} from "events" import {safeParseJSON} from "./utils" import {StreamConnection} from "@onflow/typedefs" +import {WebSocket} from "./websocket" type WebSocketConnection = StreamConnection<{ data: T @@ -24,6 +25,12 @@ export function connectWs({ const data = safeParseJSON(e.data) if (data) { emitter.emit("data", data) + } else { + emitter.emit( + "error", + new Error("connectWs: invalid JSON data: " + e.data) + ) + this.close() } } @@ -33,6 +40,7 @@ export function connectWs({ ws.onclose = function () { emitter.emit("close") + emitter.removeAllListeners() } ws.onerror = function (e) { @@ -54,15 +62,15 @@ export function connectWs({ } } -function buildConnectionUrl( +export function buildConnectionUrl( hostname: string, - path: string, + path?: string, params?: Record< string, string | number | string[] | number[] | null | undefined > ) { - const url = new URL(path, hostname) + const url = new URL(path || "", hostname) if (url.protocol === "https:") { url.protocol = "wss:" } else if (url.protocol === "http:") { diff --git a/packages/transport-http/src/websocket.ts b/packages/transport-http/src/websocket.ts new file mode 100644 index 000000000..370efcede --- /dev/null +++ b/packages/transport-http/src/websocket.ts @@ -0,0 +1,6 @@ +import _WebSocket from "isomorphic-ws" + +export const WebSocket = _WebSocket as new ( + url: string | URL, + protocols?: string | string[] | undefined +) => WebSocket diff --git a/packages/transport-http/tsconfig.json b/packages/transport-http/tsconfig.json index ac6917898..3261f4607 100644 --- a/packages/transport-http/tsconfig.json +++ b/packages/transport-http/tsconfig.json @@ -6,6 +6,7 @@ // Types should go into this directory. // Removing this would place the .d.ts files // next to the .js files - "outDir": "types" + "outDir": "types", + "allowSyntheticDefaultImports": true } } From c7758c75a27063b8ee3b21133a4b868483c57375 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 18:51:34 -0800 Subject: [PATCH 10/54] More tests --- .../src/connect-subscribe-events.test.js | 90 ------- .../src/connect-subscribe-events.test.ts | 244 ++++++++++++++++++ .../src/connect-subscribe-events.ts | 7 +- 3 files changed, 248 insertions(+), 93 deletions(-) delete mode 100644 packages/transport-http/src/connect-subscribe-events.test.js create mode 100644 packages/transport-http/src/connect-subscribe-events.test.ts diff --git a/packages/transport-http/src/connect-subscribe-events.test.js b/packages/transport-http/src/connect-subscribe-events.test.js deleted file mode 100644 index 9f2502d1a..000000000 --- a/packages/transport-http/src/connect-subscribe-events.test.js +++ /dev/null @@ -1,90 +0,0 @@ -import {sendSubscribeEvents} from "./connect-subscribe-events" -import {Buffer} from "@onflow/rlp" -import {response as responseADT} from "@onflow/sdk" -import {DataStream} from "../../util-stream/dist" - -describe("Subscribe Events", () => { - let mockStream - let subscribeWsMock - let unsubscribeMock - - beforeEach(async () => { - mockStream = new DataStream() - subscribeWsMock = jest.fn(() => mockStream) - unsubscribeMock = jest.fn() - - subscribeWsMock.mockReturnValue({ - unsubscribeCallback: unsubscribeMock, - }) - }) - - test("should initiate socket conection with correct params", async () => { - await sendSubscribeEvents( - { - tag: "SUBSCRIBE_EVENTS", - subscribeEvents: { - startBlockId: "abc123", - startHeight: 1, - eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeatInterval: 1000, - }, - }, - { - response: responseADT, - Buffer, - }, - { - subscribeWs: subscribeWsMock, - node: "localhost", - } - ) - - expect(subscribeWsMock).toHaveBeenCalledWith({ - hostname: "localhost", - path: "/v1/subscribe_events", - params: { - start_block_id: "abc123", - start_height: 1, - event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeat_interval: 1000, - }, - }) - }) - - test("should return a stream that pushes events when received from socket", async () => { - const response = await sendSubscribeEvents( - { - tag: "SUBSCRIBE_EVENTS", - subscribeEvents: { - startBlockId: "abc123", - startHeight: 1, - eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeatInterval: 1000, - }, - }, - { - response: responseADT, - Buffer, - }, - { - subscribeWs: subscribeWsMock, - node: "localhost", - } - ) - - response.subscribe(e => { - expect(e).toEqual({ - tag: "SUBSCRIBE_EVENTS", - dataStream: mockStream, - }) - }) - - mockStream.next("hello") - }) -}) diff --git a/packages/transport-http/src/connect-subscribe-events.test.ts b/packages/transport-http/src/connect-subscribe-events.test.ts new file mode 100644 index 000000000..ed239ddcb --- /dev/null +++ b/packages/transport-http/src/connect-subscribe-events.test.ts @@ -0,0 +1,244 @@ +import {connectSubscribeEvents} from "./connect-subscribe-events" +import {Buffer} from "@onflow/rlp" +import {response as responseADT} from "@onflow/sdk" +import {StreamConnection} from "@onflow/typedefs" +import EventEmitter from "events" + +describe("Subscribe Events", () => { + let emitter + let mockStream: StreamConnection<{ + data: { + events: any[] + heartbeat: { + blockId: string + blockHeight: number + blockTimestamp: number + } + } + }> + let connectWsMock + let unsubscribeMock + + beforeEach(async () => { + connectWsMock = jest.fn(() => mockStream) + unsubscribeMock = jest.fn() + emitter = new EventEmitter() + mockStream = { + on: jest.fn((event, callback) => { + emitter.on(event, callback) + }) as any, + off: jest.fn((event, callback) => { + emitter.off(event, callback) + }) as any, + close: jest.fn(), + } + + connectWsMock.mockReturnValue(mockStream) + }) + + test("should initiate socket conection with correct params", async () => { + await connectSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + }, + }, + { + response: responseADT, + Buffer, + }, + { + connectWs: connectWsMock, + node: "http://localhost", + } + ) + + expect(connectWsMock).toHaveBeenCalledWith({ + hostname: "http://localhost", + path: "/v1/subscribe_events", + params: { + start_block_id: "abc123", + start_height: 1, + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1000, + }, + }) + }) + + test("should process events & heartbeat then map to response adt", async () => { + const response = await connectSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1, + }, + }, + { + response: responseADT, + Buffer, + }, + { + connectWs: connectWsMock, + node: "http://localhost", + } + ) + + const mockPayload = Buffer.from( + JSON.stringify({ + value: { + id: "A.912d5440f7e3769e.FlowFees.FeesDeducted", + fields: [ + { + value: {value: "0.00000119", type: "UFix64"}, + name: "amount", + }, + { + value: {value: "1.00000000", type: "UFix64"}, + name: "inclusionEffort", + }, + { + value: {value: "0.00000004", type: "UFix64"}, + name: "executionEffort", + }, + ], + }, + type: "Event", + }) + ).toString("base64") + + let allData: any[] = [] + response.dataStream.on("data", data => { + allData.push(data) + }) + + emitter.emit("data", { + BlockID: "abc123", + Height: 1, + Timestamp: "1", + Events: [ + { + Type: "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", + TransactionID: "123abc", + TransactionIndex: 1, + EventIndex: 1, + Payload: mockPayload, + }, + ], + }) + + emitter.emit("data", { + BlockID: "def456", + Height: 2, + Timestamp: "2", + Events: [], + }) + + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(allData).toEqual([ + { + ...responseADT(), + tag: "SUBSCRIBE_EVENTS", + events: [ + { + type: "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", + transactionId: "123abc", + transactionIndex: 1, + eventIndex: 1, + blockId: "abc123", + blockHeight: 1, + blockTimestamp: "1", + payload: JSON.parse(Buffer.from(mockPayload, "base64").toString()), + }, + ], + heartbeat: { + blockId: "abc123", + blockHeight: 1, + blockTimestamp: "1", + }, + }, + { + ...responseADT(), + tag: "SUBSCRIBE_EVENTS", + heartbeat: { + blockId: "def456", + blockHeight: 2, + blockTimestamp: "2", + }, + }, + ]) + }) + + test("close should propogate the websocket connection", async () => { + const response = await connectSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1, + }, + }, + { + response: responseADT, + Buffer, + }, + { + connectWs: connectWsMock, + node: "http://localhost", + } + ) + + response.dataStream.close() + + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(mockStream.close).toHaveBeenCalled() + }) + + test("should unsubscribe when the stream is closed", async () => { + const response = await connectSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1, + }, + }, + { + response: responseADT, + Buffer, + }, + { + connectWs: connectWsMock, + node: "http://localhost", + } + ) + + response.dataStream.close() + + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(mockStream.close).toHaveBeenCalled() + }) +}) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index 2d7d9a1de..029fb7bd4 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -24,6 +24,9 @@ function constructData(ix: any, context: any, data: any) { "eyJ2YWx1ZSI6eyJpZCI6IkEuOTEyZDU0NDBmN2UzNzY5ZS5GbG93RmVlcy5GZWVzRGVkdWN0ZWQiLCJmaWVsZHMiOlt7InZhbHVlIjp7InZhbHVlIjoiMC4wMDAwMDExOSIsInR5cGUiOiJVRml4NjQifSwibmFtZSI6ImFtb3VudCJ9LHsidmFsdWUiOnsidmFsdWUiOiIxLjAwMDAwMDAwIiwidHlwZSI6IlVGaXg2NCJ9LCJuYW1lIjoiaW5jbHVzaW9uRWZmb3J0In0seyJ2YWx1ZSI6eyJ2YWx1ZSI6IjAuMDAwMDAwMDQiLCJ0eXBlIjoiVUZpeDY0In0sIm5hbWUiOiJleGVjdXRpb25FZmZvcnQifV19LCJ0eXBlIjoiRXZlbnQifQo=", })) } + // PLEASE REMOVE ME + // PLEASE IF YOU ARE REVIEWING THIS AND I FORGOT ABOUT IT + // PLEASE REMOVE ME :) let ret = context.response() ret.tag = ix.tag @@ -112,9 +115,7 @@ export async function connectSubscribeEvents( lastBlockId = responseData.heartbeat.blockId outputEmitter.emit("data", responseData) }) - connection.on("error", (error: Error) => { - outputEmitter.emit("error", error) - }) + connection.on("error", (error: Error) => {}) connection.on("close", () => { connect() }) From 4a7a147f1a0a9f9323c1195f4c450a50e98f20d0 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 18:59:09 -0800 Subject: [PATCH 11/54] add events dependency --- package-lock.json | 9 ++++----- packages/fcl/package.json | 3 ++- packages/sdk/package.json | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2887ece9..ee19dfbd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4661,10 +4661,6 @@ "resolved": "packages/util-semver", "link": true }, - "node_modules/@onflow/util-stream": { - "resolved": "packages/util-stream", - "link": true - }, "node_modules/@onflow/util-template": { "resolved": "packages/util-template", "link": true @@ -19914,7 +19910,8 @@ "@onflow/util-semver": "^1.0.0", "@onflow/util-template": "^1.2.0", "@onflow/util-uid": "^1.2.0", - "cross-fetch": "^3.1.6" + "cross-fetch": "^3.1.6", + "events": "^3.3.0" }, "devDependencies": { "@onflow/fcl-bundle": "^1.4.0", @@ -20661,6 +20658,7 @@ "@onflow/util-logger": "^1.3.0", "@onflow/util-template": "^1.2.0", "deepmerge": "^4.2.2", + "events": "^3.3.0", "sha3": "^2.1.4", "uuid": "^9.0.1" }, @@ -21566,6 +21564,7 @@ }, "packages/util-stream": { "version": "1.0.0", + "extraneous": true, "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.23.2" diff --git a/packages/fcl/package.json b/packages/fcl/package.json index d389e9d83..227d0b490 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -71,7 +71,8 @@ "@onflow/util-semver": "^1.0.0", "@onflow/util-template": "^1.2.0", "@onflow/util-uid": "^1.2.0", - "cross-fetch": "^3.1.6" + "cross-fetch": "^3.1.6", + "events": "^3.3.0" }, "peerDependencies": { "@react-native-async-storage/async-storage": "^1.13.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 2e3995974..43dd19867 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -50,6 +50,7 @@ "@onflow/util-logger": "^1.3.0", "@onflow/util-template": "^1.2.0", "deepmerge": "^4.2.2", + "events": "^3.3.0", "sha3": "^2.1.4", "uuid": "^9.0.1" } From bf33e9708a8a271cb4000df3d551557574e5c600 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Thu, 23 Nov 2023 22:18:01 -0800 Subject: [PATCH 12/54] Fixup tests --- packages/fcl/jordan.test.js | 18 ------------------ packages/sdk/src/contract.test.js | 2 +- packages/sdk/src/decode/decode-stream.test.ts | 3 +-- packages/sdk/src/decode/decode.test.js | 12 +----------- packages/sdk/src/decode/sdk-decode.js | 2 +- packages/sdk/src/resolve/resolve.js | 9 +++++++-- .../__snapshots__/response.test.js.snap | 2 ++ packages/util-actor/jest.config.ts | 8 -------- 8 files changed, 13 insertions(+), 43 deletions(-) delete mode 100644 packages/fcl/jordan.test.js delete mode 100644 packages/util-actor/jest.config.ts diff --git a/packages/fcl/jordan.test.js b/packages/fcl/jordan.test.js deleted file mode 100644 index 7002ef16c..000000000 --- a/packages/fcl/jordan.test.js +++ /dev/null @@ -1,18 +0,0 @@ -// TODO: REMOVE ME - -import * as fcl from "./src/fcl" - -fcl.config({ - "accessNode.api": "https://rest-testnet.onflow.org", -}) - -fcl.events().subscribe(e => console.log(e)) - -jest.setTimeout(100000) - -describe("fcl", () => { - test("dummy delay", async () => { - await new Promise(resolve => setTimeout(resolve, 90000)) - expect(false).toBe(true) - }) -}) diff --git a/packages/sdk/src/contract.test.js b/packages/sdk/src/contract.test.js index 866d0c02f..c2f44c410 100644 --- a/packages/sdk/src/contract.test.js +++ b/packages/sdk/src/contract.test.js @@ -34,7 +34,7 @@ interfaceContract("export", root)` describe("consume", () => { interfaceContract("@onflow/decode", decode)` decode - decodeResponse + makeDecodeResponse ` interfaceContract("@onflow/encode", encode)` diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts index 6fab065f1..6ac996add 100644 --- a/packages/sdk/src/decode/decode-stream.test.ts +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -10,8 +10,7 @@ describe("decode stream", () => { beforeEach(() => { mockDecodeResponse = jest.fn() - decodeStream = async (...args) => - makeDecodeStream(mockDecodeResponse)(...args) + decodeStream = makeDecodeStream(mockDecodeResponse) emitter = new EventEmitter() mockStream = { on: jest.fn((event, callback) => { diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index 9fbb8935d..61a27efba 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1,16 +1,6 @@ -import * as root from "./decode.js" import {decode, makeDecodeResponse} from "./decode.js" import {Buffer} from "@onflow/rlp" -it("exported interface contract", () => { - expect(root).toStrictEqual( - expect.objectContaining({ - decode: expect.any(Function), - decodeResponse: expect.any(Function), - }) - ) -}) - it("decodeResponse", async () => { const response = { encodedData: JSON.parse( @@ -1397,7 +1387,7 @@ describe("decode data stream tests", () => { const streamResponse = { dataStream: mockStream, } - const decodeStream = jest.fn() + const decodeStream = jest.fn().mockReturnValue(mockStream) const decodeResponse = makeDecodeResponse(decodeStream) const decoded = await decodeResponse(streamResponse) diff --git a/packages/sdk/src/decode/sdk-decode.js b/packages/sdk/src/decode/sdk-decode.js index 7d0c7bf9b..7141e776b 100644 --- a/packages/sdk/src/decode/sdk-decode.js +++ b/packages/sdk/src/decode/sdk-decode.js @@ -1,6 +1,6 @@ import {config} from "@onflow/config" import {makeDecodeResponse} from "./decode" -import {makeDecodeStream} from "./decode-stream.js" +import {makeDecodeStream} from "./decode-stream" export async function decode(response) { const decodersFromConfig = await config().where(/^decoder\./) diff --git a/packages/sdk/src/resolve/resolve.js b/packages/sdk/src/resolve/resolve.js index c17030dd8..508aae401 100644 --- a/packages/sdk/src/resolve/resolve.js +++ b/packages/sdk/src/resolve/resolve.js @@ -8,7 +8,7 @@ import {response} from "../response/response.js" import {build} from "../build/build.js" import {getBlock} from "../build/build-get-block.js" import {getAccount} from "../build/build-get-account.js" -import {decodeResponse as decode} from "../decode/decode.js" +import {makeDecodeResponse} from "../decode/decode.js" import {resolveCadence} from "./resolve-cadence.js" import {resolveArguments} from "./resolve-arguments.js" @@ -19,6 +19,11 @@ import {resolveFinalNormalization} from "./resolve-final-normalization.js" import {resolveVoucherIntercept} from "./resolve-voucher-intercept.js" import {resolveComputeLimit} from "./resolve-compute-limit.js" +// Decoder without stream support +const decode = makeDecodeResponse(() => { + throw new Error("Not Implemented") +}) + const noop = v => v const debug = (key, fn = noop) => @@ -119,4 +124,4 @@ async function execFetchSequenceNumber(ix) { } } return ix -} \ No newline at end of file +} diff --git a/packages/sdk/src/response/__snapshots__/response.test.js.snap b/packages/sdk/src/response/__snapshots__/response.test.js.snap index f92e96281..941ff3078 100644 --- a/packages/sdk/src/response/__snapshots__/response.test.js.snap +++ b/packages/sdk/src/response/__snapshots__/response.test.js.snap @@ -6,8 +6,10 @@ exports[`Response - Snapshot 1`] = ` "block": null, "blockHeader": null, "collection": null, + "dataStream": null, "encodedData": null, "events": null, + "heartbeat": null, "latestBlock": null, "networkParameters": null, "tag": null, diff --git a/packages/util-actor/jest.config.ts b/packages/util-actor/jest.config.ts deleted file mode 100644 index 22291c790..000000000 --- a/packages/util-actor/jest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {Config} from 'jest'; - -const config: Config = { - verbose: true, - preset: "ts-jest", -}; - -export default config; \ No newline at end of file From 26c43a3d182f88e500b44cecc27cc6b448d26d72 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 13:59:51 -0800 Subject: [PATCH 13/54] Add retriable websocket & remove open event --- ...test.js => build-subscribe-events.test.ts} | 4 +- ...be-events.js => build-subscribe-events.ts} | 16 +- packages/sdk/src/decode/decode-stream.ts | 1 - .../src/connect-subscribe-events.test.ts | 19 +- .../src/connect-subscribe-events.ts | 77 ++++---- .../transport-http/src/connect-ws.test.ts | 178 +++++++++++++++--- packages/transport-http/src/connect-ws.ts | 142 +++++++++++--- packages/transport-http/src/sdk-send-http.ts | 1 + packages/typedefs/src/index.ts | 2 - 9 files changed, 325 insertions(+), 115 deletions(-) rename packages/sdk/src/build/{build-subscribe-events.test.js => build-subscribe-events.test.ts} (87%) rename packages/sdk/src/build/{build-subscribe-events.js => build-subscribe-events.ts} (59%) diff --git a/packages/sdk/src/build/build-subscribe-events.test.js b/packages/sdk/src/build/build-subscribe-events.test.ts similarity index 87% rename from packages/sdk/src/build/build-subscribe-events.test.js rename to packages/sdk/src/build/build-subscribe-events.test.ts index 3d4dec385..0c6f18b62 100644 --- a/packages/sdk/src/build/build-subscribe-events.test.js +++ b/packages/sdk/src/build/build-subscribe-events.test.ts @@ -1,5 +1,5 @@ -import {interaction} from "../interaction/interaction.js" -import {subscribeEvents} from "./build-subscribe-events.js" +import {interaction} from "../interaction/interaction" +import {subscribeEvents} from "./build-subscribe-events" describe("Subscribe Events", () => { test("Subscribe Events", async () => { diff --git a/packages/sdk/src/build/build-subscribe-events.js b/packages/sdk/src/build/build-subscribe-events.ts similarity index 59% rename from packages/sdk/src/build/build-subscribe-events.js rename to packages/sdk/src/build/build-subscribe-events.ts index 1f21693c3..75ef35a56 100644 --- a/packages/sdk/src/build/build-subscribe-events.js +++ b/packages/sdk/src/build/build-subscribe-events.ts @@ -1,5 +1,10 @@ +import {invariant} from "@onflow/util-invariant" import {pipe, Ok, makeSubscribeEvents} from "../interaction/interaction.js" +import {EventFilter} from "@onflow/typedefs" +/** + * Subscribe to events with the given filter & parameters + */ export function subscribeEvents({ startBlockId, startHeight, @@ -7,10 +12,19 @@ export function subscribeEvents({ addresses, contracts, heartbeatInterval, +}: EventFilter & { + startBlockId?: string + startHeight?: number + heartbeatInterval?: number }) { + invariant( + !(startBlockId && startHeight), + `SDK Subscribe Events Error: Cannot set both startBlockId and startHeight.` + ) + return pipe([ makeSubscribeEvents, - ix => { + (ix: any) => { ix.subscribeEvents.startBlockId = startBlockId ix.subscribeEvents.startHeight = startHeight ix.subscribeEvents.eventTypes = eventTypes diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index 6409fd3a1..31afd4dce 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -53,7 +53,6 @@ export const makeDecodeStream = newStream.emit(event, message) }) } - relayEvent("open") relayEvent("close") relayEvent("error") diff --git a/packages/transport-http/src/connect-subscribe-events.test.ts b/packages/transport-http/src/connect-subscribe-events.test.ts index ed239ddcb..95f961344 100644 --- a/packages/transport-http/src/connect-subscribe-events.test.ts +++ b/packages/transport-http/src/connect-subscribe-events.test.ts @@ -62,14 +62,17 @@ describe("Subscribe Events", () => { expect(connectWsMock).toHaveBeenCalledWith({ hostname: "http://localhost", path: "/v1/subscribe_events", - params: { - start_block_id: "abc123", - start_height: 1, - event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], - addresses: ["0x1", "0x2"], - contracts: ["A.7e60df042a9c0868.FlowToken"], - heartbeat_interval: 1000, - }, + getParams: expect.any(Function), + }) + + const params = connectWsMock.mock.calls[0][0].getParams() + expect(params).toEqual({ + start_block_id: "abc123", + start_height: 1, + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1000, }) }) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index 029fb7bd4..19bc442b2 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -83,47 +83,44 @@ export async function connectSubscribeEvents( const connectWs: typeof defaultConnectWs = opts.connectWs || defaultConnectWs const outputEmitter = new EventEmitter() - let close = () => {} let lastBlockId: string | null = null - ;(function connect() { - const params: Record = { - event_types: ix.subscribeEvents.eventTypes, - addresses: ix.subscribeEvents.addresses, - contracts: ix.subscribeEvents.contracts, - heartbeat_interval: ix.subscribeEvents.heartbeatInterval, - } - - // If the lastBlockId is set, use it to resume the stream - if (lastBlockId) { - params.start_block_id = lastBlockId - } else { - params.start_block_id = ix.subscribeEvents.startBlockId - params.start_height = ix.subscribeEvents.startHeight - } - - // Connect to the websocket - const connection = connectWs({ - hostname: opts.node, - path: `/v1/subscribe_events`, - params, - }) - - // Map the connection to a formatted response stream - connection.on("data", (data: any) => { - const responseData = constructData(ix, context, data) - lastBlockId = responseData.heartbeat.blockId - outputEmitter.emit("data", responseData) - }) - connection.on("error", (error: Error) => {}) - connection.on("close", () => { - connect() - }) - connection.on("open", () => { - outputEmitter.emit("open") - }) - close = () => connection.close() - })() + // Connect to the websocket & provide reconnection parameters + const connection = connectWs({ + hostname: opts.node, + path: `/v1/subscribe_events`, + getParams: () => { + const params: Record = { + event_types: ix.subscribeEvents.eventTypes, + addresses: ix.subscribeEvents.addresses, + contracts: ix.subscribeEvents.contracts, + heartbeat_interval: ix.subscribeEvents.heartbeatInterval, + } + + // If the lastBlockId is set, use it to resume the stream + if (lastBlockId) { + params.start_block_id = lastBlockId + 1 + } else { + params.start_block_id = ix.subscribeEvents.startBlockId + params.start_height = ix.subscribeEvents.startHeight + } + + return params + }, + }) + + // Map the connection to a formatted response stream + connection.on("data", (data: any) => { + const responseData = constructData(ix, context, data) + lastBlockId = responseData.heartbeat.blockId + outputEmitter.emit("data", responseData) + }) + connection.on("error", (error: Error) => { + outputEmitter.emit("error", error) + }) + connection.on("close", () => { + outputEmitter.emit("close") + }) const responseStream: RawSubscribeEventsStream = { on(event: "data" | "error" | "close" | "open", listener: any) { @@ -135,7 +132,7 @@ export async function connectSubscribeEvents( return this }, close() { - close() + connection.close() }, } return constructResponse(ix, context, responseStream) diff --git a/packages/transport-http/src/connect-ws.test.ts b/packages/transport-http/src/connect-ws.test.ts index 1e55aa5b2..a7218e9a1 100644 --- a/packages/transport-http/src/connect-ws.test.ts +++ b/packages/transport-http/src/connect-ws.test.ts @@ -56,71 +56,189 @@ describe("connectWs", () => { }) describe("connectWs", () => { - let mockWs: any - let mockWebSocket: any - let connection: ReturnType + let mockWs: {[key: number]: any} // get websocket by connection attempt index + let mockWebSocket: any // mock implementation of WebSocket (new WebSocket()) + beforeEach(() => { - mockWs = { + const mockWsStub = { onmessage: () => {}, onopen: () => {}, onclose: () => {}, onerror: () => {}, - close: jest.fn(() => { - mockWs.onclose() - }), + close() { + return jest + .fn(() => { + this.onclose() + // Prevent handlers from being called after close + // This is so that we emulate the behavior of the real WebSocket + // And don't accidentally simulate events on a closed connection + this.onclose = () => {} + this.onmessage = () => {} + this.onopen = () => {} + this.onerror = () => {} + }) + .bind(this)() + }, } - mockWebSocket = jest.fn().mockReturnValue(mockWs) + + let wsIdx = 0 + mockWs = new Proxy({} as any, { + get(target, name) { + let idx = Number(name) + if (!isNaN(idx)) { + target[idx] = target[idx] || {...mockWsStub} + return target[idx] + } + return null + }, + }) + mockWebSocket = jest.fn().mockImplementation(() => { + return mockWs[wsIdx++] + }) jest.spyOn(WebSocketModule, "WebSocket").mockImplementation(mockWebSocket) + }) - connection = connectWs({ + test("should connect to the websocket", () => { + const connection = connectWs({ hostname: "http://example.com", path: "/events", params: {a: "b"}, }) - }) - - test("should connect to the websocket", () => { expect(mockWebSocket).toHaveBeenCalledWith("ws://example.com/events?a=b") }) test("should emit data events", () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + }) const mockListener = jest.fn() connection.on("data", mockListener) - mockWs.onmessage({data: '{"foo": "bar"}'}) + mockWs[0].onmessage({data: '{"foo": "bar"}'}) expect(mockListener).toHaveBeenCalledWith({foo: "bar"}) }) - test("should emit open events", () => { - const mockListener = jest.fn() - connection.on("open", mockListener) - mockWs.onopen() - expect(mockListener).toHaveBeenCalled() + test("should retry on close", async () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + retryLimit: 1, + retryIntervalMs: 0, + }) + const mockCloseListener = jest.fn() + connection.on("close", mockCloseListener) + mockWs[0].onclose({ + code: 1006, + reason: "connection failed", + }) + await new Promise(resolve => setTimeout(resolve, 0)) + expect(mockCloseListener).not.toHaveBeenCalled() + + mockWs[1].onopen() + await new Promise(resolve => setTimeout(resolve, 0)) + expect(mockCloseListener).not.toHaveBeenCalled() + expect(mockWebSocket.mock.calls.length).toEqual(2) }) - test("should emit close events", () => { - const mockListener = jest.fn() - connection.on("close", mockListener) - mockWs.onclose() - expect(mockListener).toHaveBeenCalled() + test("should emit close & error events on final retry", async () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + retryLimit: 1, + retryIntervalMs: 0, + }) + const mockCloseListener = jest.fn() + const mockErrorListener = jest.fn() + connection.on("close", mockCloseListener) + connection.on("error", mockErrorListener) // error must be handled so process doesn't exit + mockWs[0].onclose({ + code: 1006, + reason: "connection failed", + }) + + // Wait for retry + await new Promise(resolve => setTimeout(resolve, 10)) + expect(mockCloseListener).not.toHaveBeenCalled() + expect(mockErrorListener).not.toHaveBeenCalled() + + mockWs[1].onclose({ + code: 1006, + reason: "connection failed", + }) + await new Promise(resolve => setTimeout(resolve, 0)) + expect(mockCloseListener).toHaveBeenCalled() + expect(mockErrorListener).toHaveBeenCalled() }) - test("should emit error events", () => { - const mockListener = jest.fn() - connection.on("error", mockListener) - mockWs.onerror("error") - expect(mockListener).toHaveBeenCalledWith("error") + test("retry count should reset on successful connection", async () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + retryLimit: 1, + retryIntervalMs: 0, + }) + const mockCloseListener = jest.fn() + const mockErrorListener = jest.fn() + connection.on("close", mockCloseListener) + connection.on("error", mockErrorListener) + + mockWs[0].onclose({ + code: 1006, + reason: "connection failed", + }) + await new Promise(resolve => setTimeout(resolve, 10)) + expect(mockCloseListener).not.toHaveBeenCalled() + expect(mockErrorListener).not.toHaveBeenCalled() + + mockWs[1].onopen() + await new Promise(resolve => setTimeout(resolve, 0)) + + mockWs[1].onclose({ + code: 1006, + reason: "connection failed", + }) + await new Promise(resolve => setTimeout(resolve, 0)) + + expect(mockCloseListener).not.toHaveBeenCalled() + expect(mockErrorListener).not.toHaveBeenCalled() }) test("should remove listeners when closed", async () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + }) const mockListener = jest.fn() connection.on("data", mockListener) connection.close() - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise(resolve => setTimeout(resolve, 10)) - mockWs.onmessage({data: '{"foo": "bar"}'}) + mockWs[0].onmessage({data: '{"foo": "bar"}'}) expect(mockListener).not.toHaveBeenCalled() }) + + test("should not retry when closed by user", async () => { + const connection = connectWs({ + hostname: "http://example.com", + path: "/events", + params: {a: "b"}, + retryLimit: 1, + retryIntervalMs: 0, + }) + const mockCloseListener = jest.fn() + connection.on("close", mockCloseListener) + connection.close() + + await new Promise(resolve => setTimeout(resolve, 10)) + expect(mockCloseListener).toHaveBeenCalled() + expect(mockWebSocket.mock.calls.length).toEqual(1) + }) }) }) diff --git a/packages/transport-http/src/connect-ws.ts b/packages/transport-http/src/connect-ws.ts index 99389aa25..b9f373791 100644 --- a/packages/transport-http/src/connect-ws.ts +++ b/packages/transport-http/src/connect-ws.ts @@ -3,6 +3,36 @@ import {safeParseJSON} from "./utils" import {StreamConnection} from "@onflow/typedefs" import {WebSocket} from "./websocket" +export class WebsocketError extends Error { + code?: number + reason?: string + wasClean?: boolean + + constructor({ + code, + reason, + message, + wasClean, + }: { + code?: number + reason?: string + message?: string + wasClean?: boolean + }) { + const msg = ` + connectWs: connection closed with error${message ? `: ${message}` : ""} + ${code ? `code: ${code}` : ""} + ${reason ? `reason: ${reason}` : ""} + ${wasClean ? `wasClean: ${wasClean}` : ""} + ` + super(msg) + this.name = "WebsocketError" + this.code = code + this.reason = reason + this.wasClean = false + } +} + type WebSocketConnection = StreamConnection<{ data: T }> @@ -11,53 +41,103 @@ export function connectWs({ hostname, path, params, + getParams, + retryLimit = 5, + retryIntervalMs = 1000, }: { hostname: string path: string params?: Record + getParams?: () => Record | undefined + retryLimit?: number + retryIntervalMs?: number }): WebSocketConnection { - // Build a websocket connection with correct protocol & params - const url = buildConnectionUrl(hostname, path, params) - const ws = new WebSocket(url) - const emitter = new EventEmitter() - - ws.onmessage = function (e) { - const data = safeParseJSON(e.data) - if (data) { - emitter.emit("data", data) - } else { - emitter.emit( - "error", - new Error("connectWs: invalid JSON data: " + e.data) - ) - this.close() - } + if (getParams && params) { + throw new Error("connectWs: cannot specify both params and getParams") } + let outputEmitter = new EventEmitter() - ws.onopen = function () { - emitter.emit("open") - } + let retryCount = 0 + const resolveParams = getParams || (() => params) + let close = () => {} - ws.onclose = function () { - emitter.emit("close") - emitter.removeAllListeners() - } + ;(function connect() { + let userClosed = false + let hasOpened = false - ws.onerror = function (e) { - emitter.emit("error", e) - } + // Build a websocket connection with correct protocol & params + const url = buildConnectionUrl(hostname, path, resolveParams()) + const ws = new WebSocket(url) + + ws.onmessage = function (e) { + const data = safeParseJSON(e.data) + if (data) { + outputEmitter.emit("data", data) + } else { + outputEmitter.emit( + "error", + new WebsocketError({message: "invalid JSON data"}) + ) + this.close() + } + } + + ws.onclose = function (e) { + if (userClosed) { + outputEmitter.emit("close") + outputEmitter.removeAllListeners() + return + } + + if (!hasOpened) { + if (retryCount < retryLimit) { + retryCount++ + setTimeout(connect, retryIntervalMs) + } else { + outputEmitter.emit( + "error", + new WebsocketError({ + wasClean: e.wasClean, + code: e.code, + reason: e.reason, + message: "failed to connect", + }) + ) + + // Emit close event on next tick so that the error event is emitted first + setTimeout(() => { + outputEmitter.emit("close") + outputEmitter.removeAllListeners() + }) + } + } else { + // If the connection was established before closing, attempt to reconnect + setTimeout(connect, retryIntervalMs) + } + } + + ws.onopen = function () { + hasOpened = true + retryCount = 0 + } + + close = () => { + userClosed = true + ws.close() + } + })() return { - on(event: "data" | "close" | "error" | "open", listener: any) { - emitter.on(event, listener) + on(event: "data" | "close" | "error", listener: any) { + outputEmitter.on(event, listener) return this }, - off(event: "data" | "close" | "error" | "open", listener: any) { - emitter.off(event, listener) + off(event: "data" | "close" | "error", listener: any) { + outputEmitter.off(event, listener) return this }, close() { - ws.close() + close() }, } } diff --git a/packages/transport-http/src/sdk-send-http.ts b/packages/transport-http/src/sdk-send-http.ts index 4761e9c6b..1ed150b74 100644 --- a/packages/transport-http/src/sdk-send-http.ts +++ b/packages/transport-http/src/sdk-send-http.ts @@ -11,3 +11,4 @@ export {sendTransaction} from "./send-transaction" export {sendGetNetworkParameters} from "./send-get-network-parameters" export {connectSubscribeEvents} from "./connect-subscribe-events" export {send} from "./send-http" +export {WebsocketError} from "./connect-ws" diff --git a/packages/typedefs/src/index.ts b/packages/typedefs/src/index.ts index 575f0b42c..314230d3d 100644 --- a/packages/typedefs/src/index.ts +++ b/packages/typedefs/src/index.ts @@ -296,14 +296,12 @@ export interface StreamConnection { ): this on(event: "close", listener: () => void): this on(event: "error", listener: (err: any) => void): this - on(event: "open", listener: () => void): this off( event: C, listener: (data: ChannelMap[C]) => void ): this off(event: "close", listener: () => void): this off(event: "error", listener: (err: any) => void): this - off(event: "open", listener: () => void): this close(): void } From 056ddefce58427762a78711f525229bed816042c Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 14:00:41 -0800 Subject: [PATCH 14/54] fixup jsdoc --- packages/fcl/src/events/index.ts | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 67509f01e..3831d7e89 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -1,27 +1,14 @@ import {send, decode, subscribeEvents} from "@onflow/sdk" import {Event, EventFilter, EventStream} from "@onflow/typedefs" -/** - * @typedef {import("@onflow/typedefs").Event} Event - */ - -/** - * @typedef {object} SubscribeObject - * @property {Function} subscribe - The subscribe function. - */ - -/** - * @callback SubscriptionCallback - * @returns {Event} - */ - /** * @description - Subscribe to events * @param filterOrType - The filter or type of events to subscribe to * * @example * import * as fcl from "@onflow/fcl" - * fcl.events(eventName).subscribe((event) => console.log(event)) + * const unsubscribe = fcl.events(eventName).subscribe((event) => console.log(event)) + * unsubscribe() */ export function events(filterOrType?: EventFilter | string) { let filter: EventFilter From 42fb7feb72b7a7d7527ed53e07b1d4bc2b50425b Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 14:03:24 -0800 Subject: [PATCH 15/54] Add HTTP Request error --- packages/transport-http/src/sdk-send-http.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/transport-http/src/sdk-send-http.ts b/packages/transport-http/src/sdk-send-http.ts index 1ed150b74..9b4424fe4 100644 --- a/packages/transport-http/src/sdk-send-http.ts +++ b/packages/transport-http/src/sdk-send-http.ts @@ -12,3 +12,4 @@ export {sendGetNetworkParameters} from "./send-get-network-parameters" export {connectSubscribeEvents} from "./connect-subscribe-events" export {send} from "./send-http" export {WebsocketError} from "./connect-ws" +export {HTTPRequestError} from "./http-request.js" From cae134c66ae0b2caa4a5980f2c5f9dd58e1109b2 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 14:06:28 -0800 Subject: [PATCH 16/54] Fixup test --- packages/sdk/src/account/account.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/account/account.js b/packages/sdk/src/account/account.js index f3a2a75ed..18cc556f2 100644 --- a/packages/sdk/src/account/account.js +++ b/packages/sdk/src/account/account.js @@ -2,9 +2,13 @@ import {atBlockHeight} from "../build/build-at-block-height.js" import {atBlockId} from "../build/build-at-block-id.js" import {getAccount} from "../build/build-get-account.js" import {invariant} from "@onflow/util-invariant" -import {decodeResponse as decode} from "../decode/decode.js" +import {makeDecodeResponse} from "../decode/decode.js" import {send} from "../send/send.js" +const decode = makeDecodeResponse(() => { + throw new Error("Not Implemented") +}) + /** * @typedef {import("@onflow/typedefs").Account} Account */ From 5d50ef341094799e3b584f89a807aac826057c40 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 17:27:43 -0800 Subject: [PATCH 17/54] Revert decode changes, add tests, & fix reconnect --- packages/sdk/src/account/account.js | 6 +- .../src/build/build-subscribe-events.test.ts | 57 ++++++++- packages/sdk/src/contract.test.js | 2 +- packages/sdk/src/decode/decode-stream.test.ts | 8 +- packages/sdk/src/decode/decode-stream.ts | 112 +++++++++-------- packages/sdk/src/decode/decode.js | 113 +++++++++--------- packages/sdk/src/decode/decode.test.js | 16 +-- packages/sdk/src/decode/sdk-decode.js | 11 +- packages/sdk/src/resolve/resolve.js | 7 +- .../src/connect-subscribe-events.test.ts | 42 ++++++- .../src/connect-subscribe-events.ts | 8 +- 11 files changed, 226 insertions(+), 156 deletions(-) diff --git a/packages/sdk/src/account/account.js b/packages/sdk/src/account/account.js index 18cc556f2..f3a2a75ed 100644 --- a/packages/sdk/src/account/account.js +++ b/packages/sdk/src/account/account.js @@ -2,13 +2,9 @@ import {atBlockHeight} from "../build/build-at-block-height.js" import {atBlockId} from "../build/build-at-block-id.js" import {getAccount} from "../build/build-get-account.js" import {invariant} from "@onflow/util-invariant" -import {makeDecodeResponse} from "../decode/decode.js" +import {decodeResponse as decode} from "../decode/decode.js" import {send} from "../send/send.js" -const decode = makeDecodeResponse(() => { - throw new Error("Not Implemented") -}) - /** * @typedef {import("@onflow/typedefs").Account} Account */ diff --git a/packages/sdk/src/build/build-subscribe-events.test.ts b/packages/sdk/src/build/build-subscribe-events.test.ts index 0c6f18b62..6c1c20852 100644 --- a/packages/sdk/src/build/build-subscribe-events.test.ts +++ b/packages/sdk/src/build/build-subscribe-events.test.ts @@ -2,9 +2,26 @@ import {interaction} from "../interaction/interaction" import {subscribeEvents} from "./build-subscribe-events" describe("Subscribe Events", () => { - test("Subscribe Events", async () => { + test("subscribe events no block", async () => { + let ix = await subscribeEvents({ + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + })(interaction()) + + expect(ix.subscribeEvents.eventTypes).toEqual([ + "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", + ]) + expect(ix.subscribeEvents.addresses).toEqual(["0x1", "0x2"]) + expect(ix.subscribeEvents.contracts).toEqual([ + "A.7e60df042a9c0868.FlowToken", + ]) + expect(ix.subscribeEvents.heartbeatInterval).toBe(1000) + }) + + test("subscribe events block height", async () => { let ix = await subscribeEvents({ - startBlockId: "abc123", startHeight: 1, eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], addresses: ["0x1", "0x2"], @@ -12,7 +29,6 @@ describe("Subscribe Events", () => { heartbeatInterval: 1000, })(interaction()) - expect(ix.subscribeEvents.startBlockId).toBe("abc123") expect(ix.subscribeEvents.startHeight).toBe(1) expect(ix.subscribeEvents.eventTypes).toEqual([ "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", @@ -23,4 +39,39 @@ describe("Subscribe Events", () => { ]) expect(ix.subscribeEvents.heartbeatInterval).toBe(1000) }) + + test("subscribe events block id", async () => { + let ix = await subscribeEvents({ + startBlockId: "abc123", + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + })(interaction()) + + expect(ix.subscribeEvents.startBlockId).toBe("abc123") + expect(ix.subscribeEvents.eventTypes).toEqual([ + "A.7e60df042a9c0868.FlowToken.TokensWithdrawn", + ]) + expect(ix.subscribeEvents.addresses).toEqual(["0x1", "0x2"]) + expect(ix.subscribeEvents.contracts).toEqual([ + "A.7e60df042a9c0868.FlowToken", + ]) + expect(ix.subscribeEvents.heartbeatInterval).toBe(1000) + }) + + test("throws if both startBlockId and startHeight are set", async () => { + await expect(async () => + subscribeEvents({ + startBlockId: "abc123", + startHeight: 1, + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1000, + }) + ).rejects.toThrow( + "INVARIANT SDK Subscribe Events Error: Cannot set both startBlockId and startHeight." + ) + }) }) diff --git a/packages/sdk/src/contract.test.js b/packages/sdk/src/contract.test.js index c2f44c410..866d0c02f 100644 --- a/packages/sdk/src/contract.test.js +++ b/packages/sdk/src/contract.test.js @@ -34,7 +34,7 @@ interfaceContract("export", root)` describe("consume", () => { interfaceContract("@onflow/decode", decode)` decode - makeDecodeResponse + decodeResponse ` interfaceContract("@onflow/encode", encode)` diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts index 6ac996add..99458b4ed 100644 --- a/packages/sdk/src/decode/decode-stream.test.ts +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -1,16 +1,18 @@ import {EventEmitter} from "stream" -import {makeDecodeStream} from "./decode-stream" +import {decodeStream} from "./decode-stream" import {StreamConnection} from "@onflow/typedefs" +import * as decodeResponseModule from "./decode" describe("decode stream", () => { let mockDecodeResponse: jest.Mock - let decodeStream: any let mockStream: StreamConnection<{data: any}> let emitter: EventEmitter beforeEach(() => { mockDecodeResponse = jest.fn() - decodeStream = makeDecodeStream(mockDecodeResponse) + jest + .spyOn(decodeResponseModule, "decodeResponse") + .mockImplementation(mockDecodeResponse) emitter = new EventEmitter() mockStream = { on: jest.fn((event, callback) => { diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index 31afd4dce..d9ddcaf0d 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -1,70 +1,66 @@ import EventEmitter from "events" import {StreamConnection} from "@onflow/typedefs" -import type {makeDecodeResponse} from "./decode" - -type DecodeResponseFn = ReturnType +import {decodeResponse} from "./decode" // This function pipes a generic stream of data into a granular stream of decoded data -export const makeDecodeStream = - (decodeResponse: DecodeResponseFn) => - (stream: StreamConnection<{data: any}>) => { - const newStream = new EventEmitter() - let queue: Promise[] = [] +export const decodeStream = (stream: StreamConnection<{data: any}>) => { + const newStream = new EventEmitter() + let queue: Promise[] = [] - // Data is separated by topic & the decoded data is emitted in order - // All topics for a given message will be emitted synchronously before moving on to the next message - // The streamReady promise ensures that the data is emitted in order and avoids race conditions when decoding - stream.on("data", async data => { - const topics = Object.keys(data).filter( - key => data[key] != null && key !== "tag" - ) + // Data is separated by topic & the decoded data is emitted in order + // All topics for a given message will be emitted synchronously before moving on to the next message + // The streamReady promise ensures that the data is emitted in order and avoids race conditions when decoding + stream.on("data", async data => { + const topics = Object.keys(data).filter( + key => data[key] != null && key !== "tag" + ) - let newDataPromise = Promise.all( - topics.map(async channel => { - const partialResponse = { - [channel]: data[channel], - } - const message = await decodeResponse(partialResponse) - return { - channel, - message, - } - }) - ) - queue.push(newDataPromise) + let newDataPromise = Promise.all( + topics.map(async channel => { + const partialResponse = { + [channel]: data[channel], + } + const message = await decodeResponse(partialResponse) + return { + channel, + message, + } + }) + ) + queue.push(newDataPromise) - // Wait for the previous data to be emitted before emitting the new data - await queue[0] - queue.shift() + // Wait for the previous data to be emitted before emitting the new data + await queue[0] + queue.shift() - // Emit the new data - const newData = await newDataPromise - newData.forEach(({channel, message}) => { - newStream.emit(channel, message) - }) + // Emit the new data + const newData = await newDataPromise + newData.forEach(({channel, message}) => { + newStream.emit(channel, message) }) + }) - // Relay events from the original stream - // These events are delivered in order as well so that the stream will - // not emit more data after it has announced a contradictory state - function relayEvent(event: any) { - stream.on(event, async (message: any) => { - await queue.at(-1) - newStream.emit(event, message) - }) - } - relayEvent("close") - relayEvent("error") + // Relay events from the original stream + // These events are delivered in order as well so that the stream will + // not emit more data after it has announced a contradictory state + function relayEvent(event: any) { + stream.on(event, async (message: any) => { + await queue.at(-1) + newStream.emit(event, message) + }) + } + relayEvent("close") + relayEvent("error") - return { - on: (channel: string, callback: any) => { - newStream.on(channel, callback) - }, - off: (channel: string, callback: any) => { - newStream.off(channel, callback) - }, - close: () => { - stream.close() - }, - } + return { + on: (channel: string, callback: any) => { + newStream.on(channel, callback) + }, + off: (channel: string, callback: any) => { + newStream.off(channel, callback) + }, + close: () => { + stream.close() + }, } +} diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index d6718a1a9..b2cc81bd2 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -1,4 +1,5 @@ import {log} from "@onflow/util-logger" +import {decodeStream} from "./decode-stream" const latestBlockDeprecationNotice = () => { log.deprecate({ @@ -165,35 +166,15 @@ export const decode = async ( return recurseDecode(decodeInstructions, decoders, stack) } -export const makeDecodeResponse = - (decodeStream, customDecoders = {}) => - async response => { - if (response.encodedData) { - return decode(response.encodedData, customDecoders) - } else if (response.transactionStatus) { - return { - ...response.transactionStatus, - events: await Promise.all( - response.transactionStatus.events.map(async function decodeEvents(e) { - return { - type: e.type, - transactionId: e.transactionId, - transactionIndex: e.transactionIndex, - eventIndex: e.eventIndex, - data: await decode(e.payload, customDecoders), - } - }) - ), - } - } else if (response.transaction) { - return response.transaction - } else if (response.events) { - return await Promise.all( - response.events.map(async function decodeEvents(e) { +export const decodeResponse = async (response, customDecoders = {}) => { + if (response.encodedData) { + return decode(response.encodedData, customDecoders) + } else if (response.transactionStatus) { + return { + ...response.transactionStatus, + events: await Promise.all( + response.transactionStatus.events.map(async function decodeEvents(e) { return { - blockId: e.blockId, - blockHeight: e.blockHeight, - blockTimestamp: e.blockTimestamp, type: e.type, transactionId: e.transactionId, transactionIndex: e.transactionIndex, @@ -201,35 +182,53 @@ export const makeDecodeResponse = data: await decode(e.payload, customDecoders), } }) - ) - } else if (response.account) { - return response.account - } else if (response.block) { - return response.block - } else if (response.blockHeader) { - return response.blockHeader - } else if (response.latestBlock) { - latestBlockDeprecationNotice() - return response.latestBlock - } else if (response.transactionId) { - return response.transactionId - } else if (response.collection) { - return response.collection - } else if (response.networkParameters) { - const chainIdMap = { - "flow-testnet": "testnet", - "flow-mainnet": "mainnet", - "flow-emulator": "local", - } - - return { - chainId: chainIdMap[response.networkParameters.chainId], - } - } else if (response.dataStream) { - return decodeStream(response.dataStream) - } else if (response.heartbeat) { - return response.heartbeat + ), + } + } else if (response.transaction) { + return response.transaction + } else if (response.events) { + return await Promise.all( + response.events.map(async function decodeEvents(e) { + return { + blockId: e.blockId, + blockHeight: e.blockHeight, + blockTimestamp: e.blockTimestamp, + type: e.type, + transactionId: e.transactionId, + transactionIndex: e.transactionIndex, + eventIndex: e.eventIndex, + data: await decode(e.payload, customDecoders), + } + }) + ) + } else if (response.account) { + return response.account + } else if (response.block) { + return response.block + } else if (response.blockHeader) { + return response.blockHeader + } else if (response.latestBlock) { + latestBlockDeprecationNotice() + return response.latestBlock + } else if (response.transactionId) { + return response.transactionId + } else if (response.collection) { + return response.collection + } else if (response.networkParameters) { + const chainIdMap = { + "flow-testnet": "testnet", + "flow-mainnet": "mainnet", + "flow-emulator": "local", } - return null + return { + chainId: chainIdMap[response.networkParameters.chainId], + } + } else if (response.dataStream) { + return decodeStream(response.dataStream) + } else if (response.heartbeat) { + return response.heartbeat } + + return null +} diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index 61a27efba..e5041ebac 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1,5 +1,6 @@ -import {decode, makeDecodeResponse} from "./decode.js" +import {decode, decodeResponse, decodeStream} from "./decode.js" import {Buffer} from "@onflow/rlp" +import * as decodeStreamModule from "./decode-stream" it("decodeResponse", async () => { const response = { @@ -15,7 +16,6 @@ it("decodeResponse", async () => { ), } - const decodeResponse = makeDecodeResponse(() => {}) const data = await decodeResponse(response) expect(data).toBe("7") }) @@ -1308,7 +1308,6 @@ describe("custom decoder tests", () => { describe("decode GetEvents tests", () => { it("decodes a GetEvents response correctly", async () => { - const decodeResponse = makeDecodeResponse(() => {}) const timestampISOString = new Date().toISOString() const getEventsResponse = { @@ -1343,7 +1342,6 @@ describe("decode GetEvents tests", () => { describe("decode GetTransactionStatus tests", () => { it("decodes a GetEvents response correctly", async () => { - const decodeResponse = makeDecodeResponse(() => {}) const getTransactionStatusResponse = { transactionStatus: { status: 4, @@ -1387,18 +1385,20 @@ describe("decode data stream tests", () => { const streamResponse = { dataStream: mockStream, } - const decodeStream = jest.fn().mockReturnValue(mockStream) - const decodeResponse = makeDecodeResponse(decodeStream) + const decodeStreamSpy = jest + .spyOn(decodeStreamModule, "decodeStream") + .mockImplementation(() => { + return mockStream + }) const decoded = await decodeResponse(streamResponse) expect(decoded).toBe(mockStream) - expect(decodeStream).toHaveBeenCalledWith(mockStream) + expect(decodeStreamSpy).toHaveBeenCalledWith(mockStream) }) }) describe("decode heartbeat tests", () => { it("decodes a heartbeat response correctly", async () => { - const decodeResponse = makeDecodeResponse(() => {}) const heartbeatResponse = { heartbeat: { timestamp: 123456789, diff --git a/packages/sdk/src/decode/sdk-decode.js b/packages/sdk/src/decode/sdk-decode.js index 7141e776b..6a1db6b72 100644 --- a/packages/sdk/src/decode/sdk-decode.js +++ b/packages/sdk/src/decode/sdk-decode.js @@ -1,6 +1,5 @@ import {config} from "@onflow/config" -import {makeDecodeResponse} from "./decode" -import {makeDecodeStream} from "./decode-stream" +import {decodeResponse} from "./decode" export async function decode(response) { const decodersFromConfig = await config().where(/^decoder\./) @@ -11,11 +10,5 @@ export async function decode(response) { } ) - const decodeResponse = makeDecodeResponse( - decodeStream, - Object.fromEntries(decoders) - ) - const decodeStream = makeDecodeStream(decodeResponse) - - return decodeResponse(response) + return decodeResponse(response, Object.fromEntries(decoders)) } diff --git a/packages/sdk/src/resolve/resolve.js b/packages/sdk/src/resolve/resolve.js index 508aae401..d1717bfe1 100644 --- a/packages/sdk/src/resolve/resolve.js +++ b/packages/sdk/src/resolve/resolve.js @@ -8,7 +8,7 @@ import {response} from "../response/response.js" import {build} from "../build/build.js" import {getBlock} from "../build/build-get-block.js" import {getAccount} from "../build/build-get-account.js" -import {makeDecodeResponse} from "../decode/decode.js" +import {decodeResponse as decode} from "../decode/decode.js" import {resolveCadence} from "./resolve-cadence.js" import {resolveArguments} from "./resolve-arguments.js" @@ -19,11 +19,6 @@ import {resolveFinalNormalization} from "./resolve-final-normalization.js" import {resolveVoucherIntercept} from "./resolve-voucher-intercept.js" import {resolveComputeLimit} from "./resolve-compute-limit.js" -// Decoder without stream support -const decode = makeDecodeResponse(() => { - throw new Error("Not Implemented") -}) - const noop = v => v const debug = (key, fn = noop) => diff --git a/packages/transport-http/src/connect-subscribe-events.test.ts b/packages/transport-http/src/connect-subscribe-events.test.ts index 95f961344..c4e03cda9 100644 --- a/packages/transport-http/src/connect-subscribe-events.test.ts +++ b/packages/transport-http/src/connect-subscribe-events.test.ts @@ -17,11 +17,9 @@ describe("Subscribe Events", () => { } }> let connectWsMock - let unsubscribeMock beforeEach(async () => { connectWsMock = jest.fn(() => mockStream) - unsubscribeMock = jest.fn() emitter = new EventEmitter() mockStream = { on: jest.fn((event, callback) => { @@ -244,4 +242,44 @@ describe("Subscribe Events", () => { expect(mockStream.close).toHaveBeenCalled() }) + + test("getParams for next connection should use next block height", async () => { + const response = await connectSubscribeEvents( + { + tag: "SUBSCRIBE_EVENTS", + subscribeEvents: { + startBlockId: "abc123", + eventTypes: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeatInterval: 1, + }, + }, + { + response: responseADT, + Buffer, + }, + { + connectWs: connectWsMock, + node: "http://localhost", + } + ) + + emitter.emit("data", { + BlockID: "abc123", + Height: 1, + Timestamp: "1", + Events: [], + }) + + await new Promise(resolve => setTimeout(resolve, 10)) + + expect(connectWsMock.mock.calls[0][0].getParams()).toEqual({ + start_height: 2, + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1, + }) + }) }) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index 19bc442b2..3972f6079 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -83,7 +83,7 @@ export async function connectSubscribeEvents( const connectWs: typeof defaultConnectWs = opts.connectWs || defaultConnectWs const outputEmitter = new EventEmitter() - let lastBlockId: string | null = null + let lastBlockHeight: string | null = null // Connect to the websocket & provide reconnection parameters const connection = connectWs({ @@ -98,8 +98,8 @@ export async function connectSubscribeEvents( } // If the lastBlockId is set, use it to resume the stream - if (lastBlockId) { - params.start_block_id = lastBlockId + 1 + if (lastBlockHeight) { + params.start_height = lastBlockHeight + 1 } else { params.start_block_id = ix.subscribeEvents.startBlockId params.start_height = ix.subscribeEvents.startHeight @@ -112,7 +112,7 @@ export async function connectSubscribeEvents( // Map the connection to a formatted response stream connection.on("data", (data: any) => { const responseData = constructData(ix, context, data) - lastBlockId = responseData.heartbeat.blockId + lastBlockHeight = responseData.heartbeat.blockHeight outputEmitter.emit("data", responseData) }) connection.on("error", (error: Error) => { From 4ae5c28e49b9829d5eccdaa2ffe23bdb3c74d7df Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 17:28:48 -0800 Subject: [PATCH 18/54] expand test --- .../src/connect-subscribe-events.test.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/transport-http/src/connect-subscribe-events.test.ts b/packages/transport-http/src/connect-subscribe-events.test.ts index c4e03cda9..64aaba7f8 100644 --- a/packages/transport-http/src/connect-subscribe-events.test.ts +++ b/packages/transport-http/src/connect-subscribe-events.test.ts @@ -243,7 +243,7 @@ describe("Subscribe Events", () => { expect(mockStream.close).toHaveBeenCalled() }) - test("getParams for next connection should use next block height", async () => { + test("getParams for next connection should use next block height if available", async () => { const response = await connectSubscribeEvents( { tag: "SUBSCRIBE_EVENTS", @@ -265,6 +265,16 @@ describe("Subscribe Events", () => { } ) + // Expect same params as initial connection since no data has been received + expect(connectWsMock.mock.calls[0][0].getParams()).toEqual({ + start_block_id: "abc123", + event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], + addresses: ["0x1", "0x2"], + contracts: ["A.7e60df042a9c0868.FlowToken"], + heartbeat_interval: 1, + }) + + // Expect next connection to use the next block height as it has seen a heartbeat emitter.emit("data", { BlockID: "abc123", Height: 1, @@ -272,8 +282,6 @@ describe("Subscribe Events", () => { Events: [], }) - await new Promise(resolve => setTimeout(resolve, 10)) - expect(connectWsMock.mock.calls[0][0].getParams()).toEqual({ start_height: 2, event_types: ["A.7e60df042a9c0868.FlowToken.TokensWithdrawn"], From 7597483141100381fb8bdff608cdd00b923f7ea6 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Fri, 24 Nov 2023 20:58:39 -0800 Subject: [PATCH 19/54] Fix CI & circular dep --- packages/sdk/src/decode/decode-stream.test.ts | 6 +++--- packages/sdk/src/decode/decode-stream.ts | 6 ++++-- packages/sdk/src/decode/decode.js | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts index 99458b4ed..38d7fdaad 100644 --- a/packages/sdk/src/decode/decode-stream.test.ts +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -4,9 +4,9 @@ import {StreamConnection} from "@onflow/typedefs" import * as decodeResponseModule from "./decode" describe("decode stream", () => { - let mockDecodeResponse: jest.Mock let mockStream: StreamConnection<{data: any}> let emitter: EventEmitter + let mockDecodeResponse beforeEach(() => { mockDecodeResponse = jest.fn() @@ -45,7 +45,7 @@ describe("decode stream", () => { } }) - const decodedStream = decodeStream(mockStream) + const decodedStream = decodeStream(mockStream, mockDecodeResponse) const dummyCallback = jest.fn(data => { expect(data).toEqual(decodedData.dummy) }) @@ -87,7 +87,7 @@ describe("decode stream", () => { } }) - const decodedStream = decodeStream(mockStream) + const decodedStream = decodeStream(mockStream, mockDecodeResponse) const cb = jest.fn() decodedStream.on("foo", msg => { cb("foo", msg) diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index d9ddcaf0d..a9087c4e2 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -1,9 +1,11 @@ import EventEmitter from "events" import {StreamConnection} from "@onflow/typedefs" -import {decodeResponse} from "./decode" // This function pipes a generic stream of data into a granular stream of decoded data -export const decodeStream = (stream: StreamConnection<{data: any}>) => { +export const decodeStream = ( + stream: StreamConnection<{data: any}>, + decodeResponse: (response: any) => Promise +) => { const newStream = new EventEmitter() let queue: Promise[] = [] diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index b2cc81bd2..fcd7921ed 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -225,7 +225,9 @@ export const decodeResponse = async (response, customDecoders = {}) => { chainId: chainIdMap[response.networkParameters.chainId], } } else if (response.dataStream) { - return decodeStream(response.dataStream) + return decodeStream(response.dataStream, response => + decodeResponse(response, customDecoders) + ) } else if (response.heartbeat) { return response.heartbeat } From 36869d0c9399c0dfc9f741c920e22375f0c4868d Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sat, 25 Nov 2023 15:02:36 -0800 Subject: [PATCH 20/54] Fix tests --- packages/sdk/src/decode/decode-stream.test.ts | 66 +++++++++++++++++-- packages/sdk/src/decode/decode-stream.ts | 57 +++++++++++----- packages/sdk/src/decode/decode.js | 4 +- packages/sdk/src/decode/decode.test.js | 16 ++++- 4 files changed, 114 insertions(+), 29 deletions(-) diff --git a/packages/sdk/src/decode/decode-stream.test.ts b/packages/sdk/src/decode/decode-stream.test.ts index 38d7fdaad..b3355d71a 100644 --- a/packages/sdk/src/decode/decode-stream.test.ts +++ b/packages/sdk/src/decode/decode-stream.test.ts @@ -45,7 +45,16 @@ describe("decode stream", () => { } }) - const decodedStream = decodeStream(mockStream, mockDecodeResponse) + const customDecoders = { + foo: jest.fn(), + bar: jest.fn(), + } + + const decodedStream = decodeStream( + mockStream, + mockDecodeResponse, + customDecoders + ) const dummyCallback = jest.fn(data => { expect(data).toEqual(decodedData.dummy) }) @@ -62,12 +71,20 @@ describe("decode stream", () => { // wait for next tick await new Promise(resolve => setTimeout(resolve, 0)) - expect(mockDecodeResponse).toHaveBeenCalledWith({ - dummy: {foo: "bar"}, - }) - expect(mockDecodeResponse).toHaveBeenCalledWith({ - other: {foo: "baz"}, - }) + expect(mockDecodeResponse).toHaveBeenNthCalledWith( + 1, + { + dummy: {foo: "bar"}, + }, + customDecoders + ) + expect(mockDecodeResponse).toHaveBeenNthCalledWith( + 2, + { + other: {foo: "baz"}, + }, + customDecoders + ) expect(dummyCallback).toHaveBeenCalled() expect(otherCallback).toHaveBeenCalled() @@ -105,4 +122,39 @@ describe("decode stream", () => { expect(cb).toHaveBeenNthCalledWith(1, "foo", "one") expect(cb).toHaveBeenNthCalledWith(2, "bar", "two") }) + + test("each channel is emitted in order", async () => { + const decodedStream = decodeStream(mockStream, mockDecodeResponse) + // Data will take time to decode but must arrive before error/close + mockDecodeResponse.mockImplementation(async response => { + await new Promise(resolve => setTimeout(resolve, 100)) + return response + }) + const cb = jest.fn() + decodedStream.on("foo", msg => { + cb("foo", msg) + }) + decodedStream.on("bar", msg => { + cb("bar", msg) + }) + decodedStream.on("error", err => { + cb("error", err) + }) + decodedStream.on("close", () => { + cb("close") + }) + + emitter.emit("data", {foo: "one"}) + emitter.emit("error", new Error("error")) + emitter.emit("data", {bar: "two"}) + emitter.emit("close") + + // Wait for data to be processed + await new Promise(resolve => setTimeout(resolve, 250)) + + expect(cb).toHaveBeenNthCalledWith(1, "foo", {foo: "one"}) + expect(cb).toHaveBeenNthCalledWith(2, "error", new Error("error")) + expect(cb).toHaveBeenNthCalledWith(3, "bar", {bar: "two"}) + expect(cb).toHaveBeenNthCalledWith(4, "close") + }) }) diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index a9087c4e2..2cd7970bf 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -1,13 +1,18 @@ import EventEmitter from "events" import {StreamConnection} from "@onflow/typedefs" +import type {decodeResponse as decodeResponseFn} from "./decode" -// This function pipes a generic stream of data into a granular stream of decoded data +/** + * Pipes a generic stream of data into a granular stream of decoded data + * The data is decoded per channel and emitted in order + */ export const decodeStream = ( stream: StreamConnection<{data: any}>, - decodeResponse: (response: any) => Promise + decodeResponse: typeof decodeResponseFn, + customDecoders?: Record ) => { const newStream = new EventEmitter() - let queue: Promise[] = [] + let queue = taskQueue() // Data is separated by topic & the decoded data is emitted in order // All topics for a given message will be emitted synchronously before moving on to the next message @@ -22,23 +27,20 @@ export const decodeStream = ( const partialResponse = { [channel]: data[channel], } - const message = await decodeResponse(partialResponse) + const message = await decodeResponse(partialResponse, customDecoders) return { channel, message, } }) ) - queue.push(newDataPromise) - // Wait for the previous data to be emitted before emitting the new data - await queue[0] - queue.shift() - - // Emit the new data - const newData = await newDataPromise - newData.forEach(({channel, message}) => { - newStream.emit(channel, message) + queue.push(async () => { + // Emit the new data + const newData = await newDataPromise + newData.forEach(({channel, message}) => { + newStream.emit(channel, message) + }) }) }) @@ -46,9 +48,10 @@ export const decodeStream = ( // These events are delivered in order as well so that the stream will // not emit more data after it has announced a contradictory state function relayEvent(event: any) { - stream.on(event, async (message: any) => { - await queue.at(-1) - newStream.emit(event, message) + stream.on(event, (message: any) => { + queue.push(async () => { + newStream.emit(event, message) + }) }) } relayEvent("close") @@ -66,3 +69,25 @@ export const decodeStream = ( }, } } + +function taskQueue() { + let queue: (() => Promise)[] = [] as any as (() => Promise)[] + let running = false + + async function run() { + if (running) return + running = true + while (queue.length > 0) { + const task = queue.shift() + await task?.() + } + running = false + } + + return { + push: (task: () => Promise) => { + queue.push(task) + run() + }, + } +} diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index fcd7921ed..ad0da2ac5 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -225,9 +225,7 @@ export const decodeResponse = async (response, customDecoders = {}) => { chainId: chainIdMap[response.networkParameters.chainId], } } else if (response.dataStream) { - return decodeStream(response.dataStream, response => - decodeResponse(response, customDecoders) - ) + return decodeStream(response.dataStream, decodeResponse, customDecoders) } else if (response.heartbeat) { return response.heartbeat } diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index e5041ebac..2f538758a 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1,6 +1,7 @@ -import {decode, decodeResponse, decodeStream} from "./decode.js" +import {decode, decodeResponse} from "./decode" import {Buffer} from "@onflow/rlp" import * as decodeStreamModule from "./decode-stream" +import * as decodeModule from "./decode" it("decodeResponse", async () => { const response = { @@ -1390,10 +1391,15 @@ describe("decode data stream tests", () => { .mockImplementation(() => { return mockStream }) - const decoded = await decodeResponse(streamResponse) + const decoders = {foo: () => {}} + const decoded = await decodeResponse(streamResponse, decoders) expect(decoded).toBe(mockStream) - expect(decodeStreamSpy).toHaveBeenCalledWith(mockStream) + expect(decodeStreamSpy).toHaveBeenCalledWith( + mockStream, + decodeResponse, + decoders + ) }) }) @@ -1410,3 +1416,7 @@ describe("decode heartbeat tests", () => { }) }) }) + +afterEach(() => { + jest.restoreAllMocks() +}) From 77b90d040576ff9cadf2378c5c23a87881297869 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sat, 25 Nov 2023 16:04:56 -0800 Subject: [PATCH 21/54] fix tests --- packages/sdk/src/decode/decode-stream.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index 2cd7970bf..8ee302031 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -1,6 +1,10 @@ import EventEmitter from "events" import {StreamConnection} from "@onflow/typedefs" -import type {decodeResponse as decodeResponseFn} from "./decode" + +type DecodeResponseFn = ( + response: Record, + customDecoders?: Record +) => Promise /** * Pipes a generic stream of data into a granular stream of decoded data @@ -8,7 +12,7 @@ import type {decodeResponse as decodeResponseFn} from "./decode" */ export const decodeStream = ( stream: StreamConnection<{data: any}>, - decodeResponse: typeof decodeResponseFn, + decodeResponse: DecodeResponseFn, customDecoders?: Record ) => { const newStream = new EventEmitter() From 04062f2716fe703b4a48842ff12c564b58064e8d Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Sat, 25 Nov 2023 16:08:16 -0800 Subject: [PATCH 22/54] rename heartbeat --- packages/typedefs/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/typedefs/src/index.ts b/packages/typedefs/src/index.ts index 314230d3d..8723b8458 100644 --- a/packages/typedefs/src/index.ts +++ b/packages/typedefs/src/index.ts @@ -311,7 +311,7 @@ export interface EventFilter { contracts?: string[] } -export interface EventStreamHeartbeat { +export interface BlockHeartbeat { blockId: string blockHeight: number timestamp: string @@ -319,5 +319,5 @@ export interface EventStreamHeartbeat { export type EventStream = StreamConnection<{ events: Event[] - heartbeat: EventStreamHeartbeat + heartbeat: BlockHeartbeat }> From e6083c9a748f6c9ded7d755a88f86cb6273b6c9c Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 28 Nov 2023 14:28:14 -0800 Subject: [PATCH 23/54] Fix types --- package-lock.json | 9 +++++---- packages/fcl-bundle/package.json | 2 +- packages/fcl-bundle/src/build/build.js | 2 +- packages/fcl-bundle/src/build/get-input-options.js | 5 ++++- packages/fcl/package.json | 2 +- packages/fcl/src/events/{index.ts => events.ts} | 0 .../fcl/src/{fcl-react-native.js => fcl-react-native.ts} | 0 packages/fcl/src/shared-exports.js | 2 +- packages/fcl/tsconfig.json | 3 ++- packages/sdk/src/build/build-subscribe-events.ts | 4 ++-- packages/transport-http/src/send-http.ts | 4 +++- 11 files changed, 20 insertions(+), 13 deletions(-) rename packages/fcl/src/events/{index.ts => events.ts} (100%) rename packages/fcl/src/{fcl-react-native.js => fcl-react-native.ts} (100%) diff --git a/package-lock.json b/package-lock.json index 92f5bdff5..31ca1d38f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4211,8 +4211,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.1.0", - "license": "MIT", + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", @@ -4225,7 +4226,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.78.0||^3.0.0" + "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -19533,7 +19534,7 @@ "@babel/preset-typescript": "^7.22.5", "@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-commonjs": "^25.0.2", - "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-terser": "^0.4.3", "builtin-modules": "^3.3.0", diff --git a/packages/fcl-bundle/package.json b/packages/fcl-bundle/package.json index 61c217ade..675b22cd1 100644 --- a/packages/fcl-bundle/package.json +++ b/packages/fcl-bundle/package.json @@ -18,7 +18,7 @@ "@babel/preset-typescript": "^7.22.5", "@rollup/plugin-babel": "^6.0.3", "@rollup/plugin-commonjs": "^25.0.2", - "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-terser": "^0.4.3", "builtin-modules": "^3.3.0", diff --git a/packages/fcl-bundle/src/build/build.js b/packages/fcl-bundle/src/build/build.js index af3437255..c098d75eb 100644 --- a/packages/fcl-bundle/src/build/build.js +++ b/packages/fcl-bundle/src/build/build.js @@ -23,7 +23,7 @@ async function buildModule(build, package) { if (bundle) await bundle.close() if (buildError) { - throw new Error(buildError) + throw buildError } } diff --git a/packages/fcl-bundle/src/build/get-input-options.js b/packages/fcl-bundle/src/build/get-input-options.js index df5ff3978..0f3f9b55f 100644 --- a/packages/fcl-bundle/src/build/get-input-options.js +++ b/packages/fcl-bundle/src/build/get-input-options.js @@ -56,6 +56,8 @@ module.exports = function getInputOptions(package, build) { ), ] + const extensions = DEFAULT_EXTENSIONS.concat([".ts", ".tsx", ".mts", ".cts"]) + let options = { input: build.source, external: testExternal, @@ -68,6 +70,7 @@ module.exports = function getInputOptions(package, build) { browser: true, preferBuiltins: build.type !== "umd", resolveOnly, + extensions, }), commonjs(), isTypeScript && @@ -108,7 +111,7 @@ module.exports = function getInputOptions(package, build) { ], ], sourceMaps: true, - extensions: [...DEFAULT_EXTENSIONS, ".ts", ".tsx"], + extensions, }), /\.min\.js$/.test(build.entry) && terser({ diff --git a/packages/fcl/package.json b/packages/fcl/package.json index fd65dadb0..82fd53b0f 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -36,7 +36,7 @@ "esm": "./dist/fcl.module.js", "umd": "./dist/fcl.umd.min.js" }, - "src/fcl-react-native.js": { + "src/fcl-react-native.ts": { "cjs": "./dist/fcl-react-native.js", "esm": "./dist/fcl-react-native.module.js", "umd": "./dist/fcl-react-native.umd.min.js" diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/events.ts similarity index 100% rename from packages/fcl/src/events/index.ts rename to packages/fcl/src/events/events.ts diff --git a/packages/fcl/src/fcl-react-native.js b/packages/fcl/src/fcl-react-native.ts similarity index 100% rename from packages/fcl/src/fcl-react-native.js rename to packages/fcl/src/fcl-react-native.ts diff --git a/packages/fcl/src/shared-exports.js b/packages/fcl/src/shared-exports.js index 35b0e1090..e84b6976f 100644 --- a/packages/fcl/src/shared-exports.js +++ b/packages/fcl/src/shared-exports.js @@ -3,7 +3,7 @@ export {query} from "./exec/query" export {verifyUserSignatures} from "./exec/verify" export {serialize} from "./serialize" export {transaction as tx} from "./transaction" -export {events} from "./events" +export {events} from "./events/events" export {pluginRegistry} from "./current-user/exec-service/plugins" import {discovery} from "./discovery" diff --git a/packages/fcl/tsconfig.json b/packages/fcl/tsconfig.json index 8ca9e12c9..5b3975581 100644 --- a/packages/fcl/tsconfig.json +++ b/packages/fcl/tsconfig.json @@ -4,6 +4,7 @@ "include": ["src/**/*"], "compilerOptions": { "declarationDir": "types", - "rootDir": "./src" + "rootDir": "src", + "allowJs": true, } } diff --git a/packages/sdk/src/build/build-subscribe-events.ts b/packages/sdk/src/build/build-subscribe-events.ts index 75ef35a56..a2067b578 100644 --- a/packages/sdk/src/build/build-subscribe-events.ts +++ b/packages/sdk/src/build/build-subscribe-events.ts @@ -16,7 +16,7 @@ export function subscribeEvents({ startBlockId?: string startHeight?: number heartbeatInterval?: number -}) { +}): Function { invariant( !(startBlockId && startHeight), `SDK Subscribe Events Error: Cannot set both startBlockId and startHeight.` @@ -33,5 +33,5 @@ export function subscribeEvents({ ix.subscribeEvents.heartbeatInterval = heartbeatInterval return Ok(ix) }, - ]) + ]) as any } diff --git a/packages/transport-http/src/send-http.ts b/packages/transport-http/src/send-http.ts index 58069341d..8af2e20a7 100644 --- a/packages/transport-http/src/send-http.ts +++ b/packages/transport-http/src/send-http.ts @@ -11,7 +11,7 @@ import {sendGetBlockHeader} from "./send-get-block-header.js" import {sendGetCollection} from "./send-get-collection.js" import {sendPing, ISendPingContext} from "./send-ping" import {sendGetNetworkParameters} from "./send-get-network-parameters.js" -import { Interaction } from "@onflow/typedefs" +import {Interaction} from "@onflow/typedefs" interface InteractionModule { isTransaction: (ix: Interaction) => boolean; @@ -25,6 +25,7 @@ interface InteractionModule { isGetCollection: (ix: Interaction) => boolean; isPing: (ix: Interaction) => boolean; isGetNetworkParameters: (ix: Interaction) => boolean; + isSubscribeEvents: (ix: Interaction) => boolean; } interface IContext extends ISendPingContext{ ix: InteractionModule; @@ -46,6 +47,7 @@ interface IOpts extends IOptsCommon { sendPing?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void sendGetBlock?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void sendGetNetworkParameters?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void + connectSubscribeEvents?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void } export const send = async (ix: Interaction, context: IContext, opts: IOpts = {}) => { From d5db623be68651849cda391407384cadb63f41c4 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 28 Nov 2023 14:54:05 -0800 Subject: [PATCH 24/54] fix test --- packages/sdk/src/build/build-subscribe-events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/build/build-subscribe-events.ts b/packages/sdk/src/build/build-subscribe-events.ts index a2067b578..8a48f0c66 100644 --- a/packages/sdk/src/build/build-subscribe-events.ts +++ b/packages/sdk/src/build/build-subscribe-events.ts @@ -1,5 +1,5 @@ import {invariant} from "@onflow/util-invariant" -import {pipe, Ok, makeSubscribeEvents} from "../interaction/interaction.js" +import {pipe, Ok, makeSubscribeEvents} from "../interaction/interaction" import {EventFilter} from "@onflow/typedefs" /** From 7cb71a0ef110f99595566f378c4695d3983d8224 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 28 Nov 2023 14:57:21 -0800 Subject: [PATCH 25/54] Update events --- packages/fcl/src/events/{events.ts => index.ts} | 0 packages/fcl/src/shared-exports.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/fcl/src/events/{events.ts => index.ts} (100%) diff --git a/packages/fcl/src/events/events.ts b/packages/fcl/src/events/index.ts similarity index 100% rename from packages/fcl/src/events/events.ts rename to packages/fcl/src/events/index.ts diff --git a/packages/fcl/src/shared-exports.js b/packages/fcl/src/shared-exports.js index e84b6976f..35b0e1090 100644 --- a/packages/fcl/src/shared-exports.js +++ b/packages/fcl/src/shared-exports.js @@ -3,7 +3,7 @@ export {query} from "./exec/query" export {verifyUserSignatures} from "./exec/verify" export {serialize} from "./serialize" export {transaction as tx} from "./transaction" -export {events} from "./events/events" +export {events} from "./events" export {pluginRegistry} from "./current-user/exec-service/plugins" import {discovery} from "./discovery" From ce671837fd08d3a76e0321131d843bbba9ad9db7 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:22:37 -0800 Subject: [PATCH 26/54] Rename dataStream to streamConnection --- packages/fcl/src/shared-exports.js | 6 ++---- packages/sdk/src/decode/decode.js | 4 ++-- packages/sdk/src/decode/decode.test.js | 2 +- .../sdk/src/response/__snapshots__/response.test.ts.snap | 2 +- packages/sdk/src/response/response.ts | 2 +- .../transport-http/src/connect-subscribe-events.test.ts | 6 +++--- packages/transport-http/src/connect-subscribe-events.ts | 2 +- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/fcl/src/shared-exports.js b/packages/fcl/src/shared-exports.js index 35b0e1090..761319de1 100644 --- a/packages/fcl/src/shared-exports.js +++ b/packages/fcl/src/shared-exports.js @@ -10,10 +10,7 @@ import {discovery} from "./discovery" export {discovery} import * as types from "@onflow/types" -/** - * @type {Types} - */ -export const t = types +export {types as t} import * as WalletUtils from "./wallet-utils" export {WalletUtils} @@ -123,6 +120,7 @@ export {invariant} from "@onflow/sdk" */ import {watchForChainIdChanges} from "./utils" +import { t } from "./shared-exports" // Set chain id default on access node change watchForChainIdChanges() diff --git a/packages/sdk/src/decode/decode.js b/packages/sdk/src/decode/decode.js index ad0da2ac5..37e7c226f 100644 --- a/packages/sdk/src/decode/decode.js +++ b/packages/sdk/src/decode/decode.js @@ -224,8 +224,8 @@ export const decodeResponse = async (response, customDecoders = {}) => { return { chainId: chainIdMap[response.networkParameters.chainId], } - } else if (response.dataStream) { - return decodeStream(response.dataStream, decodeResponse, customDecoders) + } else if (response.streamConnection) { + return decodeStream(response.streamConnection, decodeResponse, customDecoders) } else if (response.heartbeat) { return response.heartbeat } diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index 2f538758a..e69f27442 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1384,7 +1384,7 @@ describe("decode data stream tests", () => { it("calls decodeStream to decode data streams", async () => { let mockStream = {} const streamResponse = { - dataStream: mockStream, + streamConnection: mockStream, } const decodeStreamSpy = jest .spyOn(decodeStreamModule, "decodeStream") diff --git a/packages/sdk/src/response/__snapshots__/response.test.ts.snap b/packages/sdk/src/response/__snapshots__/response.test.ts.snap index 941ff3078..ac176135f 100644 --- a/packages/sdk/src/response/__snapshots__/response.test.ts.snap +++ b/packages/sdk/src/response/__snapshots__/response.test.ts.snap @@ -6,7 +6,7 @@ exports[`Response - Snapshot 1`] = ` "block": null, "blockHeader": null, "collection": null, - "dataStream": null, + "streamConnection": null, "encodedData": null, "events": null, "heartbeat": null, diff --git a/packages/sdk/src/response/response.ts b/packages/sdk/src/response/response.ts index 9ccbc0837..904bcf14c 100644 --- a/packages/sdk/src/response/response.ts +++ b/packages/sdk/src/response/response.ts @@ -11,7 +11,7 @@ const DEFAULT_RESPONSE = { "latestBlock":null, "collection":null, "networkParameters":null, - "dataStream":null, + "streamConnection":null, "heartbeat":null } diff --git a/packages/transport-http/src/connect-subscribe-events.test.ts b/packages/transport-http/src/connect-subscribe-events.test.ts index 64aaba7f8..37ce32db0 100644 --- a/packages/transport-http/src/connect-subscribe-events.test.ts +++ b/packages/transport-http/src/connect-subscribe-events.test.ts @@ -121,7 +121,7 @@ describe("Subscribe Events", () => { ).toString("base64") let allData: any[] = [] - response.dataStream.on("data", data => { + response.streamConnection.on("data", data => { allData.push(data) }) @@ -206,7 +206,7 @@ describe("Subscribe Events", () => { } ) - response.dataStream.close() + response.streamConnection.close() await new Promise(resolve => setTimeout(resolve, 0)) @@ -236,7 +236,7 @@ describe("Subscribe Events", () => { } ) - response.dataStream.close() + response.streamConnection.close() await new Promise(resolve => setTimeout(resolve, 0)) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index 3972f6079..bc0fc44e5 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -59,7 +59,7 @@ function constructResponse(ix: any, context: any, stream: any) { let ret = context.response() ret.tag = ix.tag - ret.dataStream = stream + ret.streamConnection = stream return ret } From cb2b096fca64a1d5779feb6413aa36ea51d6b8fc Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:23:06 -0800 Subject: [PATCH 27/54] rename test --- packages/sdk/src/decode/decode.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/decode/decode.test.js b/packages/sdk/src/decode/decode.test.js index e69f27442..db5d3387a 100644 --- a/packages/sdk/src/decode/decode.test.js +++ b/packages/sdk/src/decode/decode.test.js @@ -1380,8 +1380,8 @@ describe("decode GetTransactionStatus tests", () => { }) }) -describe("decode data stream tests", () => { - it("calls decodeStream to decode data streams", async () => { +describe("decode stream connection tests", () => { + it("calls decodeStream to decode stream connection", async () => { let mockStream = {} const streamResponse = { streamConnection: mockStream, From bfab951a3ea87ba790f9a95fb68a2667511fa6d0 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:28:11 -0800 Subject: [PATCH 28/54] rename shared-exports --- packages/fcl/src/{shared-exports.js => shared-exports.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/fcl/src/{shared-exports.js => shared-exports.ts} (100%) diff --git a/packages/fcl/src/shared-exports.js b/packages/fcl/src/shared-exports.ts similarity index 100% rename from packages/fcl/src/shared-exports.js rename to packages/fcl/src/shared-exports.ts From 4973540480fb4e601b51f64271b0fdc87ecfbd21 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:32:04 -0800 Subject: [PATCH 29/54] fix snapshot --- packages/sdk/src/response/__snapshots__/response.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/response/__snapshots__/response.test.ts.snap b/packages/sdk/src/response/__snapshots__/response.test.ts.snap index ac176135f..00b7921b0 100644 --- a/packages/sdk/src/response/__snapshots__/response.test.ts.snap +++ b/packages/sdk/src/response/__snapshots__/response.test.ts.snap @@ -6,12 +6,12 @@ exports[`Response - Snapshot 1`] = ` "block": null, "blockHeader": null, "collection": null, - "streamConnection": null, "encodedData": null, "events": null, "heartbeat": null, "latestBlock": null, "networkParameters": null, + "streamConnection": null, "tag": null, "transaction": null, "transactionId": null, From 3d5075e7ceb0683cc1a9a13df1271f9593724dff Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:44:24 -0800 Subject: [PATCH 30/54] Remove unrelated diff --- packages/fcl/src/fcl-react-native.js | 40 +++++++++++++++++++ .../generate-template-id.js | 2 +- .../generate-template-interface-id.js | 2 +- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 packages/fcl/src/fcl-react-native.js diff --git a/packages/fcl/src/fcl-react-native.js b/packages/fcl/src/fcl-react-native.js new file mode 100644 index 000000000..0ecf8a8b8 --- /dev/null +++ b/packages/fcl/src/fcl-react-native.js @@ -0,0 +1,40 @@ +export * from "./shared-exports" + +import {getMutate} from "./exec/mutate" +export const mutate = getMutate({platform: "react-native"}) + +import {getCurrentUser} from "./current-user" +const currentUser = getCurrentUser({platform: "react-native"}) + +export {currentUser} + +export const authenticate = (opts = {}) => currentUser().authenticate(opts) +export const unauthenticate = () => currentUser().unauthenticate() +export const reauthenticate = (opts = {}) => { + currentUser().unauthenticate() + return currentUser().authenticate(opts) +} +export const signUp = (opts = {}) => currentUser().authenticate(opts) +export const logIn = (opts = {}) => currentUser().authenticate(opts) + +export const authz = currentUser().authorization + +import {config} from "@onflow/config" +import { + coreStrategies, + getDefaultConfig, + useServiceDiscovery, + ServiceDiscovery, +} from "./utils/react-native" +import {initServiceRegistry} from "./current-user/exec-service/plugins" +import {setIsReactNative} from "./utils/is-react-native" + +config(getDefaultConfig()) + +// Set chain id default on access node change +initServiceRegistry({coreStrategies}) + +// Set isReactNative flag +setIsReactNative(true) + +export {useServiceDiscovery, ServiceDiscovery} diff --git a/packages/fcl/src/interaction-template-utils/generate-template-id.js b/packages/fcl/src/interaction-template-utils/generate-template-id.js index 453db190d..f72f121b7 100644 --- a/packages/fcl/src/interaction-template-utils/generate-template-id.js +++ b/packages/fcl/src/interaction-template-utils/generate-template-id.js @@ -5,7 +5,7 @@ import {normalizeInteractionTemplate} from "../normalizers/interaction-template/ /** * @description Generates Interaction Template ID for a given Interaction Template - * + * * @param {object} params * @param {object} params.template - Interaction Template * @returns {Promise} - Interaction Template ID diff --git a/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js b/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js index 3958f3bd5..0a75932f8 100644 --- a/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js +++ b/packages/fcl/src/interaction-template-utils/generate-template-interface-id.js @@ -5,7 +5,7 @@ import {normalizeInteractionTemplateInterface} from "../normalizers/interaction- /** * @description Generates Interaction Template Interface ID for a given Interaction Template Interface - * + * * @param {object} params * @param {object} params.templateInterface - Interaction Template Interface * @returns {Promise} - Interaction Template Interface ID From 0cc70db5a52486b5e537566fe2d015fb375011c0 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:54:58 -0800 Subject: [PATCH 31/54] Make decodeStream conform to StreamConnection interface --- packages/sdk/src/decode/decode-stream.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/decode/decode-stream.ts b/packages/sdk/src/decode/decode-stream.ts index 8ee302031..06bc1d496 100644 --- a/packages/sdk/src/decode/decode-stream.ts +++ b/packages/sdk/src/decode/decode-stream.ts @@ -14,7 +14,7 @@ export const decodeStream = ( stream: StreamConnection<{data: any}>, decodeResponse: DecodeResponseFn, customDecoders?: Record -) => { +): StreamConnection => { const newStream = new EventEmitter() let queue = taskQueue() @@ -62,11 +62,13 @@ export const decodeStream = ( relayEvent("error") return { - on: (channel: string, callback: any) => { + on(channel: string, callback: any) { newStream.on(channel, callback) + return this }, - off: (channel: string, callback: any) => { + off(channel: string, callback: any) { newStream.off(channel, callback) + return this }, close: () => { stream.close() From 7420cb6e6a05e10cc43c4bde47635bb27b48c9d8 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:57:58 -0800 Subject: [PATCH 32/54] remove dummy payload --- .../transport-http/src/connect-subscribe-events.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index bc0fc44e5..cf7733be2 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -15,19 +15,6 @@ type RawSubscribeEventsStream = StreamConnection<{ }> function constructData(ix: any, context: any, data: any) { - // TODO REMOVE ME - // DUMMY PAYLOAD UNTIL ACCESS NODE BUG IS FIXED - if (data.Events) { - data.Events = data.Events.map((event: any) => ({ - ...event, - Payload: - "eyJ2YWx1ZSI6eyJpZCI6IkEuOTEyZDU0NDBmN2UzNzY5ZS5GbG93RmVlcy5GZWVzRGVkdWN0ZWQiLCJmaWVsZHMiOlt7InZhbHVlIjp7InZhbHVlIjoiMC4wMDAwMDExOSIsInR5cGUiOiJVRml4NjQifSwibmFtZSI6ImFtb3VudCJ9LHsidmFsdWUiOnsidmFsdWUiOiIxLjAwMDAwMDAwIiwidHlwZSI6IlVGaXg2NCJ9LCJuYW1lIjoiaW5jbHVzaW9uRWZmb3J0In0seyJ2YWx1ZSI6eyJ2YWx1ZSI6IjAuMDAwMDAwMDQiLCJ0eXBlIjoiVUZpeDY0In0sIm5hbWUiOiJleGVjdXRpb25FZmZvcnQifV19LCJ0eXBlIjoiRXZlbnQifQo=", - })) - } - // PLEASE REMOVE ME - // PLEASE IF YOU ARE REVIEWING THIS AND I FORGOT ABOUT IT - // PLEASE REMOVE ME :) - let ret = context.response() ret.tag = ix.tag From b80a75c495e5e25d59d725e640dc032799600352 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 11:59:26 -0800 Subject: [PATCH 33/54] restore builderror --- packages/fcl-bundle/src/build/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fcl-bundle/src/build/build.js b/packages/fcl-bundle/src/build/build.js index c098d75eb..af3437255 100644 --- a/packages/fcl-bundle/src/build/build.js +++ b/packages/fcl-bundle/src/build/build.js @@ -23,7 +23,7 @@ async function buildModule(build, package) { if (bundle) await bundle.close() if (buildError) { - throw buildError + throw new Error(buildError) } } From b19fa19b2ac7621177a84ce4f3cb7d2b89e1edd2 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 13:12:47 -0800 Subject: [PATCH 34/54] Address feedback --- .../sdk/src/build/build-subscribe-events.ts | 16 +++---- .../src/connect-subscribe-events.ts | 42 +++++++++---------- packages/typedefs/src/interaction.ts | 8 ++++ 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/packages/sdk/src/build/build-subscribe-events.ts b/packages/sdk/src/build/build-subscribe-events.ts index 8a48f0c66..8563fc840 100644 --- a/packages/sdk/src/build/build-subscribe-events.ts +++ b/packages/sdk/src/build/build-subscribe-events.ts @@ -1,6 +1,6 @@ import {invariant} from "@onflow/util-invariant" import {pipe, Ok, makeSubscribeEvents} from "../interaction/interaction" -import {EventFilter} from "@onflow/typedefs" +import {EventFilter, Interaction} from "@onflow/typedefs" /** * Subscribe to events with the given filter & parameters @@ -24,13 +24,13 @@ export function subscribeEvents({ return pipe([ makeSubscribeEvents, - (ix: any) => { - ix.subscribeEvents.startBlockId = startBlockId - ix.subscribeEvents.startHeight = startHeight - ix.subscribeEvents.eventTypes = eventTypes - ix.subscribeEvents.addresses = addresses - ix.subscribeEvents.contracts = contracts - ix.subscribeEvents.heartbeatInterval = heartbeatInterval + (ix: Interaction) => { + ix.subscribeEvents.startBlockId = startBlockId ?? null + ix.subscribeEvents.startHeight = startHeight ?? null + ix.subscribeEvents.eventTypes = eventTypes ?? null + ix.subscribeEvents.addresses = addresses ?? null + ix.subscribeEvents.contracts = contracts ?? null + ix.subscribeEvents.heartbeatInterval = heartbeatInterval ?? null return Ok(ix) }, ]) as any diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index cf7733be2..acb9e5a2d 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -1,24 +1,20 @@ import {invariant} from "@onflow/util-invariant" import {connectWs as defaultConnectWs} from "./connect-ws" import {EventEmitter} from "events" -import {StreamConnection} from "@onflow/typedefs" +import {BlockHeartbeat, Interaction, StreamConnection} from "@onflow/typedefs" type RawSubscribeEventsStream = StreamConnection<{ data: { events: any[] - heartbeat: { - blockId: string - blockHeight: number - blockTimestamp: number - } + heartbeat: BlockHeartbeat } }> function constructData(ix: any, context: any, data: any) { - let ret = context.response() - ret.tag = ix.tag + const response = context.response() + response.tag = ix.tag - ret.events = + response.events = data.Events?.length > 0 ? data.Events.map((event: any) => ({ blockId: data.BlockID, @@ -33,26 +29,26 @@ function constructData(ix: any, context: any, data: any) { ), })) : null - ret.heartbeat = { + response.heartbeat = { blockId: data.BlockID, blockHeight: Number(data.Height), blockTimestamp: data.Timestamp, } - return ret + return response } function constructResponse(ix: any, context: any, stream: any) { - let ret = context.response() - ret.tag = ix.tag + const response = context.response() + response.tag = ix.tag - ret.streamConnection = stream + response.streamConnection = stream - return ret + return response } export async function connectSubscribeEvents( - ix: any, + ix: Interaction | Promise, context: any = {}, opts: any = {} ) { @@ -66,7 +62,7 @@ export async function connectSubscribeEvents( `SDK Send Get Events Error: context.Buffer must be defined.` ) - ix = await ix + const resolvedIx = await ix const connectWs: typeof defaultConnectWs = opts.connectWs || defaultConnectWs const outputEmitter = new EventEmitter() @@ -78,18 +74,18 @@ export async function connectSubscribeEvents( path: `/v1/subscribe_events`, getParams: () => { const params: Record = { - event_types: ix.subscribeEvents.eventTypes, - addresses: ix.subscribeEvents.addresses, - contracts: ix.subscribeEvents.contracts, - heartbeat_interval: ix.subscribeEvents.heartbeatInterval, + event_types: resolvedIx.subscribeEvents?.eventTypes, + addresses: resolvedIx.subscribeEvents?.addresses, + contracts: resolvedIx.subscribeEvents?.contracts, + heartbeat_interval: resolvedIx.subscribeEvents?.heartbeatInterval, } // If the lastBlockId is set, use it to resume the stream if (lastBlockHeight) { params.start_height = lastBlockHeight + 1 } else { - params.start_block_id = ix.subscribeEvents.startBlockId - params.start_height = ix.subscribeEvents.startHeight + params.start_block_id = resolvedIx.subscribeEvents?.startBlockId + params.start_height = resolvedIx.subscribeEvents?.startHeight } return params diff --git a/packages/typedefs/src/interaction.ts b/packages/typedefs/src/interaction.ts index f51d65759..df4d53b4c 100644 --- a/packages/typedefs/src/interaction.ts +++ b/packages/typedefs/src/interaction.ts @@ -88,5 +88,13 @@ export interface Interaction { } collection: { id: string | null + }, + subscribeEvents: { + eventTypes: string | string[] | null + addresses: string | string[] | null + contracts: string | string[] | null + startBlockId: string | null + startHeight: number | null + heartbeatInterval: number | null } } From 59debea55209adab984d2c0a139aeedf8f09186b Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 13:19:01 -0800 Subject: [PATCH 35/54] changesets --- .changeset/lucky-moons-smell.md | 5 +++++ .changeset/mean-bugs-cry.md | 5 +++++ .changeset/old-plants-nail.md | 5 +++++ .changeset/sixty-turkeys-destroy.md | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 .changeset/lucky-moons-smell.md create mode 100644 .changeset/mean-bugs-cry.md create mode 100644 .changeset/old-plants-nail.md create mode 100644 .changeset/sixty-turkeys-destroy.md diff --git a/.changeset/lucky-moons-smell.md b/.changeset/lucky-moons-smell.md new file mode 100644 index 000000000..4c3921282 --- /dev/null +++ b/.changeset/lucky-moons-smell.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl": minor +--- + +Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Syntax remains unchanged & can be accessed via fcl.events(). diff --git a/.changeset/mean-bugs-cry.md b/.changeset/mean-bugs-cry.md new file mode 100644 index 000000000..14579dfaf --- /dev/null +++ b/.changeset/mean-bugs-cry.md @@ -0,0 +1,5 @@ +--- +"@onflow/sdk": minor +--- + +Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Can be accessed through new SDK builder sdk.subscribeEvents(...). diff --git a/.changeset/old-plants-nail.md b/.changeset/old-plants-nail.md new file mode 100644 index 000000000..d753af067 --- /dev/null +++ b/.changeset/old-plants-nail.md @@ -0,0 +1,5 @@ +--- +"@onflow/transport-http": minor +--- + +Add support for event streaming API interaction (subscribeEvents) diff --git a/.changeset/sixty-turkeys-destroy.md b/.changeset/sixty-turkeys-destroy.md new file mode 100644 index 000000000..c83ef6f8a --- /dev/null +++ b/.changeset/sixty-turkeys-destroy.md @@ -0,0 +1,5 @@ +--- +"@onflow/typedefs": minor +--- + +Add types for general stream connections & event streaming API From 47a8076519fc9887397e28e49c43d563cbb036d9 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 29 Nov 2023 13:22:46 -0800 Subject: [PATCH 36/54] enter prerelease mode & version packages --- .changeset/pre.json | 33 +++++++++++++++++++++++++++ packages/config/CHANGELOG.md | 11 +++++++++ packages/config/package.json | 10 ++++---- packages/fcl-bundle/CHANGELOG.md | 6 +++++ packages/fcl-bundle/package.json | 2 +- packages/fcl-wc/CHANGELOG.md | 12 ++++++++++ packages/fcl-wc/package.json | 12 +++++----- packages/fcl/CHANGELOG.md | 24 +++++++++++++++++++ packages/fcl/package.json | 26 ++++++++++----------- packages/rlp/CHANGELOG.md | 6 +++++ packages/rlp/package.json | 4 ++-- packages/sdk/CHANGELOG.md | 22 ++++++++++++++++++ packages/sdk/package.json | 22 +++++++++--------- packages/transport-grpc/CHANGELOG.md | 10 ++++++++ packages/transport-grpc/package.json | 14 ++++++------ packages/transport-http/CHANGELOG.md | 18 +++++++++++++++ packages/transport-http/package.json | 18 +++++++-------- packages/typedefs/CHANGELOG.md | 12 ++++++++++ packages/typedefs/package.json | 4 ++-- packages/types/CHANGELOG.md | 13 +++++++++++ packages/types/package.json | 6 ++--- packages/util-actor/CHANGELOG.md | 6 +++++ packages/util-actor/package.json | 4 ++-- packages/util-address/CHANGELOG.md | 6 +++++ packages/util-address/package.json | 6 ++--- packages/util-encode-key/CHANGELOG.md | 10 ++++++++ packages/util-encode-key/package.json | 10 ++++---- packages/util-invariant/CHANGELOG.md | 6 +++++ packages/util-invariant/package.json | 6 ++--- packages/util-logger/CHANGELOG.md | 6 +++++ packages/util-logger/package.json | 4 ++-- packages/util-semver/package.json | 2 +- packages/util-template/CHANGELOG.md | 9 ++++++++ packages/util-template/package.json | 6 ++--- packages/util-uid/CHANGELOG.md | 6 +++++ packages/util-uid/package.json | 4 ++-- 36 files changed, 296 insertions(+), 80 deletions(-) create mode 100644 .changeset/pre.json diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000..91fb8e254 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,33 @@ +{ + "mode": "pre", + "tag": "event-streaming", + "initialVersions": { + "@onflow/config": "1.2.1", + "@onflow/fcl": "1.9.0", + "@onflow/fcl-bundle": "1.4.1", + "@onflow/fcl-wc": "6.0.0", + "@onflow/protobuf": "1.2.2", + "@onflow/rlp": "1.2.1", + "@onflow/sdk": "1.3.1", + "@onflow/transport-grpc": "1.3.1", + "@onflow/transport-http": "1.9.0", + "@onflow/typedefs": "1.2.1", + "@onflow/types": "1.2.1", + "@onflow/util-actor": "1.3.1", + "@onflow/util-address": "1.2.1", + "@onflow/util-encode-key": "1.2.1", + "@onflow/util-invariant": "1.2.1", + "@onflow/util-logger": "1.3.1", + "@onflow/util-semver": "1.0.1", + "@onflow/util-template": "1.2.1", + "@onflow/util-uid": "1.2.1" + }, + "changesets": [ + "brown-dingos-taste", + "lucky-moons-smell", + "mean-bugs-cry", + "old-plants-nail", + "serious-seahorses-camp", + "sixty-turkeys-destroy" + ] +} diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index b276dd1c6..6b31b106e 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,16 @@ # @onflow/config +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-logger@1.3.2-event-streaming.0 + - @onflow/util-actor@1.3.2-event-streaming.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index db27fa9a2..e13c11a04 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/config", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Config for FCL-JS", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.11", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/estree": "^1.0.1", "@types/jest": "^29.5.4", "@typescript-eslint/eslint-plugin": "^6.5.0", @@ -39,9 +39,9 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.3.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-logger": "^1.3.1", + "@onflow/util-actor": "^1.3.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-logger": "^1.3.2-event-streaming.0", "eslint": "^8.34.0", "eslint-plugin-jsdoc": "^40.0.0" } diff --git a/packages/fcl-bundle/CHANGELOG.md b/packages/fcl-bundle/CHANGELOG.md index 14817bfbd..92a27b4b4 100644 --- a/packages/fcl-bundle/CHANGELOG.md +++ b/packages/fcl-bundle/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/fcl-bundle +## 1.4.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.4.1 ### Patch Changes diff --git a/packages/fcl-bundle/package.json b/packages/fcl-bundle/package.json index 675b22cd1..bcac3773e 100644 --- a/packages/fcl-bundle/package.json +++ b/packages/fcl-bundle/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-bundle", - "version": "1.4.1", + "version": "1.4.2-event-streaming.0", "description": "FCL Bundler Tool", "license": "Apache-2.0", "author": "Dapper Labs ", diff --git a/packages/fcl-wc/CHANGELOG.md b/packages/fcl-wc/CHANGELOG.md index 2bc191219..2d6ef95fb 100644 --- a/packages/fcl-wc/CHANGELOG.md +++ b/packages/fcl-wc/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/fcl-wc +## 7.0.0-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/fcl@1.10.0-event-streaming.0 + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-logger@1.3.2-event-streaming.0 + - @onflow/config@1.2.2-event-streaming.0 + ## 6.0.0 ### Patch Changes diff --git a/packages/fcl-wc/package.json b/packages/fcl-wc/package.json index ba2860e8d..2c55ccca6 100644 --- a/packages/fcl-wc/package.json +++ b/packages/fcl-wc/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-wc", - "version": "6.0.0", + "version": "7.0.0-event-streaming.0", "description": "WalletConnect adapter for FCL", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -25,21 +25,21 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "better-sqlite3": "^7.6.2", "jest": "^29.5.0" }, "dependencies": { "@babel/runtime": "^7.18.9", - "@onflow/config": "^1.2.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-logger": "^1.3.1", + "@onflow/config": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-logger": "^1.3.2-event-streaming.0", "@walletconnect/modal": "^2.4.7", "@walletconnect/sign-client": "^2.8.1", "@walletconnect/types": "^2.8.1", "@walletconnect/utils": "^2.8.1" }, "peerDependencies": { - "@onflow/fcl": "^1.9.0" + "@onflow/fcl": "^1.10.0-event-streaming.0" } } diff --git a/packages/fcl/CHANGELOG.md b/packages/fcl/CHANGELOG.md index fe7f8d4a0..9acbf5752 100644 --- a/packages/fcl/CHANGELOG.md +++ b/packages/fcl/CHANGELOG.md @@ -1,5 +1,29 @@ # @onflow/fcl +## 1.10.0-event-streaming.0 + +### Minor Changes + +- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion + +- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Syntax remains unchanged & can be accessed via fcl.events(). + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/types@1.3.0-event-streaming.0 + - @onflow/sdk@1.4.0-event-streaming.0 + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-template@1.2.2-event-streaming.0 + - @onflow/util-address@1.2.2-event-streaming.0 + - @onflow/util-logger@1.3.2-event-streaming.0 + - @onflow/util-actor@1.3.2-event-streaming.0 + - @onflow/util-uid@1.2.2-event-streaming.0 + - @onflow/config@1.2.2-event-streaming.0 + - @onflow/rlp@1.2.2-event-streaming.0 + ## 1.9.0 ### Minor Changes diff --git a/packages/fcl/package.json b/packages/fcl/package.json index 82fd53b0f..f22ab915f 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl", - "version": "1.9.0", + "version": "1.10.0-event-streaming.0", "description": "Flow Client Library", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -19,8 +19,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/typedefs": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/typedefs": "^1.3.0-event-streaming.0", "@types/estree": "^1.0.1", "@types/jest": "^29.5.10", "@types/node": "^18.13.0", @@ -58,18 +58,18 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.1", + "@onflow/config": "^1.2.2-event-streaming.0", "@onflow/interaction": "0.0.11", - "@onflow/rlp": "^1.2.1", - "@onflow/sdk": "^1.3.1", - "@onflow/types": "^1.2.1", - "@onflow/util-actor": "^1.3.1", - "@onflow/util-address": "^1.2.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-logger": "^1.3.1", + "@onflow/rlp": "^1.2.2-event-streaming.0", + "@onflow/sdk": "^1.4.0-event-streaming.0", + "@onflow/types": "^1.3.0-event-streaming.0", + "@onflow/util-actor": "^1.3.2-event-streaming.0", + "@onflow/util-address": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-logger": "^1.3.2-event-streaming.0", "@onflow/util-semver": "^1.0.1", - "@onflow/util-template": "^1.2.1", - "@onflow/util-uid": "^1.2.1", + "@onflow/util-template": "^1.2.2-event-streaming.0", + "@onflow/util-uid": "^1.2.2-event-streaming.0", "events": "^3.3.0", "cross-fetch": "^3.1.6", "sha3": "^2.1.4" diff --git a/packages/rlp/CHANGELOG.md b/packages/rlp/CHANGELOG.md index ec37bc556..5cd554f66 100644 --- a/packages/rlp/CHANGELOG.md +++ b/packages/rlp/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/rlp +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.2.1 ### Patch Changes diff --git a/packages/rlp/package.json b/packages/rlp/package.json index 3a4d40df9..c8607e0ea 100644 --- a/packages/rlp/package.json +++ b/packages/rlp/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/rlp", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Port of ethereumjs/rlp", "license": "MPL-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 1b1c47175..5063c89dd 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,27 @@ # @onflow/sdk +## 1.4.0-event-streaming.0 + +### Minor Changes + +- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion + +- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Can be accessed through new SDK builder sdk.subscribeEvents(...). + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/transport-http@1.10.0-event-streaming.0 + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-template@1.2.2-event-streaming.0 + - @onflow/util-address@1.2.2-event-streaming.0 + - @onflow/util-logger@1.3.2-event-streaming.0 + - @onflow/util-actor@1.3.2-event-streaming.0 + - @onflow/config@1.2.2-event-streaming.0 + - @onflow/rlp@1.2.2-event-streaming.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 22ff1f718..c00f93b29 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/sdk", - "version": "1.3.1", + "version": "1.4.0-event-streaming.0", "description": "Flow SDK", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -18,8 +18,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/typedefs": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/typedefs": "^1.3.0-event-streaming.0", "@types/uuid": "^9.0.6", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", @@ -42,14 +42,14 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.1", - "@onflow/rlp": "^1.2.1", - "@onflow/transport-http": "^1.8.1", - "@onflow/util-actor": "^1.3.1", - "@onflow/util-address": "^1.2.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-logger": "^1.3.1", - "@onflow/util-template": "^1.2.1", + "@onflow/config": "^1.2.2-event-streaming.0", + "@onflow/rlp": "^1.2.2-event-streaming.0", + "@onflow/transport-http": "^1.10.0-event-streaming.0", + "@onflow/util-actor": "^1.3.2-event-streaming.0", + "@onflow/util-address": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-logger": "^1.3.2-event-streaming.0", + "@onflow/util-template": "^1.2.2-event-streaming.0", "deepmerge": "^4.2.2", "events": "^3.3.0", "sha3": "^2.1.4", diff --git a/packages/transport-grpc/CHANGELOG.md b/packages/transport-grpc/CHANGELOG.md index 34b586a24..58c365cee 100644 --- a/packages/transport-grpc/CHANGELOG.md +++ b/packages/transport-grpc/CHANGELOG.md @@ -1,5 +1,15 @@ # @onflow/transport-grpc +## 1.3.2-event-streaming.0 + +### Patch Changes + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-template@1.2.2-event-streaming.0 + - @onflow/util-address@1.2.2-event-streaming.0 + - @onflow/rlp@1.2.2-event-streaming.0 + ## 1.3.1 ### Patch Changes diff --git a/packages/transport-grpc/package.json b/packages/transport-grpc/package.json index b6116b08f..3ae01c79f 100644 --- a/packages/transport-grpc/package.json +++ b/packages/transport-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/transport-grpc", - "version": "1.3.1", + "version": "1.3.2-event-streaming.0", "description": "Flow SDK GRPC Transport Module", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,8 +13,8 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/sdk": "^1.3.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/sdk": "^1.4.0-event-streaming.0", "jest": "^29.5.0" }, "source": "src/sdk-send-grpc.js", @@ -34,9 +34,9 @@ "@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web-node-http-transport": "^0.14.0", "@onflow/protobuf": "^1.2.2", - "@onflow/rlp": "^1.2.1", - "@onflow/util-address": "^1.2.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-template": "^1.2.1" + "@onflow/rlp": "^1.2.2-event-streaming.0", + "@onflow/util-address": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-template": "^1.2.2-event-streaming.0" } } diff --git a/packages/transport-http/CHANGELOG.md b/packages/transport-http/CHANGELOG.md index 3ac5c38b7..2e4446eb1 100644 --- a/packages/transport-http/CHANGELOG.md +++ b/packages/transport-http/CHANGELOG.md @@ -1,5 +1,23 @@ # @onflow/transport-http +## 1.10.0-event-streaming.0 + +### Minor Changes + +- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion + +- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for event streaming API interaction (subscribeEvents) + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/util-template@1.2.2-event-streaming.0 + - @onflow/util-address@1.2.2-event-streaming.0 + - @onflow/util-logger@1.3.2-event-streaming.0 + ## 1.9.0 ### Minor Changes diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index 21f7ca83d..481df6a6a 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/transport-http", - "version": "1.9.0", + "version": "1.10.0-event-streaming.0", "description": "Flow SDK HTTP Transport Module", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,10 +13,10 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/rlp": "^1.2.1", - "@onflow/sdk": "^1.3.1", - "@onflow/types": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/rlp": "^1.2.2-event-streaming.0", + "@onflow/sdk": "^1.4.0-event-streaming.0", + "@onflow/types": "^1.3.0-event-streaming.0", "jest": "^29.5.0" }, "source": "src/sdk-send-http.ts", @@ -34,10 +34,10 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-address": "^1.2.1", - "@onflow/util-invariant": "^1.2.1", - "@onflow/util-logger": "^1.3.1", - "@onflow/util-template": "^1.2.1", + "@onflow/util-address": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0", + "@onflow/util-logger": "^1.3.2-event-streaming.0", + "@onflow/util-template": "^1.2.2-event-streaming.0", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6", "events": "^3.3.0", diff --git a/packages/typedefs/CHANGELOG.md b/packages/typedefs/CHANGELOG.md index f90d27e89..698209c1d 100644 --- a/packages/typedefs/CHANGELOG.md +++ b/packages/typedefs/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/typedefs +## 1.3.0-event-streaming.0 + +### Minor Changes + +- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion + +- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add types for general stream connections & event streaming API + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.2.1 ### Patch Changes diff --git a/packages/typedefs/package.json b/packages/typedefs/package.json index 219335d7f..f1d12a05b 100644 --- a/packages/typedefs/package.json +++ b/packages/typedefs/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/typedefs", - "version": "1.2.1", + "version": "1.3.0-event-streaming.0", "description": "Flow JS Type Defs", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,7 +13,7 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/node": "^18.13.0", "eslint": "^8.33.0", "eslint-plugin-jsdoc": "^39.7.5", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 115ea0d70..dfefb5f6f 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,18 @@ # @onflow/types +## 1.3.0-event-streaming.0 + +### Minor Changes + +- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-logger@1.3.2-event-streaming.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index e5bf16ac7..94b707708 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/types", - "version": "1.2.1", + "version": "1.3.0-event-streaming.0", "description": "Utilities to transform javascript values into Cadence understandable values", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -37,6 +37,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.1" + "@onflow/util-logger": "^1.3.2-event-streaming.0" } } diff --git a/packages/util-actor/CHANGELOG.md b/packages/util-actor/CHANGELOG.md index c0d9fe6ee..3d472dd9b 100644 --- a/packages/util-actor/CHANGELOG.md +++ b/packages/util-actor/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-actor +## 1.3.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.3.1 ### Patch Changes diff --git a/packages/util-actor/package.json b/packages/util-actor/package.json index d9437610e..482e77f94 100644 --- a/packages/util-actor/package.json +++ b/packages/util-actor/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-actor", - "version": "1.3.1", + "version": "1.3.2-event-streaming.0", "description": "A mechanism for forcing order/transitions of scoped async state", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-address/CHANGELOG.md b/packages/util-address/CHANGELOG.md index 23469dc27..856301332 100644 --- a/packages/util-address/CHANGELOG.md +++ b/packages/util-address/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-address +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.2.1 ### Patch Changes diff --git a/packages/util-address/package.json b/packages/util-address/package.json index 926768309..f5ee0beca 100644 --- a/packages/util-address/package.json +++ b/packages/util-address/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-address", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Flow JS SDK Util -- Address", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/types": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/types": "^1.3.0-event-streaming.0", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", diff --git a/packages/util-encode-key/CHANGELOG.md b/packages/util-encode-key/CHANGELOG.md index baf279afc..303fbfad6 100644 --- a/packages/util-encode-key/CHANGELOG.md +++ b/packages/util-encode-key/CHANGELOG.md @@ -1,5 +1,15 @@ # @onflow/util-encode-key +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-invariant@1.2.2-event-streaming.0 + - @onflow/rlp@1.2.2-event-streaming.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/util-encode-key/package.json b/packages/util-encode-key/package.json index 491a01b5c..ae7e57e03 100644 --- a/packages/util-encode-key/package.json +++ b/packages/util-encode-key/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-encode-key", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Flow JS SDK Util -- Encode Key", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/types": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/types": "^1.3.0-event-streaming.0", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", @@ -40,7 +40,7 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/rlp": "^1.2.1", - "@onflow/util-invariant": "^1.2.1" + "@onflow/rlp": "^1.2.2-event-streaming.0", + "@onflow/util-invariant": "^1.2.2-event-streaming.0" } } diff --git a/packages/util-invariant/CHANGELOG.md b/packages/util-invariant/CHANGELOG.md index 131c95626..bb4e9b744 100644 --- a/packages/util-invariant/CHANGELOG.md +++ b/packages/util-invariant/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-invariant +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.2.1 ### Patch Changes diff --git a/packages/util-invariant/package.json b/packages/util-invariant/package.json index 4810aeca3..ef19e4196 100644 --- a/packages/util-invariant/package.json +++ b/packages/util-invariant/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-invariant", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Flow JS SDK Util -- Invariant", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", - "@onflow/types": "^1.2.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/types": "^1.3.0-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-logger/CHANGELOG.md b/packages/util-logger/CHANGELOG.md index 16c4e0844..c14ad76b6 100644 --- a/packages/util-logger/CHANGELOG.md +++ b/packages/util-logger/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-logger +## 1.3.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.3.1 ### Patch Changes diff --git a/packages/util-logger/package.json b/packages/util-logger/package.json index 80f772616..591ca8e2f 100644 --- a/packages/util-logger/package.json +++ b/packages/util-logger/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-logger", - "version": "1.3.1", + "version": "1.3.2-event-streaming.0", "description": "Logger for FCL-JS", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-semver/package.json b/packages/util-semver/package.json index b2063894f..a1bbf2801 100644 --- a/packages/util-semver/package.json +++ b/packages/util-semver/package.json @@ -13,7 +13,7 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "jest": "^29.5.0" }, "dependencies": { diff --git a/packages/util-template/CHANGELOG.md b/packages/util-template/CHANGELOG.md index 68db6ef39..31bf954cd 100644 --- a/packages/util-template/CHANGELOG.md +++ b/packages/util-template/CHANGELOG.md @@ -1,5 +1,14 @@ # @onflow/util-template +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + +- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: + - @onflow/util-logger@1.3.2-event-streaming.0 + ## 1.2.1 ### Patch Changes diff --git a/packages/util-template/package.json b/packages/util-template/package.json index 850c4fa54..5465ff240 100644 --- a/packages/util-template/package.json +++ b/packages/util-template/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-template", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Template Literal used for Cadence Interop", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -36,6 +36,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.1" + "@onflow/util-logger": "^1.3.2-event-streaming.0" } } diff --git a/packages/util-uid/CHANGELOG.md b/packages/util-uid/CHANGELOG.md index 0ce30d7e4..1a382cb73 100644 --- a/packages/util-uid/CHANGELOG.md +++ b/packages/util-uid/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-uid +## 1.2.2-event-streaming.0 + +### Patch Changes + +- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated + ## 1.2.1 ### Patch Changes diff --git a/packages/util-uid/package.json b/packages/util-uid/package.json index 24afb8ee4..72e304bb7 100644 --- a/packages/util-uid/package.json +++ b/packages/util-uid/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-uid", - "version": "1.2.1", + "version": "1.2.2-event-streaming.0", "description": "Utilities to generate Unique Identifiers", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.1", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", From 14ce7f53984e3766d112c7b8007299c572b80c30 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 4 Dec 2023 13:15:40 -0800 Subject: [PATCH 37/54] Add export to FCL --- .changeset/thirty-donuts-worry.md | 22 ++++++++++++++++++++++ packages/fcl/src/events/index.test.ts | 2 +- packages/fcl/src/shared-exports.ts | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/thirty-donuts-worry.md diff --git a/.changeset/thirty-donuts-worry.md b/.changeset/thirty-donuts-worry.md new file mode 100644 index 000000000..0a8a19da9 --- /dev/null +++ b/.changeset/thirty-donuts-worry.md @@ -0,0 +1,22 @@ +--- +"@onflow/util-encode-key": patch +"@onflow/transport-grpc": patch +"@onflow/transport-http": patch +"@onflow/util-invariant": patch +"@onflow/util-template": patch +"@onflow/util-address": patch +"@onflow/util-logger": patch +"@onflow/util-semver": patch +"@onflow/fcl-bundle": patch +"@onflow/util-actor": patch +"@onflow/typedefs": patch +"@onflow/util-uid": patch +"@onflow/config": patch +"@onflow/fcl-wc": patch +"@onflow/types": patch +"@onflow/fcl": patch +"@onflow/rlp": patch +"@onflow/sdk": patch +--- + +Add subscribeEvents export to FCL & fix build diff --git a/packages/fcl/src/events/index.test.ts b/packages/fcl/src/events/index.test.ts index 070e64361..b3c0c1b11 100644 --- a/packages/fcl/src/events/index.test.ts +++ b/packages/fcl/src/events/index.test.ts @@ -40,7 +40,7 @@ describe("events", () => { sdk.subscribeEvents({eventTypes: ["A"]}), ]) }) - + test("should work with empty args", () => { events().subscribe(() => {}) expect(sendSpy).toHaveBeenCalledWith([sdk.subscribeEvents({})]) diff --git a/packages/fcl/src/shared-exports.ts b/packages/fcl/src/shared-exports.ts index 761319de1..8322f5b86 100644 --- a/packages/fcl/src/shared-exports.ts +++ b/packages/fcl/src/shared-exports.ts @@ -57,6 +57,7 @@ export {getTransactionStatus} from "@onflow/sdk" export {getTransaction} from "@onflow/sdk" export {getNetworkParameters} from "@onflow/sdk" export {authorizations, authorization} from "@onflow/sdk" +export {subscribeEvents} from "@onflow/sdk" export {args, arg} from "@onflow/sdk" export {proposer} from "@onflow/sdk" export {payer} from "@onflow/sdk" From 10e9f20fb778ff2f72c7e3b46f1b52d0cf36c86f Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 4 Dec 2023 13:15:52 -0800 Subject: [PATCH 38/54] Pre-release event-streaming tagged package --- .changeset/pre.json | 5 ++-- packages/config/CHANGELOG.md | 11 ++++++++ packages/config/package.json | 10 +++---- packages/fcl-bundle/CHANGELOG.md | 6 +++++ packages/fcl-bundle/package.json | 2 +- packages/fcl-wc/CHANGELOG.md | 12 +++++++++ packages/fcl-wc/package.json | 12 ++++----- packages/fcl/CHANGELOG.md | 19 +++++++++++++ packages/fcl/package.json | 28 ++++++++++---------- packages/rlp/CHANGELOG.md | 6 +++++ packages/rlp/package.json | 4 +-- packages/sdk/CHANGELOG.md | 16 +++++++++++ packages/sdk/package.json | 22 +++++++-------- packages/transport-grpc/CHANGELOG.md | 12 +++++++++ packages/transport-grpc/README.md | 8 +++--- packages/transport-grpc/package.json | 14 +++++----- packages/transport-grpc/src/sdk-send-grpc.js | 10 ++++++- packages/transport-http/CHANGELOG.md | 12 +++++++++ packages/transport-http/package.json | 18 ++++++------- packages/typedefs/CHANGELOG.md | 6 +++++ packages/typedefs/package.json | 4 +-- packages/typedefs/src/interaction.ts | 6 ++--- packages/types/CHANGELOG.md | 9 +++++++ packages/types/package.json | 6 ++--- packages/util-actor/CHANGELOG.md | 6 +++++ packages/util-actor/package.json | 4 +-- packages/util-address/CHANGELOG.md | 6 +++++ packages/util-address/package.json | 6 ++--- packages/util-encode-key/CHANGELOG.md | 10 +++++++ packages/util-encode-key/package.json | 10 +++---- packages/util-invariant/CHANGELOG.md | 6 +++++ packages/util-invariant/package.json | 6 ++--- packages/util-logger/CHANGELOG.md | 6 +++++ packages/util-logger/package.json | 4 +-- packages/util-semver/CHANGELOG.md | 6 +++++ packages/util-semver/package.json | 4 +-- packages/util-template/CHANGELOG.md | 9 +++++++ packages/util-template/package.json | 6 ++--- packages/util-uid/CHANGELOG.md | 6 +++++ packages/util-uid/package.json | 4 +-- 40 files changed, 264 insertions(+), 93 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 91fb8e254..5352d4686 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -5,7 +5,7 @@ "@onflow/config": "1.2.1", "@onflow/fcl": "1.9.0", "@onflow/fcl-bundle": "1.4.1", - "@onflow/fcl-wc": "6.0.0", + "@onflow/fcl-wc": "5.0.1", "@onflow/protobuf": "1.2.2", "@onflow/rlp": "1.2.1", "@onflow/sdk": "1.3.1", @@ -28,6 +28,7 @@ "mean-bugs-cry", "old-plants-nail", "serious-seahorses-camp", - "sixty-turkeys-destroy" + "sixty-turkeys-destroy", + "thirty-donuts-worry" ] } diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 6b31b106e..6230a6e13 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,16 @@ # @onflow/config +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-logger@1.3.2-event-streaming.1 + - @onflow/util-actor@1.3.2-event-streaming.1 + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index e13c11a04..88b44c5a3 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/config", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Config for FCL-JS", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.11", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/estree": "^1.0.1", "@types/jest": "^29.5.4", "@typescript-eslint/eslint-plugin": "^6.5.0", @@ -39,9 +39,9 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.3.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-logger": "^1.3.2-event-streaming.0", + "@onflow/util-actor": "^1.3.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-logger": "^1.3.2-event-streaming.1", "eslint": "^8.34.0", "eslint-plugin-jsdoc": "^40.0.0" } diff --git a/packages/fcl-bundle/CHANGELOG.md b/packages/fcl-bundle/CHANGELOG.md index 92a27b4b4..0c34b3f99 100644 --- a/packages/fcl-bundle/CHANGELOG.md +++ b/packages/fcl-bundle/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/fcl-bundle +## 1.4.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.4.2-event-streaming.0 ### Patch Changes diff --git a/packages/fcl-bundle/package.json b/packages/fcl-bundle/package.json index bcac3773e..a6fad06e8 100644 --- a/packages/fcl-bundle/package.json +++ b/packages/fcl-bundle/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-bundle", - "version": "1.4.2-event-streaming.0", + "version": "1.4.2-event-streaming.1", "description": "FCL Bundler Tool", "license": "Apache-2.0", "author": "Dapper Labs ", diff --git a/packages/fcl-wc/CHANGELOG.md b/packages/fcl-wc/CHANGELOG.md index 2d6ef95fb..71d37b271 100644 --- a/packages/fcl-wc/CHANGELOG.md +++ b/packages/fcl-wc/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/fcl-wc +## 5.0.2-event-streaming.0 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-logger@1.3.2-event-streaming.1 + - @onflow/config@1.2.2-event-streaming.1 + - @onflow/fcl@1.10.0-event-streaming.1 + ## 7.0.0-event-streaming.0 ### Patch Changes diff --git a/packages/fcl-wc/package.json b/packages/fcl-wc/package.json index 2c55ccca6..152311c01 100644 --- a/packages/fcl-wc/package.json +++ b/packages/fcl-wc/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl-wc", - "version": "7.0.0-event-streaming.0", + "version": "5.0.1", "description": "WalletConnect adapter for FCL", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -25,21 +25,21 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "better-sqlite3": "^7.6.2", "jest": "^29.5.0" }, "dependencies": { "@babel/runtime": "^7.18.9", - "@onflow/config": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-logger": "^1.3.2-event-streaming.0", + "@onflow/config": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-logger": "^1.3.2-event-streaming.1", "@walletconnect/modal": "^2.4.7", "@walletconnect/sign-client": "^2.8.1", "@walletconnect/types": "^2.8.1", "@walletconnect/utils": "^2.8.1" }, "peerDependencies": { - "@onflow/fcl": "^1.10.0-event-streaming.0" + "@onflow/fcl": "^1.10.0-event-streaming.1" } } diff --git a/packages/fcl/CHANGELOG.md b/packages/fcl/CHANGELOG.md index 9acbf5752..bea837e20 100644 --- a/packages/fcl/CHANGELOG.md +++ b/packages/fcl/CHANGELOG.md @@ -1,5 +1,24 @@ # @onflow/fcl +## 1.10.0-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-template@1.2.2-event-streaming.1 + - @onflow/util-address@1.2.2-event-streaming.1 + - @onflow/util-logger@1.3.2-event-streaming.1 + - @onflow/util-semver@1.0.2-event-streaming.0 + - @onflow/util-actor@1.3.2-event-streaming.1 + - @onflow/util-uid@1.2.2-event-streaming.1 + - @onflow/config@1.2.2-event-streaming.1 + - @onflow/types@1.3.0-event-streaming.1 + - @onflow/rlp@1.2.2-event-streaming.1 + - @onflow/sdk@1.4.0-event-streaming.1 + ## 1.10.0-event-streaming.0 ### Minor Changes diff --git a/packages/fcl/package.json b/packages/fcl/package.json index f22ab915f..fa80adce2 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/fcl", - "version": "1.10.0-event-streaming.0", + "version": "1.10.0-event-streaming.1", "description": "Flow Client Library", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -19,8 +19,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/typedefs": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/typedefs": "^1.3.0-event-streaming.1", "@types/estree": "^1.0.1", "@types/jest": "^29.5.10", "@types/node": "^18.13.0", @@ -58,18 +58,18 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.2-event-streaming.0", + "@onflow/config": "^1.2.2-event-streaming.1", "@onflow/interaction": "0.0.11", - "@onflow/rlp": "^1.2.2-event-streaming.0", - "@onflow/sdk": "^1.4.0-event-streaming.0", - "@onflow/types": "^1.3.0-event-streaming.0", - "@onflow/util-actor": "^1.3.2-event-streaming.0", - "@onflow/util-address": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-logger": "^1.3.2-event-streaming.0", - "@onflow/util-semver": "^1.0.1", - "@onflow/util-template": "^1.2.2-event-streaming.0", - "@onflow/util-uid": "^1.2.2-event-streaming.0", + "@onflow/rlp": "^1.2.2-event-streaming.1", + "@onflow/sdk": "^1.4.0-event-streaming.1", + "@onflow/types": "^1.3.0-event-streaming.1", + "@onflow/util-actor": "^1.3.2-event-streaming.1", + "@onflow/util-address": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-logger": "^1.3.2-event-streaming.1", + "@onflow/util-semver": "^1.0.2-event-streaming.0", + "@onflow/util-template": "^1.2.2-event-streaming.1", + "@onflow/util-uid": "^1.2.2-event-streaming.1", "events": "^3.3.0", "cross-fetch": "^3.1.6", "sha3": "^2.1.4" diff --git a/packages/rlp/CHANGELOG.md b/packages/rlp/CHANGELOG.md index 5cd554f66..7c805f1c4 100644 --- a/packages/rlp/CHANGELOG.md +++ b/packages/rlp/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/rlp +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/rlp/package.json b/packages/rlp/package.json index c8607e0ea..a86609c34 100644 --- a/packages/rlp/package.json +++ b/packages/rlp/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/rlp", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Port of ethereumjs/rlp", "license": "MPL-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 5063c89dd..d49115693 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,21 @@ # @onflow/sdk +## 1.4.0-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/transport-http@1.10.0-event-streaming.1 + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-template@1.2.2-event-streaming.1 + - @onflow/util-address@1.2.2-event-streaming.1 + - @onflow/util-logger@1.3.2-event-streaming.1 + - @onflow/util-actor@1.3.2-event-streaming.1 + - @onflow/config@1.2.2-event-streaming.1 + - @onflow/rlp@1.2.2-event-streaming.1 + ## 1.4.0-event-streaming.0 ### Minor Changes diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c00f93b29..b2fd70650 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/sdk", - "version": "1.4.0-event-streaming.0", + "version": "1.4.0-event-streaming.1", "description": "Flow SDK", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -18,8 +18,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/typedefs": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/typedefs": "^1.3.0-event-streaming.1", "@types/uuid": "^9.0.6", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", @@ -42,14 +42,14 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.2-event-streaming.0", - "@onflow/rlp": "^1.2.2-event-streaming.0", - "@onflow/transport-http": "^1.10.0-event-streaming.0", - "@onflow/util-actor": "^1.3.2-event-streaming.0", - "@onflow/util-address": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-logger": "^1.3.2-event-streaming.0", - "@onflow/util-template": "^1.2.2-event-streaming.0", + "@onflow/config": "^1.2.2-event-streaming.1", + "@onflow/rlp": "^1.2.2-event-streaming.1", + "@onflow/transport-http": "^1.10.0-event-streaming.1", + "@onflow/util-actor": "^1.3.2-event-streaming.1", + "@onflow/util-address": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-logger": "^1.3.2-event-streaming.1", + "@onflow/util-template": "^1.2.2-event-streaming.1", "deepmerge": "^4.2.2", "events": "^3.3.0", "sha3": "^2.1.4", diff --git a/packages/transport-grpc/CHANGELOG.md b/packages/transport-grpc/CHANGELOG.md index 58c365cee..e818608f8 100644 --- a/packages/transport-grpc/CHANGELOG.md +++ b/packages/transport-grpc/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/transport-grpc +## 1.3.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-template@1.2.2-event-streaming.1 + - @onflow/util-address@1.2.2-event-streaming.1 + - @onflow/rlp@1.2.2-event-streaming.1 + ## 1.3.2-event-streaming.0 ### Patch Changes diff --git a/packages/transport-grpc/README.md b/packages/transport-grpc/README.md index d11a10713..f3607bcd9 100644 --- a/packages/transport-grpc/README.md +++ b/packages/transport-grpc/README.md @@ -3,13 +3,11 @@ title: Transport GRPC description: Sends an interaction to an access node via the GRPC Rest API and returns a response. --- -## Status +# Deprecated - DO NOT USE -- **Last Updated:** Jan 13th 2022 -- **Stable:** Yes -- **Risk of Breaking Change:** Medium +This package is deprecated and will will no receive updates for new features. Please migrate your projects to use the [HTTP Transport] package (`@onflow/transport-http`) instead. -This package is working and in active development, breaking changes may happen. +Latest versions of FCL/JS-SDK will automatically use the HTTP Transport as long as the `sdk.transport` configuration key is not set. ## Usage diff --git a/packages/transport-grpc/package.json b/packages/transport-grpc/package.json index 3ae01c79f..b1d405440 100644 --- a/packages/transport-grpc/package.json +++ b/packages/transport-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/transport-grpc", - "version": "1.3.2-event-streaming.0", + "version": "1.3.2-event-streaming.1", "description": "Flow SDK GRPC Transport Module", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,8 +13,8 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/sdk": "^1.4.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/sdk": "^1.4.0-event-streaming.1", "jest": "^29.5.0" }, "source": "src/sdk-send-grpc.js", @@ -34,9 +34,9 @@ "@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web-node-http-transport": "^0.14.0", "@onflow/protobuf": "^1.2.2", - "@onflow/rlp": "^1.2.2-event-streaming.0", - "@onflow/util-address": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-template": "^1.2.2-event-streaming.0" + "@onflow/rlp": "^1.2.2-event-streaming.1", + "@onflow/util-address": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-template": "^1.2.2-event-streaming.1" } } diff --git a/packages/transport-grpc/src/sdk-send-grpc.js b/packages/transport-grpc/src/sdk-send-grpc.js index f9fd67338..e40ce0374 100644 --- a/packages/transport-grpc/src/sdk-send-grpc.js +++ b/packages/transport-grpc/src/sdk-send-grpc.js @@ -1,5 +1,13 @@ export {sendExecuteScript} from "./send-execute-script.js" export {sendGetAccount} from "./send-get-account.js" +// @deprecated - will be removed in future versions +import * as logger from "@onflow/util-logger" +logger.log.deprecate({ + pkg: "transport-grpc", + msg: `@onflow/transport-grpc is deprecated, please use @onflow/transport-http instead or simply remove the \`sdk.transport\` config option within the FCL/JS-SDK.`, + transition: "https://github.com/onflow/fcl-js/tree/master/packages/transport-grpc/README.md" +}) + export {sendGetBlockHeader} from "./send-get-block-header.js" export {sendGetBlock} from "./send-get-block.js" export {sendGetCollection} from "./send-get-collection.js" @@ -9,4 +17,4 @@ export {sendGetTransactionStatus} from "./send-get-transaction-status.js" export {sendPing} from "./send-ping.js" export {sendTransaction} from "./send-transaction.js" export {sendGetNetworkParameters} from "./send-get-network-parameters.js" -export {send} from "./send-grpc.js" +export {send} from "./send-grpc.js" \ No newline at end of file diff --git a/packages/transport-http/CHANGELOG.md b/packages/transport-http/CHANGELOG.md index 2e4446eb1..d42f164d8 100644 --- a/packages/transport-http/CHANGELOG.md +++ b/packages/transport-http/CHANGELOG.md @@ -1,5 +1,17 @@ # @onflow/transport-http +## 1.10.0-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/util-template@1.2.2-event-streaming.1 + - @onflow/util-address@1.2.2-event-streaming.1 + - @onflow/util-logger@1.3.2-event-streaming.1 + ## 1.10.0-event-streaming.0 ### Minor Changes diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index 481df6a6a..aded8e0f1 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/transport-http", - "version": "1.10.0-event-streaming.0", + "version": "1.10.0-event-streaming.1", "description": "Flow SDK HTTP Transport Module", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,10 +13,10 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/rlp": "^1.2.2-event-streaming.0", - "@onflow/sdk": "^1.4.0-event-streaming.0", - "@onflow/types": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/rlp": "^1.2.2-event-streaming.1", + "@onflow/sdk": "^1.4.0-event-streaming.1", + "@onflow/types": "^1.3.0-event-streaming.1", "jest": "^29.5.0" }, "source": "src/sdk-send-http.ts", @@ -34,10 +34,10 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-address": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0", - "@onflow/util-logger": "^1.3.2-event-streaming.0", - "@onflow/util-template": "^1.2.2-event-streaming.0", + "@onflow/util-address": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1", + "@onflow/util-logger": "^1.3.2-event-streaming.1", + "@onflow/util-template": "^1.2.2-event-streaming.1", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6", "events": "^3.3.0", diff --git a/packages/typedefs/CHANGELOG.md b/packages/typedefs/CHANGELOG.md index 698209c1d..4bd8ed869 100644 --- a/packages/typedefs/CHANGELOG.md +++ b/packages/typedefs/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/typedefs +## 1.3.0-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.3.0-event-streaming.0 ### Minor Changes diff --git a/packages/typedefs/package.json b/packages/typedefs/package.json index f1d12a05b..e821e0208 100644 --- a/packages/typedefs/package.json +++ b/packages/typedefs/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/typedefs", - "version": "1.3.0-event-streaming.0", + "version": "1.3.0-event-streaming.1", "description": "Flow JS Type Defs", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -13,7 +13,7 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/node": "^18.13.0", "eslint": "^8.33.0", "eslint-plugin-jsdoc": "^39.7.5", diff --git a/packages/typedefs/src/interaction.ts b/packages/typedefs/src/interaction.ts index df4d53b4c..db0f86ce8 100644 --- a/packages/typedefs/src/interaction.ts +++ b/packages/typedefs/src/interaction.ts @@ -90,9 +90,9 @@ export interface Interaction { id: string | null }, subscribeEvents: { - eventTypes: string | string[] | null - addresses: string | string[] | null - contracts: string | string[] | null + eventTypes: string[] | null + addresses: string[] | null + contracts: string[] | null startBlockId: string | null startHeight: number | null heartbeatInterval: number | null diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index dfefb5f6f..350849c78 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,14 @@ # @onflow/types +## 1.3.0-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-logger@1.3.2-event-streaming.1 + ## 1.3.0-event-streaming.0 ### Minor Changes diff --git a/packages/types/package.json b/packages/types/package.json index 94b707708..430dc2642 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/types", - "version": "1.3.0-event-streaming.0", + "version": "1.3.0-event-streaming.1", "description": "Utilities to transform javascript values into Cadence understandable values", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -37,6 +37,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.2-event-streaming.0" + "@onflow/util-logger": "^1.3.2-event-streaming.1" } } diff --git a/packages/util-actor/CHANGELOG.md b/packages/util-actor/CHANGELOG.md index 3d472dd9b..e7eb897c6 100644 --- a/packages/util-actor/CHANGELOG.md +++ b/packages/util-actor/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-actor +## 1.3.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.3.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-actor/package.json b/packages/util-actor/package.json index 482e77f94..ebdb0e1c5 100644 --- a/packages/util-actor/package.json +++ b/packages/util-actor/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-actor", - "version": "1.3.2-event-streaming.0", + "version": "1.3.2-event-streaming.1", "description": "A mechanism for forcing order/transitions of scoped async state", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-address/CHANGELOG.md b/packages/util-address/CHANGELOG.md index 856301332..518fc8e89 100644 --- a/packages/util-address/CHANGELOG.md +++ b/packages/util-address/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-address +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-address/package.json b/packages/util-address/package.json index f5ee0beca..6501ea2d0 100644 --- a/packages/util-address/package.json +++ b/packages/util-address/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-address", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Flow JS SDK Util -- Address", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/types": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/types": "^1.3.0-event-streaming.1", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", diff --git a/packages/util-encode-key/CHANGELOG.md b/packages/util-encode-key/CHANGELOG.md index 303fbfad6..9cc29ab8e 100644 --- a/packages/util-encode-key/CHANGELOG.md +++ b/packages/util-encode-key/CHANGELOG.md @@ -1,5 +1,15 @@ # @onflow/util-encode-key +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-invariant@1.2.2-event-streaming.1 + - @onflow/rlp@1.2.2-event-streaming.1 + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-encode-key/package.json b/packages/util-encode-key/package.json index ae7e57e03..159fc943e 100644 --- a/packages/util-encode-key/package.json +++ b/packages/util-encode-key/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-encode-key", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Flow JS SDK Util -- Encode Key", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/types": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/types": "^1.3.0-event-streaming.1", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", @@ -40,7 +40,7 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/rlp": "^1.2.2-event-streaming.0", - "@onflow/util-invariant": "^1.2.2-event-streaming.0" + "@onflow/rlp": "^1.2.2-event-streaming.1", + "@onflow/util-invariant": "^1.2.2-event-streaming.1" } } diff --git a/packages/util-invariant/CHANGELOG.md b/packages/util-invariant/CHANGELOG.md index bb4e9b744..7e844b73b 100644 --- a/packages/util-invariant/CHANGELOG.md +++ b/packages/util-invariant/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-invariant +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-invariant/package.json b/packages/util-invariant/package.json index ef19e4196..3b5bc4eb6 100644 --- a/packages/util-invariant/package.json +++ b/packages/util-invariant/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-invariant", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Flow JS SDK Util -- Invariant", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", - "@onflow/types": "^1.3.0-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/types": "^1.3.0-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-logger/CHANGELOG.md b/packages/util-logger/CHANGELOG.md index c14ad76b6..f2ef2e46a 100644 --- a/packages/util-logger/CHANGELOG.md +++ b/packages/util-logger/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-logger +## 1.3.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.3.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-logger/package.json b/packages/util-logger/package.json index 591ca8e2f..d151a2e11 100644 --- a/packages/util-logger/package.json +++ b/packages/util-logger/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-logger", - "version": "1.3.2-event-streaming.0", + "version": "1.3.2-event-streaming.1", "description": "Logger for FCL-JS", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-semver/CHANGELOG.md b/packages/util-semver/CHANGELOG.md index fccc2c02f..788172fb7 100644 --- a/packages/util-semver/CHANGELOG.md +++ b/packages/util-semver/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-semver +## 1.0.2-event-streaming.0 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.0.1 ### Patch Changes diff --git a/packages/util-semver/package.json b/packages/util-semver/package.json index a1bbf2801..4f040cf13 100644 --- a/packages/util-semver/package.json +++ b/packages/util-semver/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-semver", - "version": "1.0.1", + "version": "1.0.2-event-streaming.0", "description": "A lightweight semver implementation for use in FCL", "main": "dist/index.js", "module": "dist/index.module.js", @@ -13,7 +13,7 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "jest": "^29.5.0" }, "dependencies": { diff --git a/packages/util-template/CHANGELOG.md b/packages/util-template/CHANGELOG.md index 31bf954cd..880bc3196 100644 --- a/packages/util-template/CHANGELOG.md +++ b/packages/util-template/CHANGELOG.md @@ -1,5 +1,14 @@ # @onflow/util-template +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + +- Updated dependencies []: + - @onflow/util-logger@1.3.2-event-streaming.1 + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-template/package.json b/packages/util-template/package.json index 5465ff240..b8422da88 100644 --- a/packages/util-template/package.json +++ b/packages/util-template/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-template", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Template Literal used for Cadence Interop", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", @@ -36,6 +36,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.2-event-streaming.0" + "@onflow/util-logger": "^1.3.2-event-streaming.1" } } diff --git a/packages/util-uid/CHANGELOG.md b/packages/util-uid/CHANGELOG.md index 1a382cb73..952f5dcee 100644 --- a/packages/util-uid/CHANGELOG.md +++ b/packages/util-uid/CHANGELOG.md @@ -1,5 +1,11 @@ # @onflow/util-uid +## 1.2.2-event-streaming.1 + +### Patch Changes + +- Add subscribeEvents export to FCL & fix build + ## 1.2.2-event-streaming.0 ### Patch Changes diff --git a/packages/util-uid/package.json b/packages/util-uid/package.json index 72e304bb7..9e8b5de67 100644 --- a/packages/util-uid/package.json +++ b/packages/util-uid/package.json @@ -1,6 +1,6 @@ { "name": "@onflow/util-uid", - "version": "1.2.2-event-streaming.0", + "version": "1.2.2-event-streaming.1", "description": "Utilities to generate Unique Identifiers", "license": "Apache-2.0", "author": "Dapper Labs ", @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.0", + "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", From b2e43f1477dd50f8acf2397b9966b5cf49c6cfd4 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 4 Dec 2023 13:42:21 -0800 Subject: [PATCH 39/54] Add error for subscribe events in transport-grpc --- .changeset/dull-oranges-tell.md | 5 +++++ packages/transport-grpc/src/send-grpc.js | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/dull-oranges-tell.md diff --git a/.changeset/dull-oranges-tell.md b/.changeset/dull-oranges-tell.md new file mode 100644 index 000000000..5caeb5436 --- /dev/null +++ b/.changeset/dull-oranges-tell.md @@ -0,0 +1,5 @@ +--- +"@onflow/transport-grpc": patch +--- + +Deprecate @onflow/transport-grpc & add "not implemented" error for subscribeEvents diff --git a/packages/transport-grpc/src/send-grpc.js b/packages/transport-grpc/src/send-grpc.js index ce26a5acf..63a3332ef 100644 --- a/packages/transport-grpc/src/send-grpc.js +++ b/packages/transport-grpc/src/send-grpc.js @@ -44,6 +44,11 @@ export const send = async (ix, context = {}, opts = {}) => { return opts.sendPing ? opts.sendPing(ix, context, opts) : sendPing(ix, context, opts) case context.ix.isGetNetworkParameters(ix): return opts.sendGetNetworkParameters ? opts.sendGetNetworkParameters(ix, context, opts) : sendGetNetworkParameters(ix, context, opts) + case context.ix.isSubscribeEvents?.(ix): + if (opts.sendSubscribeEvents) + return opts.sendSubscribeEvents(ix, context, opts) + else + throw new Error(`SDK Send Error: subscribeEvents is not supported by this transport.`) default: return ix } From 62d9c28b7632395d93960b25a58dc0162dfc0326 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 4 Dec 2023 13:44:54 -0800 Subject: [PATCH 40/54] remove un-needed changeset --- .changeset/pre.json | 5 ++--- .changeset/thirty-donuts-worry.md | 22 ---------------------- 2 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 .changeset/thirty-donuts-worry.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 5352d4686..14d068540 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,5 +1,5 @@ { - "mode": "pre", + "mode": "exit", "tag": "event-streaming", "initialVersions": { "@onflow/config": "1.2.1", @@ -28,7 +28,6 @@ "mean-bugs-cry", "old-plants-nail", "serious-seahorses-camp", - "sixty-turkeys-destroy", - "thirty-donuts-worry" + "sixty-turkeys-destroy" ] } diff --git a/.changeset/thirty-donuts-worry.md b/.changeset/thirty-donuts-worry.md deleted file mode 100644 index 0a8a19da9..000000000 --- a/.changeset/thirty-donuts-worry.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"@onflow/util-encode-key": patch -"@onflow/transport-grpc": patch -"@onflow/transport-http": patch -"@onflow/util-invariant": patch -"@onflow/util-template": patch -"@onflow/util-address": patch -"@onflow/util-logger": patch -"@onflow/util-semver": patch -"@onflow/fcl-bundle": patch -"@onflow/util-actor": patch -"@onflow/typedefs": patch -"@onflow/util-uid": patch -"@onflow/config": patch -"@onflow/fcl-wc": patch -"@onflow/types": patch -"@onflow/fcl": patch -"@onflow/rlp": patch -"@onflow/sdk": patch ---- - -Add subscribeEvents export to FCL & fix build From 5b391496ae19e552aaf300ff63c0e774bb43462f Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 09:54:23 -0800 Subject: [PATCH 41/54] Add legacy events subscription for GRPC --- packages/fcl/src/events/index.ts | 40 +++++++++-- packages/fcl/src/events/legacy-events.js | 91 ++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 packages/fcl/src/events/legacy-events.js diff --git a/packages/fcl/src/events/index.ts b/packages/fcl/src/events/index.ts index 3831d7e89..90909a34a 100644 --- a/packages/fcl/src/events/index.ts +++ b/packages/fcl/src/events/index.ts @@ -1,5 +1,6 @@ import {send, decode, subscribeEvents} from "@onflow/sdk" import {Event, EventFilter, EventStream} from "@onflow/typedefs" +import {events as legacyEvents} from "./legacy-events" /** * @description - Subscribe to events @@ -26,6 +27,19 @@ export function events(filterOrType?: EventFilter | string) { subscribeEvents(filter), ]).then(decode) + // If the subscribe fails, fallback to legacy events + const legacySubscriptionPromise = streamPromise.then(() => null).catch((e) => { + // Only fallback to legacy events if the error is specifcally about the unsupported feature + if(e.message !== "SDK Send Error: subscribeEvents is not supported by this transport.") { + throw e + } + + if (typeof filterOrType !== "string") { + throw new Error("GRPC fcl.events fallback only supports string (type) filters") + } + return legacyEvents(filterOrType).subscribe(callback) + }) + // Subscribe to the stream using the callback function onEvents(data: Event[]) { data.forEach(event => callback(event, null)) @@ -33,13 +47,29 @@ export function events(filterOrType?: EventFilter | string) { function onError(error: Error) { callback(null, error) } - streamPromise.then(stream => - stream.on("events", onEvents).on("error", onError) - ) + // If using legacy events, don't subscribe to the stream + legacySubscriptionPromise.then(legacySubscription => { + if (!legacySubscription) { + streamPromise.then(stream => + stream.on("events", onEvents).on("error", onError) + ).catch((error) => { + streamPromise.then(stream => stream.close()) + onError(error) + }) + } + }) + + // Unsubscribe will call terminate the legacy subscription or close the stream return () => { - streamPromise.then(stream => stream.close()) + legacySubscriptionPromise.then(legacySubscription => { + if (legacySubscription) { + legacySubscription() + } else { + streamPromise.then(stream => stream.close()) + } + }) } }, } -} +} \ No newline at end of file diff --git a/packages/fcl/src/events/legacy-events.js b/packages/fcl/src/events/legacy-events.js new file mode 100644 index 000000000..d47976430 --- /dev/null +++ b/packages/fcl/src/events/legacy-events.js @@ -0,0 +1,91 @@ +import {spawn, subscriber, SUBSCRIBE, UNSUBSCRIBE} from "@onflow/util-actor" +import { + config, + block, + getEventsAtBlockHeightRange, + send, + decode, +} from "@onflow/sdk" + +const RATE = 10000 +const UPDATED = "UPDATED" +const TICK = "TICK" +const HIGH_WATER_MARK = "hwm" + +const scheduleTick = async ctx => { + return setTimeout( + () => ctx.sendSelf(TICK), + await config().get("fcl.eventPollRate", RATE) + ) +} + +const HANDLERS = { + [TICK]: async ctx => { + if (!ctx.hasSubs()) return + let hwm = ctx.get(HIGH_WATER_MARK) + if (hwm == null) { + ctx.put(HIGH_WATER_MARK, await block()) + ctx.put(TICK, await scheduleTick(ctx)) + } else { + let next = await block() + ctx.put(HIGH_WATER_MARK, next) + if (hwm.height < next.height) { + const data = await send([ + getEventsAtBlockHeightRange(ctx.self(), hwm.height + 1, next.height), + ]).then(decode) + for (let d of data) ctx.broadcast(UPDATED, d.data) + } + ctx.put(TICK, await scheduleTick(ctx)) + } + }, + [SUBSCRIBE]: async (ctx, letter) => { + if (!ctx.hasSubs()) { + ctx.put(TICK, await scheduleTick(ctx)) + } + ctx.subscribe(letter.from) + }, + [UNSUBSCRIBE]: (ctx, letter) => { + ctx.unsubscribe(letter.from) + if (!ctx.hasSubs()) { + clearTimeout(ctx.get(TICK)) + ctx.delete(TICK) + ctx.delete(HIGH_WATER_MARK) + } + }, +} + +const spawnEvents = key => spawn(HANDLERS, key) + +/** + * @typedef {import("@onflow/typedefs").Event} Event + */ + +/** + * @typedef {object} SubscribeObject + * @property {Function} subscribe - The subscribe function. + */ + +/** + * @callback SubscriptionCallback + * @returns {Event} + */ + +/** + * @description - Subscribe to events + * @param {string} key - A valid event name + * @returns {SubscribeObject} + * + * @example + * import * as fcl from "@onflow/fcl" + * fcl.events(eventName).subscribe((event) => console.log(event)) + */ +export function events(key) { + return { + /** + * @description - Subscribe to events + * @param {Function} callback - The callback function + * @returns {SubscriptionCallback} + */ + subscribe: callback => subscriber(key, spawnEvents, callback), + } +} From dc1a4a61813554f76a574066caebc8a6b64e7674 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 10:44:42 -0800 Subject: [PATCH 42/54] Remove pre changeset --- .changeset/pre.json | 33 --------------------------- packages/config/package.json | 6 ++--- packages/fcl-wc/package.json | 6 ++--- packages/fcl/package.json | 22 +++++++++--------- packages/sdk/package.json | 16 ++++++------- packages/transport-grpc/package.json | 8 +++---- packages/transport-http/package.json | 8 +++---- packages/types/package.json | 2 +- packages/util-encode-key/package.json | 4 ++-- packages/util-template/package.json | 2 +- 10 files changed, 37 insertions(+), 70 deletions(-) delete mode 100644 .changeset/pre.json diff --git a/.changeset/pre.json b/.changeset/pre.json deleted file mode 100644 index 14d068540..000000000 --- a/.changeset/pre.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "mode": "exit", - "tag": "event-streaming", - "initialVersions": { - "@onflow/config": "1.2.1", - "@onflow/fcl": "1.9.0", - "@onflow/fcl-bundle": "1.4.1", - "@onflow/fcl-wc": "5.0.1", - "@onflow/protobuf": "1.2.2", - "@onflow/rlp": "1.2.1", - "@onflow/sdk": "1.3.1", - "@onflow/transport-grpc": "1.3.1", - "@onflow/transport-http": "1.9.0", - "@onflow/typedefs": "1.2.1", - "@onflow/types": "1.2.1", - "@onflow/util-actor": "1.3.1", - "@onflow/util-address": "1.2.1", - "@onflow/util-encode-key": "1.2.1", - "@onflow/util-invariant": "1.2.1", - "@onflow/util-logger": "1.3.1", - "@onflow/util-semver": "1.0.1", - "@onflow/util-template": "1.2.1", - "@onflow/util-uid": "1.2.1" - }, - "changesets": [ - "brown-dingos-taste", - "lucky-moons-smell", - "mean-bugs-cry", - "old-plants-nail", - "serious-seahorses-camp", - "sixty-turkeys-destroy" - ] -} diff --git a/packages/config/package.json b/packages/config/package.json index 88b44c5a3..70968bd85 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -39,9 +39,9 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-actor": "^1.3.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-logger": "^1.3.2-event-streaming.1", + "@onflow/util-actor": "^1.3.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-logger": "^1.3.1", "eslint": "^8.34.0", "eslint-plugin-jsdoc": "^40.0.0" } diff --git a/packages/fcl-wc/package.json b/packages/fcl-wc/package.json index 152311c01..a34915f9a 100644 --- a/packages/fcl-wc/package.json +++ b/packages/fcl-wc/package.json @@ -31,9 +31,9 @@ }, "dependencies": { "@babel/runtime": "^7.18.9", - "@onflow/config": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-logger": "^1.3.2-event-streaming.1", + "@onflow/config": "^1.2.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-logger": "^1.3.1", "@walletconnect/modal": "^2.4.7", "@walletconnect/sign-client": "^2.8.1", "@walletconnect/types": "^2.8.1", diff --git a/packages/fcl/package.json b/packages/fcl/package.json index fa80adce2..9f3129f74 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -58,18 +58,18 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.2-event-streaming.1", + "@onflow/config": "^1.2.1", "@onflow/interaction": "0.0.11", - "@onflow/rlp": "^1.2.2-event-streaming.1", - "@onflow/sdk": "^1.4.0-event-streaming.1", - "@onflow/types": "^1.3.0-event-streaming.1", - "@onflow/util-actor": "^1.3.2-event-streaming.1", - "@onflow/util-address": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-logger": "^1.3.2-event-streaming.1", - "@onflow/util-semver": "^1.0.2-event-streaming.0", - "@onflow/util-template": "^1.2.2-event-streaming.1", - "@onflow/util-uid": "^1.2.2-event-streaming.1", + "@onflow/rlp": "^1.2.1", + "@onflow/sdk": "^1.3.1", + "@onflow/types": "^1.2.1", + "@onflow/util-actor": "^1.3.1", + "@onflow/util-address": "^1.2.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-logger": "^1.3.1", + "@onflow/util-semver": "^1.0.1", + "@onflow/util-template": "^1.2.1", + "@onflow/util-uid": "^1.2.1", "events": "^3.3.0", "cross-fetch": "^3.1.6", "sha3": "^2.1.4" diff --git a/packages/sdk/package.json b/packages/sdk/package.json index b2fd70650..c5760fdb2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -42,14 +42,14 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/config": "^1.2.2-event-streaming.1", - "@onflow/rlp": "^1.2.2-event-streaming.1", - "@onflow/transport-http": "^1.10.0-event-streaming.1", - "@onflow/util-actor": "^1.3.2-event-streaming.1", - "@onflow/util-address": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-logger": "^1.3.2-event-streaming.1", - "@onflow/util-template": "^1.2.2-event-streaming.1", + "@onflow/config": "^1.2.1", + "@onflow/rlp": "^1.2.1", + "@onflow/transport-http": "^1.9.0", + "@onflow/util-actor": "^1.3.1", + "@onflow/util-address": "^1.2.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-logger": "^1.3.1", + "@onflow/util-template": "^1.2.1", "deepmerge": "^4.2.2", "events": "^3.3.0", "sha3": "^2.1.4", diff --git a/packages/transport-grpc/package.json b/packages/transport-grpc/package.json index b1d405440..0a29514ba 100644 --- a/packages/transport-grpc/package.json +++ b/packages/transport-grpc/package.json @@ -34,9 +34,9 @@ "@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web-node-http-transport": "^0.14.0", "@onflow/protobuf": "^1.2.2", - "@onflow/rlp": "^1.2.2-event-streaming.1", - "@onflow/util-address": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-template": "^1.2.2-event-streaming.1" + "@onflow/rlp": "^1.2.1", + "@onflow/util-address": "^1.2.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-template": "^1.2.1" } } diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index aded8e0f1..df26b1797 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -34,10 +34,10 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-address": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1", - "@onflow/util-logger": "^1.3.2-event-streaming.1", - "@onflow/util-template": "^1.2.2-event-streaming.1", + "@onflow/util-address": "^1.2.1", + "@onflow/util-invariant": "^1.2.1", + "@onflow/util-logger": "^1.3.1", + "@onflow/util-template": "^1.2.1", "abort-controller": "^3.0.0", "cross-fetch": "^3.1.6", "events": "^3.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index 430dc2642..83630e2a8 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -37,6 +37,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.2-event-streaming.1" + "@onflow/util-logger": "^1.3.1" } } diff --git a/packages/util-encode-key/package.json b/packages/util-encode-key/package.json index 159fc943e..c85142a4d 100644 --- a/packages/util-encode-key/package.json +++ b/packages/util-encode-key/package.json @@ -40,7 +40,7 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/rlp": "^1.2.2-event-streaming.1", - "@onflow/util-invariant": "^1.2.2-event-streaming.1" + "@onflow/rlp": "^1.2.1", + "@onflow/util-invariant": "^1.2.1" } } diff --git a/packages/util-template/package.json b/packages/util-template/package.json index b8422da88..165d976b4 100644 --- a/packages/util-template/package.json +++ b/packages/util-template/package.json @@ -36,6 +36,6 @@ }, "dependencies": { "@babel/runtime": "^7.18.6", - "@onflow/util-logger": "^1.3.2-event-streaming.1" + "@onflow/util-logger": "^1.3.1" } } From 59cfc4dc7a260249a9502b86fba362cbfbc53da5 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 10:50:28 -0800 Subject: [PATCH 43/54] remove pre versions --- packages/config/package.json | 2 +- packages/fcl-wc/package.json | 4 ++-- packages/fcl/package.json | 4 ++-- packages/rlp/package.json | 2 +- packages/sdk/package.json | 4 ++-- packages/transport-grpc/package.json | 4 ++-- packages/transport-http/package.json | 8 ++++---- packages/typedefs/package.json | 2 +- packages/types/package.json | 2 +- packages/util-actor/package.json | 2 +- packages/util-address/package.json | 4 ++-- packages/util-encode-key/package.json | 4 ++-- packages/util-invariant/package.json | 4 ++-- packages/util-logger/package.json | 2 +- packages/util-semver/package.json | 2 +- packages/util-template/package.json | 2 +- packages/util-uid/package.json | 2 +- 17 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/config/package.json b/packages/config/package.json index 70968bd85..80ce594ab 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.11", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/estree": "^1.0.1", "@types/jest": "^29.5.4", "@typescript-eslint/eslint-plugin": "^6.5.0", diff --git a/packages/fcl-wc/package.json b/packages/fcl-wc/package.json index a34915f9a..e1e50b5b0 100644 --- a/packages/fcl-wc/package.json +++ b/packages/fcl-wc/package.json @@ -25,7 +25,7 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "better-sqlite3": "^7.6.2", "jest": "^29.5.0" }, @@ -40,6 +40,6 @@ "@walletconnect/utils": "^2.8.1" }, "peerDependencies": { - "@onflow/fcl": "^1.10.0-event-streaming.1" + "@onflow/fcl": "^1.9.0" } } diff --git a/packages/fcl/package.json b/packages/fcl/package.json index 9f3129f74..c5100b4e7 100644 --- a/packages/fcl/package.json +++ b/packages/fcl/package.json @@ -19,8 +19,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/typedefs": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/typedefs": "^1.2.1", "@types/estree": "^1.0.1", "@types/jest": "^29.5.10", "@types/node": "^18.13.0", diff --git a/packages/rlp/package.json b/packages/rlp/package.json index a86609c34..f47b6b926 100644 --- a/packages/rlp/package.json +++ b/packages/rlp/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c5760fdb2..e87875bcf 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -18,8 +18,8 @@ } }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/typedefs": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/typedefs": "^1.2.1", "@types/uuid": "^9.0.6", "eslint": "^8.35.0", "eslint-plugin-jsdoc": "^40.0.1", diff --git a/packages/transport-grpc/package.json b/packages/transport-grpc/package.json index 0a29514ba..aedca561d 100644 --- a/packages/transport-grpc/package.json +++ b/packages/transport-grpc/package.json @@ -13,8 +13,8 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/sdk": "^1.4.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/sdk": "^1.3.1", "jest": "^29.5.0" }, "source": "src/sdk-send-grpc.js", diff --git a/packages/transport-http/package.json b/packages/transport-http/package.json index df26b1797..283037925 100644 --- a/packages/transport-http/package.json +++ b/packages/transport-http/package.json @@ -13,10 +13,10 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/rlp": "^1.2.2-event-streaming.1", - "@onflow/sdk": "^1.4.0-event-streaming.1", - "@onflow/types": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/rlp": "^1.2.1", + "@onflow/sdk": "^1.3.1", + "@onflow/types": "^1.2.1", "jest": "^29.5.0" }, "source": "src/sdk-send-http.ts", diff --git a/packages/typedefs/package.json b/packages/typedefs/package.json index e821e0208..374c78ffe 100644 --- a/packages/typedefs/package.json +++ b/packages/typedefs/package.json @@ -13,7 +13,7 @@ "url": "https://github.com/onflow/flow-js-sdk/issues" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/node": "^18.13.0", "eslint": "^8.33.0", "eslint-plugin-jsdoc": "^39.7.5", diff --git a/packages/types/package.json b/packages/types/package.json index 83630e2a8..302be30ad 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-actor/package.json b/packages/util-actor/package.json index ebdb0e1c5..e542c4fdc 100644 --- a/packages/util-actor/package.json +++ b/packages/util-actor/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-address/package.json b/packages/util-address/package.json index 6501ea2d0..4f1d41beb 100644 --- a/packages/util-address/package.json +++ b/packages/util-address/package.json @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/types": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/types": "^1.2.1", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", diff --git a/packages/util-encode-key/package.json b/packages/util-encode-key/package.json index c85142a4d..ffea41c6c 100644 --- a/packages/util-encode-key/package.json +++ b/packages/util-encode-key/package.json @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/types": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/types": "^1.2.1", "@types/jest": "^29.5.3", "@types/node": "^18.13.0", "@typescript-eslint/eslint-plugin": "^6.4.0", diff --git a/packages/util-invariant/package.json b/packages/util-invariant/package.json index 3b5bc4eb6..7aae1b26d 100644 --- a/packages/util-invariant/package.json +++ b/packages/util-invariant/package.json @@ -14,8 +14,8 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", - "@onflow/types": "^1.3.0-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", + "@onflow/types": "^1.2.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-logger/package.json b/packages/util-logger/package.json index d151a2e11..4fbdb2266 100644 --- a/packages/util-logger/package.json +++ b/packages/util-logger/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-semver/package.json b/packages/util-semver/package.json index 4f040cf13..78672717e 100644 --- a/packages/util-semver/package.json +++ b/packages/util-semver/package.json @@ -13,7 +13,7 @@ "start": "fcl-bundle --watch" }, "devDependencies": { - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "jest": "^29.5.0" }, "dependencies": { diff --git a/packages/util-template/package.json b/packages/util-template/package.json index 165d976b4..335e3e3a4 100644 --- a/packages/util-template/package.json +++ b/packages/util-template/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", diff --git a/packages/util-uid/package.json b/packages/util-uid/package.json index 9e8b5de67..43c0b8882 100644 --- a/packages/util-uid/package.json +++ b/packages/util-uid/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@babel/preset-typescript": "^7.22.5", - "@onflow/fcl-bundle": "^1.4.2-event-streaming.1", + "@onflow/fcl-bundle": "^1.4.1", "@types/jest": "^29.5.3", "@typescript-eslint/eslint-plugin": "^6.4.0", "@typescript-eslint/parser": "^6.4.0", From dd0ce4cf1fe8e0988df85bb7dff31219b84bdcae Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 11:26:46 -0800 Subject: [PATCH 44/54] remove pre.json --- .changeset/pre.json | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .changeset/pre.json diff --git a/.changeset/pre.json b/.changeset/pre.json deleted file mode 100644 index 14d068540..000000000 --- a/.changeset/pre.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "mode": "exit", - "tag": "event-streaming", - "initialVersions": { - "@onflow/config": "1.2.1", - "@onflow/fcl": "1.9.0", - "@onflow/fcl-bundle": "1.4.1", - "@onflow/fcl-wc": "5.0.1", - "@onflow/protobuf": "1.2.2", - "@onflow/rlp": "1.2.1", - "@onflow/sdk": "1.3.1", - "@onflow/transport-grpc": "1.3.1", - "@onflow/transport-http": "1.9.0", - "@onflow/typedefs": "1.2.1", - "@onflow/types": "1.2.1", - "@onflow/util-actor": "1.3.1", - "@onflow/util-address": "1.2.1", - "@onflow/util-encode-key": "1.2.1", - "@onflow/util-invariant": "1.2.1", - "@onflow/util-logger": "1.3.1", - "@onflow/util-semver": "1.0.1", - "@onflow/util-template": "1.2.1", - "@onflow/util-uid": "1.2.1" - }, - "changesets": [ - "brown-dingos-taste", - "lucky-moons-smell", - "mean-bugs-cry", - "old-plants-nail", - "serious-seahorses-camp", - "sixty-turkeys-destroy" - ] -} From c26225fb756b0fa9a9131c350abcaac4814d28ee Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 11:27:57 -0800 Subject: [PATCH 45/54] Revert changelogs --- packages/config/CHANGELOG.md | 22 -------------- packages/fcl-bundle/CHANGELOG.md | 12 -------- packages/fcl-core/CHANGELOG.md | 43 --------------------------- packages/fcl-wc/CHANGELOG.md | 24 --------------- packages/rlp/CHANGELOG.md | 12 -------- packages/sdk/CHANGELOG.md | 38 ----------------------- packages/transport-grpc/CHANGELOG.md | 22 -------------- packages/transport-http/CHANGELOG.md | 30 ------------------- packages/typedefs/CHANGELOG.md | 18 ----------- packages/types/CHANGELOG.md | 22 -------------- packages/util-actor/CHANGELOG.md | 12 -------- packages/util-address/CHANGELOG.md | 12 -------- packages/util-encode-key/CHANGELOG.md | 20 ------------- packages/util-invariant/CHANGELOG.md | 12 -------- packages/util-logger/CHANGELOG.md | 12 -------- packages/util-semver/CHANGELOG.md | 6 ---- packages/util-template/CHANGELOG.md | 18 ----------- packages/util-uid/CHANGELOG.md | 12 -------- 18 files changed, 347 deletions(-) diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 6230a6e13..b276dd1c6 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,27 +1,5 @@ # @onflow/config -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-logger@1.3.2-event-streaming.1 - - @onflow/util-actor@1.3.2-event-streaming.1 - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-logger@1.3.2-event-streaming.0 - - @onflow/util-actor@1.3.2-event-streaming.0 - ## 1.2.1 ### Patch Changes diff --git a/packages/fcl-bundle/CHANGELOG.md b/packages/fcl-bundle/CHANGELOG.md index 0c34b3f99..14817bfbd 100644 --- a/packages/fcl-bundle/CHANGELOG.md +++ b/packages/fcl-bundle/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/fcl-bundle -## 1.4.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.4.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.4.1 ### Patch Changes diff --git a/packages/fcl-core/CHANGELOG.md b/packages/fcl-core/CHANGELOG.md index bea837e20..fe7f8d4a0 100644 --- a/packages/fcl-core/CHANGELOG.md +++ b/packages/fcl-core/CHANGELOG.md @@ -1,48 +1,5 @@ # @onflow/fcl -## 1.10.0-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-template@1.2.2-event-streaming.1 - - @onflow/util-address@1.2.2-event-streaming.1 - - @onflow/util-logger@1.3.2-event-streaming.1 - - @onflow/util-semver@1.0.2-event-streaming.0 - - @onflow/util-actor@1.3.2-event-streaming.1 - - @onflow/util-uid@1.2.2-event-streaming.1 - - @onflow/config@1.2.2-event-streaming.1 - - @onflow/types@1.3.0-event-streaming.1 - - @onflow/rlp@1.2.2-event-streaming.1 - - @onflow/sdk@1.4.0-event-streaming.1 - -## 1.10.0-event-streaming.0 - -### Minor Changes - -- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion - -- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Syntax remains unchanged & can be accessed via fcl.events(). - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/types@1.3.0-event-streaming.0 - - @onflow/sdk@1.4.0-event-streaming.0 - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-template@1.2.2-event-streaming.0 - - @onflow/util-address@1.2.2-event-streaming.0 - - @onflow/util-logger@1.3.2-event-streaming.0 - - @onflow/util-actor@1.3.2-event-streaming.0 - - @onflow/util-uid@1.2.2-event-streaming.0 - - @onflow/config@1.2.2-event-streaming.0 - - @onflow/rlp@1.2.2-event-streaming.0 - ## 1.9.0 ### Minor Changes diff --git a/packages/fcl-wc/CHANGELOG.md b/packages/fcl-wc/CHANGELOG.md index 71d37b271..2bc191219 100644 --- a/packages/fcl-wc/CHANGELOG.md +++ b/packages/fcl-wc/CHANGELOG.md @@ -1,29 +1,5 @@ # @onflow/fcl-wc -## 5.0.2-event-streaming.0 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-logger@1.3.2-event-streaming.1 - - @onflow/config@1.2.2-event-streaming.1 - - @onflow/fcl@1.10.0-event-streaming.1 - -## 7.0.0-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/fcl@1.10.0-event-streaming.0 - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-logger@1.3.2-event-streaming.0 - - @onflow/config@1.2.2-event-streaming.0 - ## 6.0.0 ### Patch Changes diff --git a/packages/rlp/CHANGELOG.md b/packages/rlp/CHANGELOG.md index 7c805f1c4..ec37bc556 100644 --- a/packages/rlp/CHANGELOG.md +++ b/packages/rlp/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/rlp -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.2.1 ### Patch Changes diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index d49115693..1b1c47175 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,43 +1,5 @@ # @onflow/sdk -## 1.4.0-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/transport-http@1.10.0-event-streaming.1 - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-template@1.2.2-event-streaming.1 - - @onflow/util-address@1.2.2-event-streaming.1 - - @onflow/util-logger@1.3.2-event-streaming.1 - - @onflow/util-actor@1.3.2-event-streaming.1 - - @onflow/config@1.2.2-event-streaming.1 - - @onflow/rlp@1.2.2-event-streaming.1 - -## 1.4.0-event-streaming.0 - -### Minor Changes - -- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion - -- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for new event streaming API https://github.com/onflow/flips/blob/4152912f8ec39515eb1c4dddbc6605c6ebe70966/protocol/20230309-accessnode-event-streaming-api.md. Can be accessed through new SDK builder sdk.subscribeEvents(...). - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259), [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/transport-http@1.10.0-event-streaming.0 - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-template@1.2.2-event-streaming.0 - - @onflow/util-address@1.2.2-event-streaming.0 - - @onflow/util-logger@1.3.2-event-streaming.0 - - @onflow/util-actor@1.3.2-event-streaming.0 - - @onflow/config@1.2.2-event-streaming.0 - - @onflow/rlp@1.2.2-event-streaming.0 - ## 1.3.1 ### Patch Changes diff --git a/packages/transport-grpc/CHANGELOG.md b/packages/transport-grpc/CHANGELOG.md index e818608f8..34b586a24 100644 --- a/packages/transport-grpc/CHANGELOG.md +++ b/packages/transport-grpc/CHANGELOG.md @@ -1,27 +1,5 @@ # @onflow/transport-grpc -## 1.3.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-template@1.2.2-event-streaming.1 - - @onflow/util-address@1.2.2-event-streaming.1 - - @onflow/rlp@1.2.2-event-streaming.1 - -## 1.3.2-event-streaming.0 - -### Patch Changes - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-template@1.2.2-event-streaming.0 - - @onflow/util-address@1.2.2-event-streaming.0 - - @onflow/rlp@1.2.2-event-streaming.0 - ## 1.3.1 ### Patch Changes diff --git a/packages/transport-http/CHANGELOG.md b/packages/transport-http/CHANGELOG.md index d42f164d8..3ac5c38b7 100644 --- a/packages/transport-http/CHANGELOG.md +++ b/packages/transport-http/CHANGELOG.md @@ -1,35 +1,5 @@ # @onflow/transport-http -## 1.10.0-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/util-template@1.2.2-event-streaming.1 - - @onflow/util-address@1.2.2-event-streaming.1 - - @onflow/util-logger@1.3.2-event-streaming.1 - -## 1.10.0-event-streaming.0 - -### Minor Changes - -- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion - -- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add support for event streaming API interaction (subscribeEvents) - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/util-template@1.2.2-event-streaming.0 - - @onflow/util-address@1.2.2-event-streaming.0 - - @onflow/util-logger@1.3.2-event-streaming.0 - ## 1.9.0 ### Minor Changes diff --git a/packages/typedefs/CHANGELOG.md b/packages/typedefs/CHANGELOG.md index 4bd8ed869..f90d27e89 100644 --- a/packages/typedefs/CHANGELOG.md +++ b/packages/typedefs/CHANGELOG.md @@ -1,23 +1,5 @@ # @onflow/typedefs -## 1.3.0-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.3.0-event-streaming.0 - -### Minor Changes - -- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion - -- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`59debea5`](https://github.com/onflow/fcl-js/commit/59debea55209adab984d2c0a139aeedf8f09186b) Thanks [@jribbink](https://github.com/jribbink)! - Add types for general stream connections & event streaming API - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.2.1 ### Patch Changes diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 350849c78..115ea0d70 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,27 +1,5 @@ # @onflow/types -## 1.3.0-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-logger@1.3.2-event-streaming.1 - -## 1.3.0-event-streaming.0 - -### Minor Changes - -- [#1802](https://github.com/onflow/fcl-js/pull/1802) [`699303cf`](https://github.com/onflow/fcl-js/commit/699303cfd5e0545267632c9236f8c91833ce1259) Thanks [@nialexsan](https://github.com/nialexsan)! - TS conversion - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-logger@1.3.2-event-streaming.0 - ## 1.2.1 ### Patch Changes diff --git a/packages/util-actor/CHANGELOG.md b/packages/util-actor/CHANGELOG.md index e7eb897c6..c0d9fe6ee 100644 --- a/packages/util-actor/CHANGELOG.md +++ b/packages/util-actor/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/util-actor -## 1.3.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.3.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.3.1 ### Patch Changes diff --git a/packages/util-address/CHANGELOG.md b/packages/util-address/CHANGELOG.md index 518fc8e89..23469dc27 100644 --- a/packages/util-address/CHANGELOG.md +++ b/packages/util-address/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/util-address -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.2.1 ### Patch Changes diff --git a/packages/util-encode-key/CHANGELOG.md b/packages/util-encode-key/CHANGELOG.md index 9cc29ab8e..baf279afc 100644 --- a/packages/util-encode-key/CHANGELOG.md +++ b/packages/util-encode-key/CHANGELOG.md @@ -1,25 +1,5 @@ # @onflow/util-encode-key -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-invariant@1.2.2-event-streaming.1 - - @onflow/rlp@1.2.2-event-streaming.1 - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-invariant@1.2.2-event-streaming.0 - - @onflow/rlp@1.2.2-event-streaming.0 - ## 1.2.1 ### Patch Changes diff --git a/packages/util-invariant/CHANGELOG.md b/packages/util-invariant/CHANGELOG.md index 7e844b73b..131c95626 100644 --- a/packages/util-invariant/CHANGELOG.md +++ b/packages/util-invariant/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/util-invariant -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.2.1 ### Patch Changes diff --git a/packages/util-logger/CHANGELOG.md b/packages/util-logger/CHANGELOG.md index f2ef2e46a..16c4e0844 100644 --- a/packages/util-logger/CHANGELOG.md +++ b/packages/util-logger/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/util-logger -## 1.3.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.3.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.3.1 ### Patch Changes diff --git a/packages/util-semver/CHANGELOG.md b/packages/util-semver/CHANGELOG.md index 788172fb7..fccc2c02f 100644 --- a/packages/util-semver/CHANGELOG.md +++ b/packages/util-semver/CHANGELOG.md @@ -1,11 +1,5 @@ # @onflow/util-semver -## 1.0.2-event-streaming.0 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - ## 1.0.1 ### Patch Changes diff --git a/packages/util-template/CHANGELOG.md b/packages/util-template/CHANGELOG.md index 880bc3196..68db6ef39 100644 --- a/packages/util-template/CHANGELOG.md +++ b/packages/util-template/CHANGELOG.md @@ -1,23 +1,5 @@ # @onflow/util-template -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -- Updated dependencies []: - - @onflow/util-logger@1.3.2-event-streaming.1 - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - -- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: - - @onflow/util-logger@1.3.2-event-streaming.0 - ## 1.2.1 ### Patch Changes diff --git a/packages/util-uid/CHANGELOG.md b/packages/util-uid/CHANGELOG.md index 952f5dcee..0ce30d7e4 100644 --- a/packages/util-uid/CHANGELOG.md +++ b/packages/util-uid/CHANGELOG.md @@ -1,17 +1,5 @@ # @onflow/util-uid -## 1.2.2-event-streaming.1 - -### Patch Changes - -- Add subscribeEvents export to FCL & fix build - -## 1.2.2-event-streaming.0 - -### Patch Changes - -- [#1814](https://github.com/onflow/fcl-js/pull/1814) [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4) Thanks [@jribbink](https://github.com/jribbink)! - Fix type declarations not fully being generated - ## 1.2.1 ### Patch Changes From 04caf11b0572846a566656de1b13db944a74ceda Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 11:51:11 -0800 Subject: [PATCH 46/54] fix typescript test --- packages/fcl-core/.babelrc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/fcl-core/.babelrc b/packages/fcl-core/.babelrc index 67fc2886b..927807e21 100644 --- a/packages/fcl-core/.babelrc +++ b/packages/fcl-core/.babelrc @@ -1,7 +1,11 @@ { "presets": [ [ - "@babel/preset-env" - ] - ] + "@babel/preset-env", + { + "useBuiltIns": false + } + ], + "@babel/preset-typescript" + ] } From ee1f6f4bd21105c2abb2cf33494443898827d83b Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 11:52:25 -0800 Subject: [PATCH 47/54] Add react native subscribeEvents export --- packages/fcl-react-native/src/fcl-react-native.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fcl-react-native/src/fcl-react-native.ts b/packages/fcl-react-native/src/fcl-react-native.ts index bbc92510d..0b7ea0586 100644 --- a/packages/fcl-react-native/src/fcl-react-native.ts +++ b/packages/fcl-react-native/src/fcl-react-native.ts @@ -58,6 +58,7 @@ export { param, validator, invariant, + subscribeEvents, } from "@onflow/fcl-core" import {getMutate, getCurrentUser, initServiceRegistry, setIsReactNative} from "@onflow/fcl-core" From a4923591950f2479ae3b7537fb4108ec86a7a9ec Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 12:06:19 -0800 Subject: [PATCH 48/54] upgrade node to 18 --- .github/workflows/integrate.yml | 2 +- .github/workflows/release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml index 71542e1d2..cafe53565 100644 --- a/.github/workflows/integrate.yml +++ b/.github/workflows/integrate.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x - run: make ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index baf6c7020..2872686ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,11 +18,11 @@ jobs: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - # gallium is the v16 lts - - name: Setup Node.js lts/gallium + # v18 node + - name: Setup Node.js lts/hydrogen uses: actions/setup-node@v2 with: - node-version: lts/gallium + node-version: lts/hydrogen - name: Install Dependencies run: npm i From 94118ba3fc586bd96b413c5e03b50d04682b9393 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 12:35:52 -0800 Subject: [PATCH 49/54] Add nx peer dependencies patch --- package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.json b/package.json index 273bd872a..b8088bc22 100644 --- a/package.json +++ b/package.json @@ -26,5 +26,11 @@ "prettier": "^2.6.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "17.0.3", + "@nx/nx-darwin-x64": "17.0.3", + "@nx/nx-linux-x64-gnu": "17.0.3", + "@nx/nx-win32-x64-msvc": "17.0.3" } } From d87bdc48ba12a9563e6ed7f84b31f2bb0176e6fa Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 12:37:53 -0800 Subject: [PATCH 50/54] Revert "upgrade node to 18" This reverts commit a4923591950f2479ae3b7537fb4108ec86a7a9ec. --- .github/workflows/integrate.yml | 2 +- .github/workflows/release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml index cafe53565..71542e1d2 100644 --- a/.github/workflows/integrate.yml +++ b/.github/workflows/integrate.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 18.x + node-version: 16.x - run: make ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2872686ca..baf6c7020 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,11 +18,11 @@ jobs: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - # v18 node - - name: Setup Node.js lts/hydrogen + # gallium is the v16 lts + - name: Setup Node.js lts/gallium uses: actions/setup-node@v2 with: - node-version: lts/hydrogen + node-version: lts/gallium - name: Install Dependencies run: npm i From f61a87860e77b80d724cc98d423ced4db5b9c3e5 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 12:06:19 -0800 Subject: [PATCH 51/54] upgrade node to 18 --- .github/workflows/integrate.yml | 2 +- .github/workflows/release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml index 71542e1d2..cafe53565 100644 --- a/.github/workflows/integrate.yml +++ b/.github/workflows/integrate.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 18.x - run: make ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index baf6c7020..2872686ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,11 +18,11 @@ jobs: # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits fetch-depth: 0 - # gallium is the v16 lts - - name: Setup Node.js lts/gallium + # v18 node + - name: Setup Node.js lts/hydrogen uses: actions/setup-node@v2 with: - node-version: lts/gallium + node-version: lts/hydrogen - name: Install Dependencies run: npm i From 3a059fbcabd7e1129981686758e2b8ddb2b90bb4 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 13:20:16 -0800 Subject: [PATCH 52/54] remove opt deps --- package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/package.json b/package.json index b8088bc22..273bd872a 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,5 @@ "prettier": "^2.6.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "17.0.3", - "@nx/nx-darwin-x64": "17.0.3", - "@nx/nx-linux-x64-gnu": "17.0.3", - "@nx/nx-win32-x64-msvc": "17.0.3" } } From 830b0a414a3d1247e0a655b136c699cb3adbc645 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 13:26:17 -0800 Subject: [PATCH 53/54] add opt dependencies patch --- package-lock.json | 52 ++++++++++++++++++++++++++++++++++++++++++++++- package.json | 6 ++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 5f057740b..88388de64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,12 @@ "prettier": "^2.6.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "^17.0.0", + "@nx/nx-darwin-x64": "^17.0.0", + "@nx/nx-linux-x64-gnu": "^17.0.0", + "@nx/nx-win32-x64-msvc": "^17.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -6021,7 +6027,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -6031,6 +6036,51 @@ "node": ">= 10" } }, + "node_modules/@nx/nx-darwin-x64": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-17.1.3.tgz", + "integrity": "sha512-kh76ZjqkLeQUIAfTa9G/DFFf+e1sZ5ipDzk7zFGhZ2k68PoQoFdsFOO3C513JmuEdavspts6Hkifsqh61TaE+A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-17.1.3.tgz", + "integrity": "sha512-pJS994sa5PBPFak93RydTB9KdEmiVb3rgiSB7PDBegphERbzHEB77B7G8M5TZ62dGlMdplIEKmdhY5XNqeAf9A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-17.1.3.tgz", + "integrity": "sha512-eTuTpBHFvA5NFJh/iosmqCL4JOAjDrwXLSMgfKrZKjiApHMG1T/5Hb+PrsNpt+WnGp94ur7c4Dtx4xD5vlpAEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@octokit/auth-token": { "version": "3.0.4", "dev": true, diff --git a/package.json b/package.json index 273bd872a..6a7163fe7 100644 --- a/package.json +++ b/package.json @@ -26,5 +26,11 @@ "prettier": "^2.6.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "^17.0.0", + "@nx/nx-darwin-x64": "^17.0.0", + "@nx/nx-linux-x64-gnu": "^17.0.0", + "@nx/nx-win32-x64-msvc": "^17.0.0" } } From 1940a64aaef7fc303f9ac185707ff57b40b63ed6 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Wed, 6 Dec 2023 13:53:56 -0800 Subject: [PATCH 54/54] fix constructResponse --- packages/transport-http/src/connect-subscribe-events.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/transport-http/src/connect-subscribe-events.ts b/packages/transport-http/src/connect-subscribe-events.ts index acb9e5a2d..a602ce487 100644 --- a/packages/transport-http/src/connect-subscribe-events.ts +++ b/packages/transport-http/src/connect-subscribe-events.ts @@ -10,7 +10,7 @@ type RawSubscribeEventsStream = StreamConnection<{ } }> -function constructData(ix: any, context: any, data: any) { +function constructData(ix: Interaction, context: any, data: any) { const response = context.response() response.tag = ix.tag @@ -38,7 +38,7 @@ function constructData(ix: any, context: any, data: any) { return response } -function constructResponse(ix: any, context: any, stream: any) { +function constructResponse(ix: Interaction, context: any, stream: any) { const response = context.response() response.tag = ix.tag @@ -94,7 +94,7 @@ export async function connectSubscribeEvents( // Map the connection to a formatted response stream connection.on("data", (data: any) => { - const responseData = constructData(ix, context, data) + const responseData = constructData(resolvedIx, context, data) lastBlockHeight = responseData.heartbeat.blockHeight outputEmitter.emit("data", responseData) }) @@ -118,5 +118,5 @@ export async function connectSubscribeEvents( connection.close() }, } - return constructResponse(ix, context, responseStream) + return constructResponse(resolvedIx, context, responseStream) }