@onflow/transport-http
Advanced tools
Comparing version 1.10.0-alpha.0 to 1.10.0-alpha.1
# @onflow/transport-http | ||
## 1.10.0-alpha.0 | ||
## 1.10.0-alpha.1 | ||
@@ -9,13 +9,13 @@ ### Minor Changes | ||
- [#1794](https://github.com/onflow/fcl-js/pull/1794) [`acf90a78`](https://github.com/onflow/fcl-js/commit/acf90a7841f843227d5d9edb450ef08322c77c4d) Thanks [@jribbink](https://github.com/jribbink)! - Add support for event streaming API interaction (subscribeEvents) | ||
### Patch Changes | ||
- [#1809](https://github.com/onflow/fcl-js/pull/1809) [`3af8a434`](https://github.com/onflow/fcl-js/commit/3af8a434b7b1238e44b6aa56f335c6d3d787cd21) Thanks [@nialexsan](https://github.com/nialexsan)! - split fcl to fcl-core, fcl-react-native, fcl(-web) | ||
- [#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 [[`3af8a434`](https://github.com/onflow/fcl-js/commit/3af8a434b7b1238e44b6aa56f335c6d3d787cd21), [`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: | ||
- @onflow/util-invariant@1.2.2-alpha.0 | ||
- @onflow/util-template@1.2.2-alpha.0 | ||
- @onflow/util-address@1.2.2-alpha.0 | ||
- @onflow/util-logger@1.3.2-alpha.0 | ||
- Updated dependencies [[`0d09d838`](https://github.com/onflow/fcl-js/commit/0d09d8386c2fc472833df7152467d477f36dddc4)]: | ||
- @onflow/util-invariant@1.2.2-alpha.1 | ||
- @onflow/util-template@1.2.2-alpha.1 | ||
- @onflow/util-address@1.2.2-alpha.1 | ||
- @onflow/util-logger@1.3.2-alpha.1 | ||
@@ -22,0 +22,0 @@ ## 1.9.0 |
@@ -10,2 +10,4 @@ 'use strict'; | ||
var utilAddress = require('@onflow/util-address'); | ||
var events = require('events'); | ||
var _WebSocket = require('isomorphic-ws'); | ||
@@ -34,3 +36,12 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var fetchTransport__default = /*#__PURE__*/_interopDefaultLegacy(fetchTransport); | ||
var _WebSocket__default = /*#__PURE__*/_interopDefaultLegacy(_WebSocket); | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
const AbortController = globalThis.AbortController || require("abort-controller"); | ||
@@ -180,9 +191,2 @@ class HTTPRequestError extends Error { | ||
} | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
@@ -200,3 +204,3 @@ async function sendExecuteScriptAtBlockIDRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -214,3 +218,3 @@ async function sendExecuteScriptAtBlockHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -228,5 +232,5 @@ async function sendExecuteScriptAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
function constructResponse$4(ix, context, res) { | ||
function constructResponse$5(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -273,3 +277,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
@@ -284,5 +288,5 @@ async function sendGetAccountAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
function constructResponse$3(ix, context, res) { | ||
function constructResponse$4(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -339,3 +343,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -350,3 +354,3 @@ async function sendGetBlockHeaderByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -362,5 +366,5 @@ async function sendGetLatestBlockHeaderRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
function constructResponse$2(ix, context, res) { | ||
function constructResponse$3(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -402,3 +406,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -413,3 +417,3 @@ async function sendGetBlockByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -425,5 +429,5 @@ async function sendGetBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
function constructResponse$1(ix, context, res) { | ||
function constructResponse$2(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -494,3 +498,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
@@ -505,5 +509,5 @@ async function sendGetEventsForBlockIDsRequest(ix, context, opts) { | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
function constructResponse(ix, context, res) { | ||
function constructResponse$1(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -754,2 +758,221 @@ ret.tag = ix.tag; | ||
const WebSocket = _WebSocket__default["default"]; | ||
class WebsocketError extends Error { | ||
constructor(_ref) { | ||
let { | ||
code, | ||
reason, | ||
message, | ||
wasClean | ||
} = _ref; | ||
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; | ||
} | ||
} | ||
function connectWs(_ref2) { | ||
let { | ||
hostname, | ||
path, | ||
params, | ||
getParams, | ||
retryLimit = 5, | ||
retryIntervalMs = 1000 | ||
} = _ref2; | ||
if (getParams && params) { | ||
throw new Error("connectWs: cannot specify both params and getParams"); | ||
} | ||
let outputEmitter = new events.EventEmitter(); | ||
let retryCount = 0; | ||
const resolveParams = getParams || (() => params); | ||
let close = () => {}; | ||
(function connect() { | ||
let userClosed = false; | ||
let hasOpened = false; | ||
// 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, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
close(); | ||
} | ||
}; | ||
} | ||
function buildConnectionUrl(hostname, path, params) { | ||
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(_ref3 => { | ||
let [key, value] = _ref3; | ||
if (value) { | ||
let formattedValue; | ||
if (Array.isArray(value)) { | ||
formattedValue = value.join(","); | ||
} else { | ||
formattedValue = value.toString(); | ||
} | ||
url.searchParams.append(key, formattedValue); | ||
} | ||
}); | ||
return url.toString(); | ||
} | ||
function constructData(ix, context, data) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.events = data.Events?.length > 0 ? 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()) | ||
})) : null; | ||
response.heartbeat = { | ||
blockId: data.BlockID, | ||
blockHeight: Number(data.Height), | ||
blockTimestamp: data.Timestamp | ||
}; | ||
return response; | ||
} | ||
function constructResponse(ix, context, stream) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.streamConnection = stream; | ||
return response; | ||
} | ||
async function connectSubscribeEvents(ix) { | ||
let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
utilInvariant.invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`); | ||
utilInvariant.invariant(context.response, `SDK Send Get Events Error: context.response must be defined.`); | ||
utilInvariant.invariant(context.Buffer, `SDK Send Get Events Error: context.Buffer must be defined.`); | ||
const resolvedIx = await ix; | ||
const connectWs$1 = opts.connectWs || connectWs; | ||
const outputEmitter = new events.EventEmitter(); | ||
let lastBlockHeight = null; | ||
// Connect to the websocket & provide reconnection parameters | ||
const connection = connectWs$1({ | ||
hostname: opts.node, | ||
path: `/v1/subscribe_events`, | ||
getParams: () => { | ||
const params = { | ||
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 = resolvedIx.subscribeEvents?.startBlockId; | ||
params.start_height = resolvedIx.subscribeEvents?.startHeight; | ||
} | ||
return params; | ||
} | ||
}); | ||
// Map the connection to a formatted response stream | ||
connection.on("data", data => { | ||
const responseData = constructData(resolvedIx, context, data); | ||
lastBlockHeight = responseData.heartbeat.blockHeight; | ||
outputEmitter.emit("data", responseData); | ||
}); | ||
connection.on("error", error => { | ||
outputEmitter.emit("error", error); | ||
}); | ||
connection.on("close", () => { | ||
outputEmitter.emit("close"); | ||
}); | ||
const responseStream = { | ||
on(event, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
connection.close(); | ||
} | ||
}; | ||
return constructResponse(resolvedIx, context, responseStream); | ||
} | ||
const send = async function (ix, context) { | ||
@@ -775,2 +998,4 @@ let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
return opts.sendGetEvents ? opts.sendGetEvents(ix, context, opts) : sendGetEvents(ix, context, opts); | ||
case context.ix.isSubscribeEvents(ix): | ||
return opts.connectSubscribeEvents ? opts.connectSubscribeEvents(ix, context, opts) : connectSubscribeEvents(ix, context, opts); | ||
case context.ix.isGetBlock(ix): | ||
@@ -792,2 +1017,4 @@ return opts.sendGetBlock ? opts.sendGetBlock(ix, context, opts) : sendGetBlock(ix, context, opts); | ||
exports.HTTPRequestError = HTTPRequestError; | ||
exports.WebsocketError = WebsocketError; | ||
exports.connectSubscribeEvents = connectSubscribeEvents; | ||
exports.send = send; | ||
@@ -794,0 +1021,0 @@ exports.sendExecuteScript = sendExecuteScript; |
@@ -6,3 +6,13 @@ import { invariant } from '@onflow/util-invariant'; | ||
import { sansPrefix } from '@onflow/util-address'; | ||
import { EventEmitter } from 'events'; | ||
import _WebSocket from 'isomorphic-ws'; | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
const AbortController = globalThis.AbortController || require("abort-controller"); | ||
@@ -152,9 +162,2 @@ class HTTPRequestError extends Error { | ||
} | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
@@ -172,3 +175,3 @@ async function sendExecuteScriptAtBlockIDRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -186,3 +189,3 @@ async function sendExecuteScriptAtBlockHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -200,5 +203,5 @@ async function sendExecuteScriptAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
function constructResponse$4(ix, context, res) { | ||
function constructResponse$5(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -245,3 +248,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
@@ -256,5 +259,5 @@ async function sendGetAccountAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
function constructResponse$3(ix, context, res) { | ||
function constructResponse$4(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -311,3 +314,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -322,3 +325,3 @@ async function sendGetBlockHeaderByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -334,5 +337,5 @@ async function sendGetLatestBlockHeaderRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
function constructResponse$2(ix, context, res) { | ||
function constructResponse$3(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -374,3 +377,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -385,3 +388,3 @@ async function sendGetBlockByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -397,5 +400,5 @@ async function sendGetBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
function constructResponse$1(ix, context, res) { | ||
function constructResponse$2(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -466,3 +469,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
@@ -477,5 +480,5 @@ async function sendGetEventsForBlockIDsRequest(ix, context, opts) { | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
function constructResponse(ix, context, res) { | ||
function constructResponse$1(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -726,2 +729,221 @@ ret.tag = ix.tag; | ||
const WebSocket = _WebSocket; | ||
class WebsocketError extends Error { | ||
constructor(_ref) { | ||
let { | ||
code, | ||
reason, | ||
message, | ||
wasClean | ||
} = _ref; | ||
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; | ||
} | ||
} | ||
function connectWs(_ref2) { | ||
let { | ||
hostname, | ||
path, | ||
params, | ||
getParams, | ||
retryLimit = 5, | ||
retryIntervalMs = 1000 | ||
} = _ref2; | ||
if (getParams && params) { | ||
throw new Error("connectWs: cannot specify both params and getParams"); | ||
} | ||
let outputEmitter = new EventEmitter(); | ||
let retryCount = 0; | ||
const resolveParams = getParams || (() => params); | ||
let close = () => {}; | ||
(function connect() { | ||
let userClosed = false; | ||
let hasOpened = false; | ||
// 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, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
close(); | ||
} | ||
}; | ||
} | ||
function buildConnectionUrl(hostname, path, params) { | ||
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(_ref3 => { | ||
let [key, value] = _ref3; | ||
if (value) { | ||
let formattedValue; | ||
if (Array.isArray(value)) { | ||
formattedValue = value.join(","); | ||
} else { | ||
formattedValue = value.toString(); | ||
} | ||
url.searchParams.append(key, formattedValue); | ||
} | ||
}); | ||
return url.toString(); | ||
} | ||
function constructData(ix, context, data) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.events = data.Events?.length > 0 ? 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()) | ||
})) : null; | ||
response.heartbeat = { | ||
blockId: data.BlockID, | ||
blockHeight: Number(data.Height), | ||
blockTimestamp: data.Timestamp | ||
}; | ||
return response; | ||
} | ||
function constructResponse(ix, context, stream) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.streamConnection = stream; | ||
return response; | ||
} | ||
async function connectSubscribeEvents(ix) { | ||
let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
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.`); | ||
const resolvedIx = await ix; | ||
const connectWs$1 = opts.connectWs || connectWs; | ||
const outputEmitter = new EventEmitter(); | ||
let lastBlockHeight = null; | ||
// Connect to the websocket & provide reconnection parameters | ||
const connection = connectWs$1({ | ||
hostname: opts.node, | ||
path: `/v1/subscribe_events`, | ||
getParams: () => { | ||
const params = { | ||
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 = resolvedIx.subscribeEvents?.startBlockId; | ||
params.start_height = resolvedIx.subscribeEvents?.startHeight; | ||
} | ||
return params; | ||
} | ||
}); | ||
// Map the connection to a formatted response stream | ||
connection.on("data", data => { | ||
const responseData = constructData(resolvedIx, context, data); | ||
lastBlockHeight = responseData.heartbeat.blockHeight; | ||
outputEmitter.emit("data", responseData); | ||
}); | ||
connection.on("error", error => { | ||
outputEmitter.emit("error", error); | ||
}); | ||
connection.on("close", () => { | ||
outputEmitter.emit("close"); | ||
}); | ||
const responseStream = { | ||
on(event, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
connection.close(); | ||
} | ||
}; | ||
return constructResponse(resolvedIx, context, responseStream); | ||
} | ||
const send = async function (ix, context) { | ||
@@ -747,2 +969,4 @@ let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
return opts.sendGetEvents ? opts.sendGetEvents(ix, context, opts) : sendGetEvents(ix, context, opts); | ||
case context.ix.isSubscribeEvents(ix): | ||
return opts.connectSubscribeEvents ? opts.connectSubscribeEvents(ix, context, opts) : connectSubscribeEvents(ix, context, opts); | ||
case context.ix.isGetBlock(ix): | ||
@@ -763,3 +987,3 @@ return opts.sendGetBlock ? opts.sendGetBlock(ix, context, opts) : sendGetBlock(ix, context, opts); | ||
export { HTTPRequestError, send, sendExecuteScript, sendGetAccount, sendGetBlock, sendGetBlockHeader, sendGetCollection, sendGetEvents, sendGetNetworkParameters, sendGetTransaction, sendGetTransactionStatus, sendPing, sendTransaction }; | ||
export { HTTPRequestError, WebsocketError, connectSubscribeEvents, send, sendExecuteScript, sendGetAccount, sendGetBlock, sendGetBlockHeader, sendGetCollection, sendGetEvents, sendGetNetworkParameters, sendGetTransaction, sendGetTransactionStatus, sendPing, sendTransaction }; | ||
//# sourceMappingURL=sdk-send-http.module.js.map |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@onflow/util-invariant'), require('@onflow/rlp'), require('@onflow/util-logger'), require('cross-fetch'), require('@onflow/util-address')) : | ||
typeof define === 'function' && define.amd ? define(['exports', '@onflow/util-invariant', '@onflow/rlp', '@onflow/util-logger', 'cross-fetch', '@onflow/util-address'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["onflowTransport-http"] = {}, global.utilInvariant, null, global.logger, global.fetchTransport, global.utilAddress)); | ||
})(this, (function (exports, utilInvariant, rlp, logger, fetchTransport, utilAddress) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@onflow/util-invariant'), require('@onflow/rlp'), require('@onflow/util-logger'), require('cross-fetch'), require('@onflow/util-address'), require('events'), require('isomorphic-ws')) : | ||
typeof define === 'function' && define.amd ? define(['exports', '@onflow/util-invariant', '@onflow/rlp', '@onflow/util-logger', 'cross-fetch', '@onflow/util-address', 'events', 'isomorphic-ws'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["onflowTransport-http"] = {}, global.utilInvariant, null, global.logger, global.fetchTransport, global.utilAddress, global.events, global._WebSocket)); | ||
})(this, (function (exports, utilInvariant, rlp, logger, fetchTransport, utilAddress, events, _WebSocket) { 'use strict'; | ||
@@ -29,3 +29,12 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var fetchTransport__default = /*#__PURE__*/_interopDefaultLegacy(fetchTransport); | ||
var _WebSocket__default = /*#__PURE__*/_interopDefaultLegacy(_WebSocket); | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
const AbortController = globalThis.AbortController || require("abort-controller"); | ||
@@ -175,9 +184,2 @@ class HTTPRequestError extends Error { | ||
} | ||
function safeParseJSON(data) { | ||
try { | ||
return JSON.parse(data); | ||
} catch { | ||
return null; | ||
} | ||
} | ||
@@ -195,3 +197,3 @@ async function sendExecuteScriptAtBlockIDRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -209,3 +211,3 @@ async function sendExecuteScriptAtBlockHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
@@ -223,5 +225,5 @@ async function sendExecuteScriptAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$4(ix, context, res); | ||
return constructResponse$5(ix, context, res); | ||
} | ||
function constructResponse$4(ix, context, res) { | ||
function constructResponse$5(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -268,3 +270,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
@@ -279,5 +281,5 @@ async function sendGetAccountAtLatestBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$3(ix, context, res); | ||
return constructResponse$4(ix, context, res); | ||
} | ||
function constructResponse$3(ix, context, res) { | ||
function constructResponse$4(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -334,3 +336,3 @@ ret.tag = ix.tag; | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -345,3 +347,3 @@ async function sendGetBlockHeaderByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
@@ -357,5 +359,5 @@ async function sendGetLatestBlockHeaderRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$2(ix, context, res); | ||
return constructResponse$3(ix, context, res); | ||
} | ||
function constructResponse$2(ix, context, res) { | ||
function constructResponse$3(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -397,3 +399,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -408,3 +410,3 @@ async function sendGetBlockByHeightRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
@@ -420,5 +422,5 @@ async function sendGetBlockRequest(ix, context, opts) { | ||
}); | ||
return constructResponse$1(ix, context, res); | ||
return constructResponse$2(ix, context, res); | ||
} | ||
function constructResponse$1(ix, context, res) { | ||
function constructResponse$2(ix, context, res) { | ||
const block = res.length ? res[0] : null; | ||
@@ -489,3 +491,3 @@ const ret = context.response(); | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
@@ -500,5 +502,5 @@ async function sendGetEventsForBlockIDsRequest(ix, context, opts) { | ||
}); | ||
return constructResponse(ix, context, res); | ||
return constructResponse$1(ix, context, res); | ||
} | ||
function constructResponse(ix, context, res) { | ||
function constructResponse$1(ix, context, res) { | ||
let ret = context.response(); | ||
@@ -749,2 +751,221 @@ ret.tag = ix.tag; | ||
const WebSocket = _WebSocket__default["default"]; | ||
class WebsocketError extends Error { | ||
constructor(_ref) { | ||
let { | ||
code, | ||
reason, | ||
message, | ||
wasClean | ||
} = _ref; | ||
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; | ||
} | ||
} | ||
function connectWs(_ref2) { | ||
let { | ||
hostname, | ||
path, | ||
params, | ||
getParams, | ||
retryLimit = 5, | ||
retryIntervalMs = 1000 | ||
} = _ref2; | ||
if (getParams && params) { | ||
throw new Error("connectWs: cannot specify both params and getParams"); | ||
} | ||
let outputEmitter = new events.EventEmitter(); | ||
let retryCount = 0; | ||
const resolveParams = getParams || (() => params); | ||
let close = () => {}; | ||
(function connect() { | ||
let userClosed = false; | ||
let hasOpened = false; | ||
// 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, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
close(); | ||
} | ||
}; | ||
} | ||
function buildConnectionUrl(hostname, path, params) { | ||
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(_ref3 => { | ||
let [key, value] = _ref3; | ||
if (value) { | ||
let formattedValue; | ||
if (Array.isArray(value)) { | ||
formattedValue = value.join(","); | ||
} else { | ||
formattedValue = value.toString(); | ||
} | ||
url.searchParams.append(key, formattedValue); | ||
} | ||
}); | ||
return url.toString(); | ||
} | ||
function constructData(ix, context, data) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.events = data.Events?.length > 0 ? 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()) | ||
})) : null; | ||
response.heartbeat = { | ||
blockId: data.BlockID, | ||
blockHeight: Number(data.Height), | ||
blockTimestamp: data.Timestamp | ||
}; | ||
return response; | ||
} | ||
function constructResponse(ix, context, stream) { | ||
const response = context.response(); | ||
response.tag = ix.tag; | ||
response.streamConnection = stream; | ||
return response; | ||
} | ||
async function connectSubscribeEvents(ix) { | ||
let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
utilInvariant.invariant(opts.node, `SDK Send Get Events Error: opts.node must be defined.`); | ||
utilInvariant.invariant(context.response, `SDK Send Get Events Error: context.response must be defined.`); | ||
utilInvariant.invariant(context.Buffer, `SDK Send Get Events Error: context.Buffer must be defined.`); | ||
const resolvedIx = await ix; | ||
const connectWs$1 = opts.connectWs || connectWs; | ||
const outputEmitter = new events.EventEmitter(); | ||
let lastBlockHeight = null; | ||
// Connect to the websocket & provide reconnection parameters | ||
const connection = connectWs$1({ | ||
hostname: opts.node, | ||
path: `/v1/subscribe_events`, | ||
getParams: () => { | ||
const params = { | ||
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 = resolvedIx.subscribeEvents?.startBlockId; | ||
params.start_height = resolvedIx.subscribeEvents?.startHeight; | ||
} | ||
return params; | ||
} | ||
}); | ||
// Map the connection to a formatted response stream | ||
connection.on("data", data => { | ||
const responseData = constructData(resolvedIx, context, data); | ||
lastBlockHeight = responseData.heartbeat.blockHeight; | ||
outputEmitter.emit("data", responseData); | ||
}); | ||
connection.on("error", error => { | ||
outputEmitter.emit("error", error); | ||
}); | ||
connection.on("close", () => { | ||
outputEmitter.emit("close"); | ||
}); | ||
const responseStream = { | ||
on(event, listener) { | ||
outputEmitter.on(event, listener); | ||
return this; | ||
}, | ||
off(event, listener) { | ||
outputEmitter.off(event, listener); | ||
return this; | ||
}, | ||
close() { | ||
connection.close(); | ||
} | ||
}; | ||
return constructResponse(resolvedIx, context, responseStream); | ||
} | ||
const send = async function (ix, context) { | ||
@@ -770,2 +991,4 @@ let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
return opts.sendGetEvents ? opts.sendGetEvents(ix, context, opts) : sendGetEvents(ix, context, opts); | ||
case context.ix.isSubscribeEvents(ix): | ||
return opts.connectSubscribeEvents ? opts.connectSubscribeEvents(ix, context, opts) : connectSubscribeEvents(ix, context, opts); | ||
case context.ix.isGetBlock(ix): | ||
@@ -787,2 +1010,4 @@ return opts.sendGetBlock ? opts.sendGetBlock(ix, context, opts) : sendGetBlock(ix, context, opts); | ||
exports.HTTPRequestError = HTTPRequestError; | ||
exports.WebsocketError = WebsocketError; | ||
exports.connectSubscribeEvents = connectSubscribeEvents; | ||
exports.send = send; | ||
@@ -789,0 +1014,0 @@ exports.sendExecuteScript = sendExecuteScript; |
{ | ||
"name": "@onflow/transport-http", | ||
"version": "1.10.0-alpha.0", | ||
"version": "1.10.0-alpha.1", | ||
"description": "Flow SDK HTTP Transport Module", | ||
@@ -16,6 +16,6 @@ "license": "Apache-2.0", | ||
"devDependencies": { | ||
"@onflow/fcl-bundle": "^1.4.2-alpha.0", | ||
"@onflow/rlp": "^1.2.2-alpha.0", | ||
"@onflow/sdk": "^1.4.0-alpha.0", | ||
"@onflow/types": "^1.3.0-alpha.0", | ||
"@onflow/fcl-bundle": "^1.4.2-alpha.1", | ||
"@onflow/rlp": "^1.2.2-alpha.1", | ||
"@onflow/sdk": "^1.4.0-alpha.2", | ||
"@onflow/types": "^1.3.0-alpha.1", | ||
"jest": "^29.5.0" | ||
@@ -38,9 +38,12 @@ }, | ||
"@babel/runtime": "^7.18.6", | ||
"@onflow/util-address": "^1.2.2-alpha.0", | ||
"@onflow/util-invariant": "^1.2.2-alpha.0", | ||
"@onflow/util-logger": "^1.3.2-alpha.0", | ||
"@onflow/util-template": "^1.2.2-alpha.0", | ||
"@onflow/util-address": "^1.2.2-alpha.1", | ||
"@onflow/util-invariant": "^1.2.2-alpha.1", | ||
"@onflow/util-logger": "^1.3.2-alpha.1", | ||
"@onflow/util-template": "^1.2.2-alpha.1", | ||
"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" | ||
} | ||
} |
@@ -1,13 +0,15 @@ | ||
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 { 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.js"; | ||
export { sendGetNetworkParameters } from "./send-get-network-parameters.js"; | ||
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"; | ||
export { HTTPRequestError } from "./http-request.js"; |
@@ -15,2 +15,3 @@ import { ISendPingContext } from "./send-ping"; | ||
isGetNetworkParameters: (ix: Interaction) => boolean; | ||
isSubscribeEvents: (ix: Interaction) => boolean; | ||
} | ||
@@ -35,4 +36,5 @@ interface IContext extends ISendPingContext { | ||
sendGetNetworkParameters?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void; | ||
connectSubscribeEvents?: (ix: Interaction, context: IContext, opts: IOptsCommon) => void; | ||
} | ||
export declare const send: (ix: Interaction, context: IContext, opts?: IOpts) => Promise<any>; | ||
export {}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
415471
30
3051
10
+ Addedevents@^3.3.0
+ Addedisomorphic-ws@^5.0.0
+ Addedws@^8.14.2
+ Addedevents@3.3.0(transitive)
+ Addedisomorphic-ws@5.0.0(transitive)
+ Addedws@8.18.0(transitive)