Socket
Socket
Sign inDemoInstall

@graphql-tools/executor-http

Package Overview
Dependencies
20
Maintainers
3
Versions
190
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.3-alpha-20230809181317-aca946d4 to 1.0.3-alpha-20230809204600-c19e0921

6

cjs/createFormDataFromVariables.js

@@ -5,3 +5,2 @@ "use strict";

const extract_files_1 = require("extract-files");
const value_or_promise_1 = require("value-or-promise");
const utils_1 = require("@graphql-tools/utils");

@@ -70,5 +69,4 @@ const fetch_1 = require("@whatwg-node/fetch");

}
return value_or_promise_1.ValueOrPromise.all(uploads.map((upload, i) => new value_or_promise_1.ValueOrPromise(() => handleUpload(upload, i))))
.then(() => form)
.resolve();
return Promise.all(uploads.map((upload, i) => handleUpload(upload, i)))
.then(() => form);
}

@@ -75,0 +73,0 @@ exports.createFormDataFromVariables = createFormDataFromVariables;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleEventStreamResponse = exports.isReadableStream = void 0;
exports.isReadableStream = exports.handleEventStreamResponse = void 0;
const utils_1 = require("@graphql-tools/utils");
const addCancelToResponseStream_js_1 = require("./addCancelToResponseStream.js");
const handleAsyncIterable_js_1 = require("./handleAsyncIterable.js");
const handleReadableStream_js_1 = require("./handleReadableStream.js");
async function* handleEventStreamResponse(response) {
let stream;
const body = response.body;
if (isReadableStream(body)) {
stream = streamToAsyncIterator(body);
}
else if ((0, utils_1.isAsyncIterable)(body)) {
stream = body;
}
else {
throw new Error('Response body is expected to be a readable stream, but got ' + (0, utils_1.inspect)(body));
}
const decoder = new TextDecoder();
for await (const rawChunk of stream) {
const chunk = typeof rawChunk === 'string' ? rawChunk : decoder.decode(rawChunk, { stream: true });
for (const msg of chunk.split('\n\n')) {
const parts = msg.split('\n');
const eventPart = parts.find(part => part.startsWith('event:'));
const dataPart = parts.find(part => part.startsWith('data:'));
if (!eventPart && !dataPart) {
continue; // ping
}
if (!eventPart) {
throw new Error('Missing stream message event');
}
const event = eventPart.replace('event:', '').trim();
if (event === 'complete') {
return;
}
else if (event === 'next') {
if (!dataPart) {
throw new Error(`Missing stream message data for "next" event`);
}
const data = dataPart.replace('data:', '').trim();
const parsed = JSON.parse(data);
yield parsed.payload || parsed;
}
else {
throw new Error(`Unsupported stream message event "${event}"`);
}
}
}
}
exports.handleEventStreamResponse = handleEventStreamResponse;
function isReadableStream(value) {
return value && typeof value.getReader === 'function';
return typeof Object(value).getReader === 'function';
}
exports.isReadableStream = isReadableStream;
function handleEventStreamResponse(response, controller) {
// node-fetch returns body as a promise so we need to resolve it
const body = response.body;
if (body) {
if ((0, utils_1.isAsyncIterable)(body)) {
const resultStream = (0, handleAsyncIterable_js_1.handleAsyncIterable)(body);
return (0, addCancelToResponseStream_js_1.addCancelToResponseStream)(resultStream, controller);
}
if (isReadableStream(body)) {
return (0, handleReadableStream_js_1.handleReadableStream)(body);
}
async function* streamToAsyncIterator(stream) {
const reader = stream.getReader();
for (;;) {
const { done, value } = await reader.read();
if (done)
return;
yield value;
}
throw new Error('Response body is expected to be a readable stream but got; ' + (0, utils_1.inspect)(body));
}
exports.handleEventStreamResponse = handleEventStreamResponse;

@@ -7,10 +7,7 @@ "use strict";

const utils_1 = require("@graphql-tools/utils");
const addCancelToResponseStream_js_1 = require("./addCancelToResponseStream.js");
function isIncomingMessage(body) {
return body != null && typeof body === 'object' && 'pipe' in body;
}
async function handleMultipartMixedResponse(response, controller) {
// eslint-disable-next-line require-yield -- the returned iterable will yield
async function* handleMultipartMixedResponse(response) {
const body = response.body;
const contentType = response.headers.get('content-type') || '';
let asyncIterator;
let stream;
if (isIncomingMessage(body)) {

@@ -23,18 +20,16 @@ // Meros/node expects headers as an object map with the content-type prop

const result = await (0, node_1.meros)(body);
if ('next' in result) {
asyncIterator = result;
if (!('next' in result)) {
throw new Error('Multipart mixed response must be iterable');
}
stream = result;
}
else {
// Nothing is needed for regular `Response`.
const result = await (0, browser_1.meros)(response);
if ('next' in result) {
asyncIterator = result;
if (!('next' in result)) {
throw new Error('Multipart mixed response must be iterable');
}
stream = result;
}
const executionResult = {};
if (asyncIterator == null) {
return executionResult;
}
const resultStream = (0, utils_1.mapAsyncIterator)(asyncIterator, (part) => {
const resultStream = (0, utils_1.mapAsyncIterator)(stream, part => {
if (part.json) {

@@ -48,5 +43,11 @@ const incrementalResult = part.body;

}
else {
throw new Error(`Unexpected multipart stream data ${(0, utils_1.inspect)(part)}`);
}
});
return (0, addCancelToResponseStream_js_1.addCancelToResponseStream)(resultStream, controller);
return resultStream;
}
exports.handleMultipartMixedResponse = handleMultipartMixedResponse;
function isIncomingMessage(body) {
return body != null && typeof body === 'object' && 'pipe' in body;
}

@@ -5,3 +5,2 @@ "use strict";

const graphql_1 = require("graphql");
const value_or_promise_1 = require("value-or-promise");
const utils_1 = require("@graphql-tools/utils");

@@ -16,5 +15,5 @@ const fetch_1 = require("@whatwg-node/fetch");

function buildHTTPExecutor(options) {
const executor = (request) => {
const executor = async (request) => {
const controller = new AbortController();
const fetchFn = request.extensions?.fetch ?? options?.fetch ?? fetch_1.fetch;
const controller = new AbortController();
let method = request.extensions?.method || options?.method || 'POST';

@@ -37,3 +36,3 @@ const operationAst = (0, utils_1.getOperationASTFromRequest)(request);

const query = (0, graphql_1.print)(request.document);
let timeoutId;
let timeoutId = null;
if (options?.timeout) {

@@ -46,99 +45,97 @@ timeoutId = setTimeout(() => {

}
const responseDetailsForError = {};
return new value_or_promise_1.ValueOrPromise(() => {
switch (method) {
case 'GET': {
const finalUrl = (0, prepareGETUrl_js_1.prepareGETUrl)({
baseUrl: endpoint,
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
});
const fetchOptions = {
method: 'GET',
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(finalUrl, fetchOptions, request.context, request.info);
let fetching;
switch (method) {
case 'GET': {
const finalUrl = (0, prepareGETUrl_js_1.prepareGETUrl)({
baseUrl: endpoint,
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
});
const fetchOptions = {
method: 'GET',
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
case 'POST':
return new value_or_promise_1.ValueOrPromise(() => (0, createFormDataFromVariables_js_1.createFormDataFromVariables)({
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
}, {
File: options?.File,
FormData: options?.FormData,
}))
.then(body => {
if (typeof body === 'string') {
headers['content-type'] = 'application/json';
}
const fetchOptions = {
method: 'POST',
body,
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(endpoint, fetchOptions, request.context, request.info);
})
.resolve();
fetching = fetchFn(finalUrl, fetchOptions, request.context, request.info);
break;
}
})
.then((fetchResult) => {
responseDetailsForError.status = fetchResult.status;
responseDetailsForError.statusText = fetchResult.statusText;
if (timeoutId != null) {
case 'POST': {
const body = await (0, createFormDataFromVariables_js_1.createFormDataFromVariables)({
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
}, {
File: options?.File,
FormData: options?.FormData,
});
if (typeof body === 'string') {
headers['content-type'] = 'application/json';
}
const fetchOptions = {
method: 'POST',
body,
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
fetching = fetchFn(endpoint, fetchOptions, request.context, request.info);
break;
}
default:
throw new Error(`Unsupported request method "${method}"`);
}
const responseDetailsForError = {};
try {
const response = await fetching;
responseDetailsForError.status = response.status;
responseDetailsForError.statusText = response.statusText;
if (timeoutId) {
clearTimeout(timeoutId);
}
// Retry should respect HTTP Errors
if (options?.retry != null && !fetchResult.status.toString().startsWith('2')) {
throw new Error(fetchResult.statusText || `HTTP Error: ${fetchResult.status}`);
if (options?.retry != null && !response.status.toString().startsWith('2')) {
throw new Error(response.statusText || `HTTP Error: ${response.status}`);
}
const contentType = fetchResult.headers.get('content-type');
let result;
const contentType = response.headers.get('content-type');
if (contentType?.includes('text/event-stream')) {
return (0, handleEventStreamResponse_js_1.handleEventStreamResponse)(fetchResult, controller);
result = (0, handleEventStreamResponse_js_1.handleEventStreamResponse)(response);
}
else if (contentType?.includes('multipart/mixed')) {
return (0, handleMultipartMixedResponse_js_1.handleMultipartMixedResponse)(fetchResult, controller);
// TODO: should we assert async response?
result = (0, handleMultipartMixedResponse_js_1.handleMultipartMixedResponse)(response);
}
return fetchResult.text();
})
.then(result => {
if (typeof result === 'string') {
if (result) {
try {
return JSON.parse(result);
}
catch (e) {
return {
errors: [
(0, utils_1.createGraphQLError)(`Unexpected response: ${JSON.stringify(result)}`, {
extensions: {
requestBody: {
query,
operationName: request.operationName,
},
responseDetails: responseDetailsForError,
else {
const text = await response.text();
try {
result = JSON.parse(text);
}
catch (e) {
result = {
errors: [
(0, utils_1.createGraphQLError)(`Unexpected response: "${text}"`, {
extensions: {
requestBody: {
query,
operationName: request.operationName,
},
originalError: e,
}),
],
};
}
responseDetails: responseDetailsForError,
},
originalError: e,
}),
],
};
}
}
else {
return result;
}
})
.catch((e) => {
return result;
}
catch (e) {
if (typeof e === 'string') {

@@ -183,3 +180,3 @@ return {

errors: [
(0, utils_1.createGraphQLError)('The operation was aborted. reason: ' + controller.signal.reason, {
(0, utils_1.createGraphQLError)('The operation was aborted. Reason: ' + controller.signal.reason, {
extensions: {

@@ -229,4 +226,3 @@ requestBody: {

}
})
.resolve();
}
};

@@ -237,3 +233,3 @@ if (options?.retry != null) {

let attempt = 0;
function retryAttempt() {
async function retryAttempt() {
attempt++;

@@ -248,11 +244,7 @@ if (attempt > options.retry) {

}
return new value_or_promise_1.ValueOrPromise(() => executor(request))
.then(res => {
result = res;
if (result?.errors?.length) {
return retryAttempt();
}
return result;
})
.resolve();
result = await executor(request);
if (Object(result).errors?.length) {
return retryAttempt();
}
return result;
}

@@ -259,0 +251,0 @@ return retryAttempt();

import { extractFiles, isExtractableFile } from 'extract-files';
import { ValueOrPromise } from 'value-or-promise';
import { isAsyncIterable, isPromise } from '@graphql-tools/utils';

@@ -66,5 +65,4 @@ import { File as DefaultFile, FormData as DefaultFormData } from '@whatwg-node/fetch';

}
return ValueOrPromise.all(uploads.map((upload, i) => new ValueOrPromise(() => handleUpload(upload, i))))
.then(() => form)
.resolve();
return Promise.all(uploads.map((upload, i) => handleUpload(upload, i)))
.then(() => form);
}

@@ -71,0 +69,0 @@ function isBlob(obj) {

import { inspect, isAsyncIterable } from '@graphql-tools/utils';
import { addCancelToResponseStream } from './addCancelToResponseStream.js';
import { handleAsyncIterable } from './handleAsyncIterable.js';
import { handleReadableStream } from './handleReadableStream.js';
export function isReadableStream(value) {
return value && typeof value.getReader === 'function';
}
export function handleEventStreamResponse(response, controller) {
// node-fetch returns body as a promise so we need to resolve it
export async function* handleEventStreamResponse(response) {
let stream;
const body = response.body;
if (body) {
if (isAsyncIterable(body)) {
const resultStream = handleAsyncIterable(body);
return addCancelToResponseStream(resultStream, controller);
if (isReadableStream(body)) {
stream = streamToAsyncIterator(body);
}
else if (isAsyncIterable(body)) {
stream = body;
}
else {
throw new Error('Response body is expected to be a readable stream, but got ' + inspect(body));
}
const decoder = new TextDecoder();
for await (const rawChunk of stream) {
const chunk = typeof rawChunk === 'string' ? rawChunk : decoder.decode(rawChunk, { stream: true });
for (const msg of chunk.split('\n\n')) {
const parts = msg.split('\n');
const eventPart = parts.find(part => part.startsWith('event:'));
const dataPart = parts.find(part => part.startsWith('data:'));
if (!eventPart && !dataPart) {
continue; // ping
}
if (!eventPart) {
throw new Error('Missing stream message event');
}
const event = eventPart.replace('event:', '').trim();
if (event === 'complete') {
return;
}
else if (event === 'next') {
if (!dataPart) {
throw new Error(`Missing stream message data for "next" event`);
}
const data = dataPart.replace('data:', '').trim();
const parsed = JSON.parse(data);
yield parsed.payload || parsed;
}
else {
throw new Error(`Unsupported stream message event "${event}"`);
}
}
if (isReadableStream(body)) {
return handleReadableStream(body);
}
}
throw new Error('Response body is expected to be a readable stream but got; ' + inspect(body));
}
export function isReadableStream(value) {
return typeof Object(value).getReader === 'function';
}
async function* streamToAsyncIterator(stream) {
const reader = stream.getReader();
for (;;) {
const { done, value } = await reader.read();
if (done)
return;
yield value;
}
}
import { meros as merosReadableStream } from 'meros/browser';
import { meros as merosIncomingMessage } from 'meros/node';
import { mapAsyncIterator, mergeIncrementalResult } from '@graphql-tools/utils';
import { addCancelToResponseStream } from './addCancelToResponseStream.js';
function isIncomingMessage(body) {
return body != null && typeof body === 'object' && 'pipe' in body;
}
export async function handleMultipartMixedResponse(response, controller) {
import { inspect, mapAsyncIterator, mergeIncrementalResult, } from '@graphql-tools/utils';
// eslint-disable-next-line require-yield -- the returned iterable will yield
export async function* handleMultipartMixedResponse(response) {
const body = response.body;
const contentType = response.headers.get('content-type') || '';
let asyncIterator;
let stream;
if (isIncomingMessage(body)) {

@@ -19,18 +16,16 @@ // Meros/node expects headers as an object map with the content-type prop

const result = await merosIncomingMessage(body);
if ('next' in result) {
asyncIterator = result;
if (!('next' in result)) {
throw new Error('Multipart mixed response must be iterable');
}
stream = result;
}
else {
// Nothing is needed for regular `Response`.
const result = await merosReadableStream(response);
if ('next' in result) {
asyncIterator = result;
if (!('next' in result)) {
throw new Error('Multipart mixed response must be iterable');
}
stream = result;
}
const executionResult = {};
if (asyncIterator == null) {
return executionResult;
}
const resultStream = mapAsyncIterator(asyncIterator, (part) => {
const resultStream = mapAsyncIterator(stream, part => {
if (part.json) {

@@ -44,4 +39,10 @@ const incrementalResult = part.body;

}
else {
throw new Error(`Unexpected multipart stream data ${inspect(part)}`);
}
});
return addCancelToResponseStream(resultStream, controller);
return resultStream;
}
function isIncomingMessage(body) {
return body != null && typeof body === 'object' && 'pipe' in body;
}
import { print } from 'graphql';
import { ValueOrPromise } from 'value-or-promise';
import { createGraphQLError, getOperationASTFromRequest, } from '@graphql-tools/utils';

@@ -11,5 +10,5 @@ import { fetch as defaultFetch } from '@whatwg-node/fetch';

export function buildHTTPExecutor(options) {
const executor = (request) => {
const executor = async (request) => {
const controller = new AbortController();
const fetchFn = request.extensions?.fetch ?? options?.fetch ?? defaultFetch;
const controller = new AbortController();
let method = request.extensions?.method || options?.method || 'POST';

@@ -32,3 +31,3 @@ const operationAst = getOperationASTFromRequest(request);

const query = print(request.document);
let timeoutId;
let timeoutId = null;
if (options?.timeout) {

@@ -41,99 +40,97 @@ timeoutId = setTimeout(() => {

}
const responseDetailsForError = {};
return new ValueOrPromise(() => {
switch (method) {
case 'GET': {
const finalUrl = prepareGETUrl({
baseUrl: endpoint,
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
});
const fetchOptions = {
method: 'GET',
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(finalUrl, fetchOptions, request.context, request.info);
let fetching;
switch (method) {
case 'GET': {
const finalUrl = prepareGETUrl({
baseUrl: endpoint,
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
});
const fetchOptions = {
method: 'GET',
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
case 'POST':
return new ValueOrPromise(() => createFormDataFromVariables({
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
}, {
File: options?.File,
FormData: options?.FormData,
}))
.then(body => {
if (typeof body === 'string') {
headers['content-type'] = 'application/json';
}
const fetchOptions = {
method: 'POST',
body,
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(endpoint, fetchOptions, request.context, request.info);
})
.resolve();
fetching = fetchFn(finalUrl, fetchOptions, request.context, request.info);
break;
}
})
.then((fetchResult) => {
responseDetailsForError.status = fetchResult.status;
responseDetailsForError.statusText = fetchResult.statusText;
if (timeoutId != null) {
case 'POST': {
const body = await createFormDataFromVariables({
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions,
}, {
File: options?.File,
FormData: options?.FormData,
});
if (typeof body === 'string') {
headers['content-type'] = 'application/json';
}
const fetchOptions = {
method: 'POST',
body,
headers,
signal: controller.signal,
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
fetching = fetchFn(endpoint, fetchOptions, request.context, request.info);
break;
}
default:
throw new Error(`Unsupported request method "${method}"`);
}
const responseDetailsForError = {};
try {
const response = await fetching;
responseDetailsForError.status = response.status;
responseDetailsForError.statusText = response.statusText;
if (timeoutId) {
clearTimeout(timeoutId);
}
// Retry should respect HTTP Errors
if (options?.retry != null && !fetchResult.status.toString().startsWith('2')) {
throw new Error(fetchResult.statusText || `HTTP Error: ${fetchResult.status}`);
if (options?.retry != null && !response.status.toString().startsWith('2')) {
throw new Error(response.statusText || `HTTP Error: ${response.status}`);
}
const contentType = fetchResult.headers.get('content-type');
let result;
const contentType = response.headers.get('content-type');
if (contentType?.includes('text/event-stream')) {
return handleEventStreamResponse(fetchResult, controller);
result = handleEventStreamResponse(response);
}
else if (contentType?.includes('multipart/mixed')) {
return handleMultipartMixedResponse(fetchResult, controller);
// TODO: should we assert async response?
result = handleMultipartMixedResponse(response);
}
return fetchResult.text();
})
.then(result => {
if (typeof result === 'string') {
if (result) {
try {
return JSON.parse(result);
}
catch (e) {
return {
errors: [
createGraphQLError(`Unexpected response: ${JSON.stringify(result)}`, {
extensions: {
requestBody: {
query,
operationName: request.operationName,
},
responseDetails: responseDetailsForError,
else {
const text = await response.text();
try {
result = JSON.parse(text);
}
catch (e) {
result = {
errors: [
createGraphQLError(`Unexpected response: "${text}"`, {
extensions: {
requestBody: {
query,
operationName: request.operationName,
},
originalError: e,
}),
],
};
}
responseDetails: responseDetailsForError,
},
originalError: e,
}),
],
};
}
}
else {
return result;
}
})
.catch((e) => {
return result;
}
catch (e) {
if (typeof e === 'string') {

@@ -178,3 +175,3 @@ return {

errors: [
createGraphQLError('The operation was aborted. reason: ' + controller.signal.reason, {
createGraphQLError('The operation was aborted. Reason: ' + controller.signal.reason, {
extensions: {

@@ -224,4 +221,3 @@ requestBody: {

}
})
.resolve();
}
};

@@ -232,3 +228,3 @@ if (options?.retry != null) {

let attempt = 0;
function retryAttempt() {
async function retryAttempt() {
attempt++;

@@ -243,11 +239,7 @@ if (attempt > options.retry) {

}
return new ValueOrPromise(() => executor(request))
.then(res => {
result = res;
if (result?.errors?.length) {
return retryAttempt();
}
return result;
})
.resolve();
result = await executor(request);
if (Object(result).errors?.length) {
return retryAttempt();
}
return result;
}

@@ -254,0 +246,0 @@ return retryAttempt();

{
"name": "@graphql-tools/executor-http",
"version": "1.0.3-alpha-20230809181317-aca946d4",
"version": "1.0.3-alpha-20230809204600-c19e0921",
"description": "A set of utils for faster development of GraphQL tools",

@@ -15,4 +15,3 @@ "sideEffects": false,

"meros": "^1.2.1",
"tslib": "^2.4.0",
"value-or-promise": "^1.0.12"
"tslib": "^2.4.0"
},

@@ -19,0 +18,0 @@ "repository": {

@@ -10,2 +10,2 @@ import { FormData as DefaultFormData } from '@whatwg-node/fetch';

FormData?: typeof DefaultFormData;
}): string | FormData | Promise<FormData>;
}): string | Promise<FormData>;
import { ExecutionResult } from '@graphql-tools/utils';
export declare function isReadableStream(value: any): value is ReadableStream;
export declare function handleEventStreamResponse(response: Response, controller: AbortController): AsyncIterable<ExecutionResult>;
import { SyncResponse } from './index.js';
export declare function handleEventStreamResponse(response: Response | SyncResponse): AsyncIterableIterator<ExecutionResult>;
export declare function isReadableStream<T>(value: unknown): value is ReadableStream<T>;
import { ExecutionResult } from '@graphql-tools/utils';
export declare function handleMultipartMixedResponse(response: Response, controller: AbortController): Promise<ExecutionResult<any, any> | AsyncIterable<ExecutionResult<any, any> | undefined>>;
export declare function handleMultipartMixedResponse(response: Response): AsyncIterableIterator<ExecutionResult>;

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc