New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@graphql-tools/executor-http

Package Overview
Dependencies
Maintainers
4
Versions
659
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@graphql-tools/executor-http - npm Package Compare versions

Comparing version 1.1.14 to 1.2.0-alpha-753096403f92c5dbcdd17828acdd17a1aa53a51e

29

CHANGELOG.md
# @graphql-tools/executor-http
## 1.2.0-alpha-753096403f92c5dbcdd17828acdd17a1aa53a51e
### Minor Changes
- [#313](https://github.com/graphql-hive/gateway/pull/313) [`7530964`](https://github.com/graphql-hive/gateway/commit/753096403f92c5dbcdd17828acdd17a1aa53a51e) Thanks [@ardatan](https://github.com/ardatan)! - Automatic Persisted Queries support for upstream requests
For HTTP Executor;
```ts
buildHTTPExecutor({
// ...
apq: true,
});
```
For Gateway Configuration;
```ts
export const gatewayConfig = defineConfig({
transportEntries: {
'*': {
options: {
apq: true,
},
},
},
});
```
## 1.1.14

@@ -4,0 +33,0 @@

9

dist/index.d.ts

@@ -58,2 +58,3 @@ import { ExecutionRequest, DisposableSyncExecutor, DisposableAsyncExecutor } from '@graphql-tools/utils';

print?: (doc: DocumentNode) => string;
apq?: boolean;
/**

@@ -65,2 +66,8 @@ * Enable [Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management)

}
type SerializedRequest = {
query?: string;
variables?: Record<string, any>;
operationName?: string;
extensions?: any;
};
type HeadersConfig = Record<string, string>;

@@ -78,2 +85,2 @@ declare function buildHTTPExecutor(options?: Omit<HTTPExecutorOptions, 'fetch'> & {

export { type AsyncFetchFn, type AsyncImportFn, type FetchFn, type HTTPExecutorOptions, type HeadersConfig, type RegularFetchFn, type SyncFetchFn, type SyncImportFn, type SyncResponse, buildHTTPExecutor, isLiveQueryOperationDefinitionNode };
export { type AsyncFetchFn, type AsyncImportFn, type FetchFn, type HTTPExecutorOptions, type HeadersConfig, type RegularFetchFn, type SerializedRequest, type SyncFetchFn, type SyncImportFn, type SyncResponse, buildHTTPExecutor, isLiveQueryOperationDefinitionNode };

374

dist/index.js
import { isAsyncIterable, isPromise, mapMaybePromise, memoize1, createGraphQLError, inspect, mapAsyncIterator, mergeIncrementalResult, getOperationASTFromRequest } from '@graphql-tools/utils';
import { DisposableSymbols } from '@whatwg-node/disposablestack';
import { File, FormData, TextDecoder, fetch } from '@whatwg-node/fetch';
import { File, FormData, TextEncoder, crypto, TextDecoder, fetch } from '@whatwg-node/fetch';
import { ValueOrPromise } from 'value-or-promise';

@@ -31,12 +31,10 @@ import { extractFiles, isExtractableFile } from 'extract-files';

}
function createFormDataFromVariables({
query,
variables,
operationName,
extensions
}, {
function createFormDataFromVariables(body, {
File: FileCtor = File,
FormData: FormDataCtor = FormData
}) {
const vars = Object.assign({}, variables);
if (!body.variables) {
return JSON.stringify(body);
}
const vars = Object.assign({}, body.variables);
const { clone, files } = extractFiles(

@@ -48,12 +46,3 @@ vars,

if (files.size === 0) {
return JSON.stringify(
{
query,
variables,
operationName,
extensions
},
null,
2
);
return JSON.stringify(body);
}

@@ -72,6 +61,4 @@ const map = {};

JSON.stringify({
query,
variables: clone,
operationName,
extensions
...body,
variables: clone
})

@@ -154,2 +141,16 @@ );

}
function hashSHA256(str) {
const textEncoder = new TextEncoder();
const utf8 = textEncoder.encode(str);
return mapMaybePromise(
crypto.subtle.digest("SHA-256", utf8),
(hashBuffer) => {
let hashHex = "";
for (const bytes of new Uint8Array(hashBuffer)) {
hashHex += bytes.toString(16).padStart(2, "0");
}
return hashHex;
}
);
}

@@ -257,6 +258,3 @@ const DELIM = "\n\n";

baseUrl = "",
query,
variables,
operationName,
extensions
body
}) {

@@ -266,12 +264,14 @@ const dummyHostname = "https://dummyhostname.com";

const urlObj = new URL(validUrl);
urlObj.searchParams.set("query", stripIgnoredCharacters(query));
if (variables && Object.keys(variables).length > 0) {
urlObj.searchParams.set("variables", JSON.stringify(variables));
if (body.query) {
urlObj.searchParams.set("query", body.query);
}
if (operationName) {
urlObj.searchParams.set("operationName", operationName);
if (body.variables && Object.keys(body.variables).length > 0) {
urlObj.searchParams.set("variables", JSON.stringify(body.variables));
}
if (extensions) {
urlObj.searchParams.set("extensions", JSON.stringify(extensions));
if (body.operationName) {
urlObj.searchParams.set("operationName", body.operationName);
}
if (body.extensions) {
urlObj.searchParams.set("extensions", JSON.stringify(body.extensions));
}
const finalUrl = urlObj.toString().replace(dummyHostname, "");

@@ -301,3 +301,3 @@ return finalUrl;

const sharedSignal = createSignalWrapper(disposeCtrl.signal);
const baseExecutor = (request) => {
const baseExecutor = (request, excludeQuery) => {
if (sharedSignal.aborted) {

@@ -333,3 +333,2 @@ return createResultForAbort(sharedSignal.reason);

}
const query = printFn(request.document);
let signal = sharedSignal;

@@ -348,94 +347,149 @@ if (options?.timeout) {

};
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
const query = printFn(request.document);
let serializeFn = function serialize() {
return {
query: excludeQuery ? void 0 : printFn(request.document),
variables: (request.variables && Object.keys(request.variables).length) > 0 ? request.variables : void 0,
operationName: request.operationName ? request.operationName : void 0,
extensions: request.extensions && Object.keys(request.extensions).length > 0 ? request.extensions : void 0
};
};
if (options?.apq) {
serializeFn = function serializeWithAPQ() {
return mapMaybePromise(hashSHA256(query), (sha256Hash) => {
const extensions = request.extensions || {};
extensions["persistedQuery"] = {
version: 1,
sha256Hash
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
return {
query: excludeQuery ? void 0 : query,
variables: (request.variables && Object.keys(request.variables).length) > 0 ? request.variables : void 0,
operationName: request.operationName ? request.operationName : void 0,
extensions
};
});
};
}
return mapMaybePromise(
serializeFn(),
(body) => new ValueOrPromise(() => {
switch (method) {
case "GET": {
const finalUrl = prepareGETUrl({
baseUrl: endpoint,
body
});
const fetchOptions = {
method: "GET",
headers,
signal
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
upstreamErrorExtensions.request.url = finalUrl;
return fetchFn(
finalUrl,
fetchOptions,
request.context,
request.info
);
}
upstreamErrorExtensions.request.url = finalUrl;
return fetchFn(finalUrl, fetchOptions, request.context, request.info);
case "POST": {
upstreamErrorExtensions.request.body = body;
return mapMaybePromise(
createFormDataFromVariables(body, {
File: options?.File,
FormData: options?.FormData
}),
(body2) => {
if (typeof body2 === "string" && !headers["content-type"]) {
upstreamErrorExtensions.request.body = body2;
headers["content-type"] = "application/json";
}
const fetchOptions = {
method: "POST",
body: body2,
headers,
signal
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(
endpoint,
fetchOptions,
request.context,
request.info
);
}
);
}
}
case "POST": {
const body = {
query,
variables: request.variables,
operationName: request.operationName,
extensions: request.extensions
};
upstreamErrorExtensions.request.body = body;
return mapMaybePromise(
createFormDataFromVariables(body, {
File: options?.File,
FormData: options?.FormData
}),
(body2) => {
if (typeof body2 === "string" && !headers["content-type"]) {
upstreamErrorExtensions.request.body = body2;
headers["content-type"] = "application/json";
}
const fetchOptions = {
method: "POST",
body: body2,
headers,
signal
};
if (options?.credentials != null) {
fetchOptions.credentials = options.credentials;
}
return fetchFn(
endpoint,
fetchOptions,
request.context,
request.info
);
}
}).then((fetchResult) => {
upstreamErrorExtensions.response.status = fetchResult.status;
upstreamErrorExtensions.response.statusText = fetchResult.statusText;
Object.defineProperty(upstreamErrorExtensions.response, "headers", {
get() {
return Object.fromEntries(fetchResult.headers.entries());
}
});
if (options?.retry != null && !fetchResult.status.toString().startsWith("2")) {
throw new Error(
fetchResult.statusText || `Upstream HTTP Error: ${fetchResult.status}`
);
}
}
}).then((fetchResult) => {
upstreamErrorExtensions.response.status = fetchResult.status;
upstreamErrorExtensions.response.statusText = fetchResult.statusText;
Object.defineProperty(upstreamErrorExtensions.response, "headers", {
get() {
return Object.fromEntries(fetchResult.headers.entries());
const contentType = fetchResult.headers.get("content-type");
if (contentType?.includes("text/event-stream")) {
return handleEventStreamResponse(signal, fetchResult);
} else if (contentType?.includes("multipart/mixed")) {
return handleMultipartMixedResponse(fetchResult);
}
});
if (options?.retry != null && !fetchResult.status.toString().startsWith("2")) {
throw new Error(
fetchResult.statusText || `Upstream HTTP Error: ${fetchResult.status}`
);
}
const contentType = fetchResult.headers.get("content-type");
if (contentType?.includes("text/event-stream")) {
return handleEventStreamResponse(signal, fetchResult);
} else if (contentType?.includes("multipart/mixed")) {
return handleMultipartMixedResponse(fetchResult);
}
return fetchResult.text();
}).then((result) => {
if (typeof result === "string") {
upstreamErrorExtensions.response.body = result;
if (result) {
try {
const parsedResult = JSON.parse(result);
upstreamErrorExtensions.response.body = parsedResult;
if (parsedResult.data == null && (parsedResult.errors == null || parsedResult.errors.length === 0)) {
return fetchResult.text();
}).then((result) => {
if (typeof result === "string") {
upstreamErrorExtensions.response.body = result;
if (result) {
try {
const parsedResult = JSON.parse(result);
upstreamErrorExtensions.response.body = parsedResult;
if (parsedResult.data == null && (parsedResult.errors == null || parsedResult.errors.length === 0)) {
return {
errors: [
createGraphQLError(
'Unexpected empty "data" and "errors" fields in result: ' + result,
{
extensions: upstreamErrorExtensions
}
)
]
};
}
if (Array.isArray(parsedResult.errors)) {
return {
...parsedResult,
errors: parsedResult.errors.map(
({
message,
...options2
}) => createGraphQLError(message, {
...options2,
extensions: {
code: "DOWNSTREAM_SERVICE_ERROR",
...options2.extensions || {}
}
})
)
};
}
return parsedResult;
} catch (e) {
return {
errors: [
createGraphQLError(
'Unexpected empty "data" and "errors" fields in result: ' + result,
`Unexpected response: ${JSON.stringify(result)}`,
{
extensions: upstreamErrorExtensions
extensions: upstreamErrorExtensions,
originalError: e
}

@@ -446,42 +500,21 @@ )

}
if (Array.isArray(parsedResult.errors)) {
return {
...parsedResult,
errors: parsedResult.errors.map(
({
message,
...options2
}) => createGraphQLError(message, {
...options2,
extensions: {
code: "DOWNSTREAM_SERVICE_ERROR",
...options2.extensions || {}
}
})
)
};
}
return parsedResult;
} catch (e) {
return {
errors: [
createGraphQLError(
`Unexpected response: ${JSON.stringify(result)}`,
{
extensions: upstreamErrorExtensions,
originalError: e
}
)
]
};
}
} else {
return result;
}
} else {
return result;
}
}).catch((e) => {
if (e.name === "AggregateError") {
}).catch((e) => {
if (e.name === "AggregateError") {
return {
errors: e.errors.map(
(e2) => coerceFetchError(e2, {
signal,
endpoint,
upstreamErrorExtensions
})
)
};
}
return {
errors: e.errors.map(
(e2) => coerceFetchError(e2, {
errors: [
coerceFetchError(e, {
signal,

@@ -491,18 +524,25 @@ endpoint,

})
)
]
};
}
return {
errors: [
coerceFetchError(e, {
signal,
endpoint,
upstreamErrorExtensions
})
]
};
}).resolve();
}).resolve()
);
};
let executor = baseExecutor;
if (options?.apq != null) {
executor = function apqExecutor(request) {
return mapMaybePromise(
baseExecutor(request, true),
(res) => {
if (res.errors?.some(
(error) => error.extensions["code"] === "PERSISTED_QUERY_NOT_FOUND" || error.message === "PersistedQueryNotFound"
)) {
return baseExecutor(request, false);
}
return res;
}
);
};
}
if (options?.retry != null) {
const prevExecutor = executor;
executor = function retryExecutor(request) {

@@ -524,3 +564,3 @@ let result;

}
return mapMaybePromise(baseExecutor(request), (res) => {
return mapMaybePromise(prevExecutor(request), (res) => {
result = res;

@@ -527,0 +567,0 @@ if (result?.errors?.length) {

{
"name": "@graphql-tools/executor-http",
"version": "1.1.14",
"version": "1.2.0-alpha-753096403f92c5dbcdd17828acdd17a1aa53a51e",
"type": "module",

@@ -52,2 +52,3 @@ "description": "A set of utils for faster development of GraphQL tools",

"devDependencies": {
"@apollo/server": "^4.11.2",
"@types/extract-files": "8.1.3",

@@ -54,0 +55,0 @@ "@whatwg-node/disposablestack": "^0.0.5",

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
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc