graphql-sse
Advanced tools
Comparing version 1.3.2 to 2.0.0
@@ -54,2 +54,4 @@ /** | ||
/** @category Common */ | ||
export declare function print<ForID extends boolean, E extends StreamEvent>(msg: StreamMessage<ForID, E>): string; | ||
/** @category Common */ | ||
export interface ExecutionResult<Data = Record<string, unknown>, Extensions = Record<string, unknown>> { | ||
@@ -94,1 +96,13 @@ errors?: ReadonlyArray<GraphQLError>; | ||
} | ||
/** | ||
* Checkes whether the provided value is an async iterable. | ||
* | ||
* @category Common | ||
*/ | ||
export declare function isAsyncIterable<T>(val: unknown): val is AsyncIterable<T>; | ||
/** | ||
* Checkes whether the provided value is an async generator. | ||
* | ||
* @category Common | ||
*/ | ||
export declare function isAsyncGenerator<T>(val: unknown): val is AsyncGenerator<T>; |
@@ -8,3 +8,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseStreamData = exports.validateStreamEvent = exports.TOKEN_QUERY_KEY = exports.TOKEN_HEADER_KEY = void 0; | ||
exports.isAsyncGenerator = exports.isAsyncIterable = exports.parseStreamData = exports.print = exports.validateStreamEvent = exports.TOKEN_QUERY_KEY = exports.TOKEN_HEADER_KEY = void 0; | ||
const utils_1 = require("./utils"); | ||
/** | ||
@@ -37,2 +38,11 @@ * Header key through which the event stream token is transmitted | ||
/** @category Common */ | ||
function print(msg) { | ||
let str = `event: ${msg.event}`; | ||
if (msg.data) | ||
str += `\ndata: ${JSON.stringify(msg.data)}`; | ||
str += '\n\n'; | ||
return str; | ||
} | ||
exports.print = print; | ||
/** @category Common */ | ||
function parseStreamData(e, data) { | ||
@@ -52,1 +62,23 @@ if (data) { | ||
exports.parseStreamData = parseStreamData; | ||
/** | ||
* Checkes whether the provided value is an async iterable. | ||
* | ||
* @category Common | ||
*/ | ||
function isAsyncIterable(val) { | ||
return typeof Object(val)[Symbol.asyncIterator] === 'function'; | ||
} | ||
exports.isAsyncIterable = isAsyncIterable; | ||
/** | ||
* Checkes whether the provided value is an async generator. | ||
* | ||
* @category Common | ||
*/ | ||
function isAsyncGenerator(val) { | ||
return ((0, utils_1.isObject)(val) && | ||
typeof Object(val)[Symbol.asyncIterator] === 'function' && | ||
typeof val.return === 'function' && | ||
typeof val.throw === 'function' && | ||
typeof val.next === 'function'); | ||
} | ||
exports.isAsyncGenerator = isAsyncGenerator; |
@@ -6,17 +6,78 @@ /** | ||
*/ | ||
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import type { IncomingMessage, ServerResponse } from 'http'; | ||
import type { Http2ServerRequest, Http2ServerResponse } from 'http2'; | ||
import { ExecutionArgs, GraphQLSchema, validate as graphqlValidate } from 'graphql'; | ||
import { RequestParams, ExecutionResult, ExecutionPatchResult } from './common'; | ||
import { ExecutionResult, ExecutionPatchResult, RequestParams } from './common'; | ||
/** | ||
* The incoming request headers the implementing server should provide. | ||
* | ||
* @category Server | ||
*/ | ||
export type NodeRequest = IncomingMessage | Http2ServerRequest; | ||
export interface RequestHeaders { | ||
get: (key: string) => string | null | undefined; | ||
} | ||
/** | ||
* Server agnostic request interface containing the raw request | ||
* which is server dependant. | ||
* | ||
* @category Server | ||
*/ | ||
export type NodeResponse = ServerResponse | Http2ServerResponse; | ||
export interface Request<Raw = unknown, Context = unknown> { | ||
readonly method: string; | ||
readonly url: string; | ||
readonly headers: RequestHeaders; | ||
/** | ||
* Parsed request body or a parser function. | ||
* | ||
* If the provided function throws, the error message "Unparsable JSON body" will | ||
* be in the erroneous response. | ||
*/ | ||
readonly body: string | Record<PropertyKey, unknown> | null | (() => string | Record<PropertyKey, unknown> | null | Promise<string | Record<PropertyKey, unknown> | null>); | ||
/** | ||
* The raw request itself from the implementing server. | ||
*/ | ||
readonly raw: Raw; | ||
/** | ||
* Context value about the incoming request, you're free to pass any information here. | ||
* | ||
* Intentionally not readonly because you're free to mutate it whenever you want. | ||
*/ | ||
context: Context; | ||
} | ||
/** | ||
* The response headers that get returned from graphql-sse. | ||
* | ||
* @category Server | ||
*/ | ||
export type ResponseHeaders = { | ||
accept?: string; | ||
allow?: string; | ||
'content-type'?: string; | ||
} & Record<string, string>; | ||
/** | ||
* Server agnostic response body returned from `graphql-sse` needing | ||
* to be coerced to the server implementation in use. | ||
* | ||
* When the body is a string, it is NOT a GraphQL response. | ||
* | ||
* @category Server | ||
*/ | ||
export type ResponseBody = string | AsyncGenerator<string, void, undefined>; | ||
/** | ||
* Server agnostic response options (ex. status and headers) returned from | ||
* `graphql-sse` needing to be coerced to the server implementation in use. | ||
* | ||
* @category Server | ||
*/ | ||
export interface ResponseInit { | ||
readonly status: number; | ||
readonly statusText: string; | ||
readonly headers?: ResponseHeaders; | ||
} | ||
/** | ||
* Server agnostic response returned from `graphql-sse` containing the | ||
* body and init options needing to be coerced to the server implementation in use. | ||
* | ||
* @category Server | ||
*/ | ||
export type Response = readonly [body: ResponseBody | null, init: ResponseInit]; | ||
/** | ||
* A concrete GraphQL execution context value type. | ||
@@ -31,32 +92,12 @@ * | ||
*/ | ||
export type ExecutionContext = object | symbol | number | string | boolean | undefined | null; | ||
export type OperationContext = Record<PropertyKey, unknown> | symbol | number | string | boolean | undefined | null; | ||
/** @category Server */ | ||
export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & { | ||
contextValue: Context; | ||
}; | ||
/** @category Server */ | ||
export type OperationResult = Promise<AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult> | AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult; | ||
/** @category Server */ | ||
export interface HandlerOptions<Request extends NodeRequest = NodeRequest, Response extends NodeResponse = NodeResponse> { | ||
export interface HandlerOptions<RequestRaw = unknown, RequestContext = unknown, Context extends OperationContext = undefined> { | ||
/** | ||
* The GraphQL schema on which the operations will | ||
* be executed and validated against. | ||
* | ||
* If a function is provided, it will be called on every | ||
* subscription request allowing you to manipulate schema | ||
* dynamically. | ||
* | ||
* If the schema is left undefined, you're trusted to | ||
* provide one in the returned `ExecutionArgs` from the | ||
* `onSubscribe` callback. | ||
*/ | ||
schema?: GraphQLSchema | ((req: Request, args: Omit<ExecutionArgs, 'schema'>) => Promise<GraphQLSchema> | GraphQLSchema); | ||
/** | ||
* A value which is provided to every resolver and holds | ||
* important contextual information like the currently | ||
* logged in user, or access to a database. | ||
* | ||
* Note that the context function is invoked on each operation only once. | ||
* Meaning, for subscriptions, only at the point of initialising the subscription; | ||
* not on every subscription event emission. Read more about the context lifecycle | ||
* in subscriptions here: https://github.com/graphql/graphql-js/issues/894. | ||
*/ | ||
context?: ExecutionContext | ((req: Request, args: ExecutionArgs) => Promise<ExecutionContext> | ExecutionContext); | ||
/** | ||
* A custom GraphQL validate function allowing you to apply your | ||
@@ -70,3 +111,3 @@ * own validation rules. | ||
*/ | ||
execute?: (args: ExecutionArgs) => OperationResult; | ||
execute?: (args: OperationArgs<Context>) => OperationResult; | ||
/** | ||
@@ -76,32 +117,48 @@ * Is the `subscribe` function from GraphQL which is | ||
*/ | ||
subscribe?: (args: ExecutionArgs) => OperationResult; | ||
subscribe?: (args: OperationArgs<Context>) => OperationResult; | ||
/** | ||
* The GraphQL schema on which the operations will | ||
* be executed and validated against. | ||
* | ||
* If a function is provided, it will be called on every | ||
* subscription request allowing you to manipulate schema | ||
* dynamically. | ||
* | ||
* If the schema is left undefined, you're trusted to | ||
* provide one in the returned `ExecutionArgs` from the | ||
* `onSubscribe` callback. | ||
*/ | ||
schema?: GraphQLSchema | ((req: Request<RequestRaw, RequestContext>, args: Pick<OperationArgs<Context>, 'contextValue' | 'operationName' | 'document' | 'variableValues'>) => Promise<GraphQLSchema> | GraphQLSchema); | ||
/** | ||
* Authenticate the client. Returning a string indicates that the client | ||
* is authenticated and the request is ready to be processed. | ||
* | ||
* A token of type string MUST be supplied; if there is no token, you may | ||
* return an empty string (`''`); | ||
* A distinct token of type string must be supplied to enable the "single connection mode". | ||
* | ||
* If you want to respond to the client with a custom status or body, | ||
* you should do so using the provided `res` argument which will stop | ||
* further execution. | ||
* Providing `null` as the token will completely disable the "single connection mode" | ||
* and all incoming requests will always use the "distinct connection mode". | ||
* | ||
* @default 'req.headers["x-graphql-event-stream-token"] || req.url.searchParams["token"] || generateRandomUUID()' // https://gist.github.com/jed/982883 | ||
*/ | ||
authenticate?: (req: Request, res: Response) => Promise<string | undefined | void> | string | undefined | void; | ||
authenticate?: (req: Request<RequestRaw, RequestContext>) => Promise<Response | string | undefined | null> | Response | string | undefined | null; | ||
/** | ||
* Called when a new event stream is connecting BEFORE it is accepted. | ||
* By accepted, its meant the server responded with a 200 (OK), alongside | ||
* flushing the necessary event stream headers. | ||
* | ||
* If you want to respond to the client with a custom status or body, | ||
* you should do so using the provided `res` argument which will stop | ||
* further execution. | ||
* By accepted, its meant the server processed the request and responded | ||
* with a 200 (OK), alongside flushing the necessary event stream headers. | ||
*/ | ||
onConnecting?: (req: Request, res: Response) => Promise<void> | void; | ||
onConnect?: (req: Request<RequestRaw, RequestContext>) => Promise<Response | null | undefined | void> | Response | null | undefined | void; | ||
/** | ||
* Called when a new event stream has been succesfully connected and | ||
* accepted, and after all pending messages have been flushed. | ||
* A value which is provided to every resolver and holds | ||
* important contextual information like the currently | ||
* logged in user, or access to a database. | ||
* | ||
* Note that the context function is invoked on each operation only once. | ||
* Meaning, for subscriptions, only at the point of initialising the subscription; | ||
* not on every subscription event emission. Read more about the context lifecycle | ||
* in subscriptions here: https://github.com/graphql/graphql-js/issues/894. | ||
* | ||
* If you don't provide the context context field, but have a context - you're trusted to | ||
* provide one in `onSubscribe`. | ||
*/ | ||
onConnected?: (req: Request) => Promise<void> | void; | ||
context?: Context | ((req: Request<RequestRaw, RequestContext>, params: RequestParams) => Promise<Context> | Context); | ||
/** | ||
@@ -126,3 +183,3 @@ * The subscribe callback executed right after processing the request | ||
*/ | ||
onSubscribe?: (req: Request, res: Response, params: RequestParams) => Promise<ExecutionArgs | void> | ExecutionArgs | void; | ||
onSubscribe?: (req: Request<RequestRaw, RequestContext>, params: RequestParams) => Promise<Response | OperationResult | OperationArgs<Context> | void> | Response | OperationResult | OperationArgs<Context> | void; | ||
/** | ||
@@ -138,13 +195,9 @@ * Executed after the operation call resolves. For streaming | ||
* | ||
* Use this callback to listen for GraphQL operations and | ||
* execution result manipulation. | ||
* If you want the single result and the events from a streaming | ||
* operation, use the `onNext` callback. | ||
* | ||
* If you want to respond to the client with a custom status or body, | ||
* you should do so using the provided `res` argument which will stop | ||
* further execution. | ||
* | ||
* First argument, the request, is always the GraphQL operation | ||
* request. | ||
* If `onSubscribe` returns an `OperationResult`, this hook | ||
* will NOT be called. | ||
*/ | ||
onOperation?: (req: Request, res: Response, args: ExecutionArgs, result: OperationResult) => Promise<OperationResult | void> | OperationResult | void; | ||
onOperation?: (ctx: Context, req: Request<RequestRaw, RequestContext>, args: ExecutionArgs, result: OperationResult) => Promise<OperationResult | void> | OperationResult | void; | ||
/** | ||
@@ -160,6 +213,5 @@ * Executed after an operation has emitted a result right before | ||
* | ||
* First argument, the request, is always the GraphQL operation | ||
* request. | ||
* @param req - Always the request that contains the GraphQL operation. | ||
*/ | ||
onNext?: (req: Request, args: ExecutionArgs, result: ExecutionResult | ExecutionPatchResult) => Promise<ExecutionResult | ExecutionPatchResult | void> | ExecutionResult | ExecutionPatchResult | void; | ||
onNext?: (ctx: Context, req: Request<RequestRaw, RequestContext>, result: ExecutionResult | ExecutionPatchResult) => Promise<ExecutionResult | ExecutionPatchResult | void> | ExecutionResult | ExecutionPatchResult | void; | ||
/** | ||
@@ -173,20 +225,10 @@ * The complete callback is executed after the operation | ||
* | ||
* First argument, the request, is always the GraphQL operation | ||
* request. | ||
* @param req - Always the request that contains the GraphQL operation. | ||
*/ | ||
onComplete?: (req: Request, args: ExecutionArgs) => Promise<void> | void; | ||
/** | ||
* Called when an event stream has disconnected right before the | ||
* accepting the stream. | ||
*/ | ||
onDisconnect?: (req: Request) => Promise<void> | void; | ||
onComplete?: (ctx: Context, req: Request<RequestRaw, RequestContext>) => Promise<void> | void; | ||
} | ||
/** | ||
* The ready-to-use handler. Simply plug it in your favourite HTTP framework | ||
* and enjoy. | ||
* The ready-to-use handler. Simply plug it in your favourite fetch-enabled HTTP | ||
* framework and enjoy. | ||
* | ||
* Beware that the handler resolves only after the whole operation completes. | ||
* - If query/mutation, waits for result | ||
* - If subscription, waits for complete | ||
* | ||
* Errors thrown from **any** of the provided options or callbacks (or even due to | ||
@@ -196,37 +238,7 @@ * library misuse or potential bugs) will reject the handler's promise. They are | ||
* | ||
* For production environments, its recommended not to transmit the exact internal | ||
* error details to the client, but instead report to an error logging tool or simply | ||
* the console. Roughly: | ||
* | ||
* ```ts | ||
* import http from 'http'; | ||
* import { createHandler } from 'graphql-sse'; | ||
* | ||
* const handler = createHandler({ ... }); | ||
* | ||
* http.createServer(async (req, res) => { | ||
* try { | ||
* await handler(req, res); | ||
* } catch (err) { | ||
* console.error(err); | ||
* // or | ||
* Sentry.captureException(err); | ||
* | ||
* if (!res.headersSent) { | ||
* res.writeHead(500, 'Internal Server Error').end(); | ||
* } | ||
* } | ||
* }); | ||
* ``` | ||
* | ||
* Note that some libraries, like fastify, parse the body before reaching the handler. | ||
* In such cases all request 'data' events are already consumed. Use this `body` argument | ||
* too pass in the read body and avoid listening for the 'data' events internally. Do | ||
* beware that the `body` argument will be consumed **only** if it's an object. | ||
* | ||
* @category Server | ||
*/ | ||
export type Handler<Request extends NodeRequest = NodeRequest, Response extends NodeResponse = NodeResponse> = (req: Request, res: Response, body?: unknown) => Promise<void>; | ||
export type Handler<RequestRaw = unknown, RequestContext = unknown> = (req: Request<RequestRaw, RequestContext>) => Promise<Response>; | ||
/** | ||
* Makes a Protocol complient HTTP GraphQL server handler. The handler can | ||
* Makes a Protocol complient HTTP GraphQL server handler. The handler can | ||
* be used with your favourite server library. | ||
@@ -238,3 +250,2 @@ * | ||
*/ | ||
export declare function createHandler<Request extends NodeRequest = NodeRequest, Response extends NodeResponse = NodeResponse>(options: HandlerOptions<Request, Response>): Handler<Request, Response>; | ||
export declare function isAsyncGenerator<T>(val: unknown): val is AsyncGenerator<T>; | ||
export declare function createHandler<RequestRaw = unknown, RequestContext = unknown, Context extends OperationContext = undefined>(options: HandlerOptions<RequestRaw, RequestContext, Context>): Handler<RequestRaw, RequestContext>; |
@@ -7,2 +7,14 @@ "use strict"; | ||
*/ | ||
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } | ||
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { | ||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); | ||
var g = generator.apply(thisArg, _arguments || []), i, q = []; | ||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; | ||
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } | ||
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } | ||
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } | ||
function fulfill(value) { resume("next", value); } | ||
function reject(value) { resume("throw", value); } | ||
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } | ||
}; | ||
var __asyncValues = (this && this.__asyncValues) || function (o) { | ||
@@ -16,3 +28,3 @@ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isAsyncGenerator = exports.createHandler = void 0; | ||
exports.createHandler = void 0; | ||
const graphql_1 = require("graphql"); | ||
@@ -22,3 +34,3 @@ const utils_1 = require("./utils"); | ||
/** | ||
* Makes a Protocol complient HTTP GraphQL server handler. The handler can | ||
* Makes a Protocol complient HTTP GraphQL server handler. The handler can | ||
* be used with your favourite server library. | ||
@@ -31,5 +43,5 @@ * | ||
function createHandler(options) { | ||
const { schema, context, validate = graphql_1.validate, execute = graphql_1.execute, subscribe = graphql_1.subscribe, authenticate = function extractOrCreateStreamToken(req) { | ||
const { validate = graphql_1.validate, execute = graphql_1.execute, subscribe = graphql_1.subscribe, schema, authenticate = function extractOrCreateStreamToken(req) { | ||
var _a; | ||
const headerToken = req.headers[common_1.TOKEN_HEADER_KEY] || req.headers['x-graphql-stream-token']; // @deprecated >v1.0.0 | ||
const headerToken = req.headers.get(common_1.TOKEN_HEADER_KEY); | ||
if (headerToken) | ||
@@ -44,149 +56,179 @@ return Array.isArray(headerToken) ? headerToken.join('') : headerToken; | ||
}); | ||
}, onConnecting, onConnected, onSubscribe, onOperation, onNext, onComplete, onDisconnect, } = options; | ||
}, onConnect, context, onSubscribe, onOperation, onNext, onComplete, } = options; | ||
const streams = {}; | ||
function createStream(token) { | ||
let request = null, response = null, pinger, disposed = false; | ||
const pendingMsgs = []; | ||
const ops = {}; | ||
function write(msg) { | ||
return new Promise((resolve, reject) => { | ||
if (disposed || !response || !response.writable) | ||
return resolve(false); | ||
// @ts-expect-error both ServerResponse and Http2ServerResponse have this write signature | ||
response.write(msg, 'utf-8', (err) => { | ||
if (err) | ||
return reject(err); | ||
resolve(true); | ||
let pinger; | ||
const msgs = (() => { | ||
const pending = []; | ||
const deferred = { | ||
done: false, | ||
error: null, | ||
resolve: () => { | ||
// noop | ||
}, | ||
}; | ||
async function dispose() { | ||
clearInterval(pinger); | ||
// make room for another potential stream while this one is being disposed | ||
if (typeof token === 'string') | ||
delete streams[token]; | ||
// complete all operations and flush messages queue before ending the stream | ||
for (const op of Object.values(ops)) { | ||
if ((0, common_1.isAsyncGenerator)(op)) { | ||
await op.return(undefined); | ||
} | ||
} | ||
} | ||
const iterator = (function iterator() { | ||
return __asyncGenerator(this, arguments, function* iterator_1() { | ||
for (;;) { | ||
if (!pending.length) { | ||
// only wait if there are no pending messages available | ||
yield __await(new Promise((resolve) => (deferred.resolve = resolve))); | ||
} | ||
// first flush | ||
while (pending.length) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
yield yield __await(pending.shift()); | ||
} | ||
// then error | ||
if (deferred.error) { | ||
throw deferred.error; | ||
} | ||
// or complete | ||
if (deferred.done) { | ||
return yield __await(void 0); | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
async function emit(event, data) { | ||
let msg = `event: ${event}`; | ||
if (data) | ||
msg += `\ndata: ${JSON.stringify(data)}`; | ||
msg += '\n\n'; | ||
const wrote = await write(msg); | ||
if (!wrote) | ||
pendingMsgs.push(msg); | ||
} | ||
async function dispose() { | ||
if (disposed) | ||
return; | ||
disposed = true; | ||
// make room for another potential stream while this one is being disposed | ||
if (typeof token === 'string') | ||
delete streams[token]; | ||
// complete all operations and flush messages queue before ending the stream | ||
for (const op of Object.values(ops)) { | ||
if (isAsyncGenerator(op)) | ||
await op.return(undefined); | ||
} | ||
while (pendingMsgs.length) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const msg = pendingMsgs.shift(); | ||
await write(msg); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
response.end(); // response must exist at this point | ||
response = null; | ||
clearInterval(pinger); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
onDisconnect === null || onDisconnect === void 0 ? void 0 : onDisconnect(request); // request must exist at this point | ||
request = null; | ||
} | ||
})(); | ||
iterator.throw = async (err) => { | ||
if (!deferred.done) { | ||
deferred.done = true; | ||
deferred.error = err; | ||
deferred.resolve(); | ||
await dispose(); | ||
} | ||
return { done: true, value: undefined }; | ||
}; | ||
iterator.return = async () => { | ||
if (!deferred.done) { | ||
deferred.done = true; | ||
deferred.resolve(); | ||
await dispose(); | ||
} | ||
return { done: true, value: undefined }; | ||
}; | ||
return { | ||
next(msg) { | ||
pending.push(msg); | ||
deferred.resolve(); | ||
}, | ||
iterator, | ||
}; | ||
})(); | ||
let subscribed = false; | ||
return { | ||
get open() { | ||
return disposed || Boolean(response); | ||
return subscribed; | ||
}, | ||
ops, | ||
async use(req, res) { | ||
request = req; | ||
response = res; | ||
req.socket.setTimeout(0); | ||
req.socket.setNoDelay(true); | ||
req.socket.setKeepAlive(true); | ||
res.once('close', dispose); | ||
res.statusCode = 200; | ||
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8'); | ||
res.setHeader('Cache-Control', 'no-cache'); | ||
res.setHeader('X-Accel-Buffering', 'no'); | ||
if (req.httpVersionMajor < 2) | ||
res.setHeader('Connection', 'keep-alive'); | ||
if ('flushHeaders' in res) | ||
res.flushHeaders(); | ||
subscribe() { | ||
subscribed = true; | ||
// write an empty message because some browsers (like Firefox and Safari) | ||
// dont accept the header flush | ||
await write(':\n\n'); | ||
msgs.next(':\n\n'); | ||
// ping client every 12 seconds to keep the connection alive | ||
pinger = setInterval(() => write(':\n\n'), 12000); | ||
while (pendingMsgs.length) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const msg = pendingMsgs.shift(); | ||
const wrote = await write(msg); | ||
if (!wrote) | ||
throw new Error('Unable to flush messages'); | ||
} | ||
await (onConnected === null || onConnected === void 0 ? void 0 : onConnected(req)); | ||
pinger = setInterval(() => msgs.next(':\n\n'), 12000); | ||
return msgs.iterator; | ||
}, | ||
async from(operationReq, args, result, opId) { | ||
var _a, e_1, _b, _c; | ||
if (isAsyncIterable(result)) { | ||
try { | ||
/** multiple emitted results */ | ||
for (var _d = true, result_1 = __asyncValues(result), result_1_1; result_1_1 = await result_1.next(), _a = result_1_1.done, !_a;) { | ||
_c = result_1_1.value; | ||
_d = false; | ||
from(ctx, req, result, opId) { | ||
(async () => { | ||
var _a, e_1, _b, _c; | ||
if ((0, common_1.isAsyncIterable)(result)) { | ||
try { | ||
/** multiple emitted results */ | ||
for (var _d = true, result_1 = __asyncValues(result), result_1_1; result_1_1 = await result_1.next(), _a = result_1_1.done, !_a;) { | ||
_c = result_1_1.value; | ||
_d = false; | ||
try { | ||
let part = _c; | ||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(ctx, req, part)); | ||
if (maybeResult) | ||
part = maybeResult; | ||
msgs.next((0, common_1.print)({ | ||
event: 'next', | ||
data: opId | ||
? { | ||
id: opId, | ||
payload: part, | ||
} | ||
: part, | ||
})); | ||
} | ||
finally { | ||
_d = true; | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
let part = _c; | ||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(operationReq, args, part)); | ||
if (maybeResult) | ||
part = maybeResult; | ||
await emit('next', opId | ||
? { | ||
id: opId, | ||
payload: part, | ||
} | ||
: part); | ||
if (!_d && !_a && (_b = result_1.return)) await _b.call(result_1); | ||
} | ||
finally { | ||
_d = true; | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (!_d && !_a && (_b = result_1.return)) await _b.call(result_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
else { | ||
/** single emitted result */ | ||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(ctx, req, result)); | ||
if (maybeResult) | ||
result = maybeResult; | ||
msgs.next((0, common_1.print)({ | ||
event: 'next', | ||
data: opId | ||
? { | ||
id: opId, | ||
payload: result, | ||
} | ||
: result, | ||
})); | ||
} | ||
} | ||
else { | ||
/** single emitted result */ | ||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(operationReq, args, result)); | ||
if (maybeResult) | ||
result = maybeResult; | ||
await emit('next', opId | ||
? { | ||
id: opId, | ||
payload: result, | ||
} | ||
: result); | ||
} | ||
await emit('complete', opId ? { id: opId } : null); | ||
// end on complete when no operation id is present | ||
// because distinct event streams are used for each operation | ||
if (!opId) | ||
await dispose(); | ||
else | ||
delete ops[opId]; | ||
await (onComplete === null || onComplete === void 0 ? void 0 : onComplete(operationReq, args)); | ||
msgs.next((0, common_1.print)({ | ||
event: 'complete', | ||
data: opId ? { id: opId } : null, | ||
})); | ||
await (onComplete === null || onComplete === void 0 ? void 0 : onComplete(ctx, req)); | ||
if (!opId) { | ||
// end on complete when no operation id is present | ||
// because distinct event streams are used for each operation | ||
await msgs.iterator.return(); | ||
} | ||
else { | ||
delete ops[opId]; | ||
} | ||
})().catch(msgs.iterator.throw); | ||
}, | ||
}; | ||
} | ||
async function prepare(req, res, params) { | ||
let args, operation; | ||
const maybeExecArgs = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, res, params)); | ||
if (maybeExecArgs) | ||
args = maybeExecArgs; | ||
async function prepare(req, params) { | ||
let args; | ||
const onSubscribeResult = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, params)); | ||
if (isResponse(onSubscribeResult)) | ||
return onSubscribeResult; | ||
else if (isExecutionResult(onSubscribeResult) || | ||
(0, common_1.isAsyncIterable)(onSubscribeResult)) | ||
return { | ||
// even if the result is already available, use | ||
// context because onNext and onComplete needs it | ||
ctx: (typeof context === 'function' | ||
? await context(req, params) | ||
: context), | ||
perform() { | ||
return onSubscribeResult; | ||
}, | ||
}; | ||
else if (onSubscribeResult) | ||
args = onSubscribeResult; | ||
else { | ||
@@ -203,5 +245,21 @@ // you either provide a schema dynamically through | ||
} | ||
catch (_a) { | ||
res.writeHead(400, 'GraphQL query syntax error').end(); | ||
return; | ||
catch (err) { | ||
return [ | ||
JSON.stringify({ | ||
errors: [ | ||
err instanceof Error | ||
? { | ||
message: err.message, | ||
// TODO: stack might leak sensitive information | ||
// stack: err.stack, | ||
} | ||
: err, | ||
], | ||
}), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { 'content-type': 'application/json; charset=utf-8' }, | ||
}, | ||
]; | ||
} | ||
@@ -213,2 +271,5 @@ } | ||
variableValues: variables, | ||
contextValue: (typeof context === 'function' | ||
? await context(req, params) | ||
: context), | ||
}; | ||
@@ -219,2 +280,3 @@ args = Object.assign(Object.assign({}, argsWithoutSchema), { schema: typeof schema === 'function' | ||
} | ||
let operation; | ||
try { | ||
@@ -226,5 +288,13 @@ const ast = (0, graphql_1.getOperationAST)(args.document, args.operationName); | ||
} | ||
catch (_b) { | ||
res.writeHead(400, 'Unable to detect operation AST').end(); | ||
return; | ||
catch (_a) { | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Unable to detect operation AST' }], | ||
}), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { 'content-type': 'application/json; charset=utf-8' }, | ||
}, | ||
]; | ||
} | ||
@@ -234,12 +304,16 @@ // mutations cannot happen over GETs as per the spec | ||
if (operation === 'mutation' && req.method === 'GET') { | ||
res | ||
.writeHead(405, 'Cannot perform mutations over GET', { | ||
Allow: 'POST', | ||
}) | ||
.end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Cannot perform mutations over GET' }], | ||
}), | ||
{ | ||
status: 405, | ||
statusText: 'Method Not Allowed', | ||
headers: { | ||
allow: 'POST', | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
if (!('contextValue' in args)) | ||
args.contextValue = | ||
typeof context === 'function' ? await context(req, args) : context; | ||
// we validate after injecting the context because the process of | ||
@@ -249,3 +323,3 @@ // reporting the validation errors might need the supplied context value | ||
if (validationErrs.length) { | ||
if (req.headers.accept === 'text/event-stream') { | ||
if (req.headers.get('accept') === 'text/event-stream') { | ||
// accept the request and emit the validation error in event streams, | ||
@@ -255,106 +329,136 @@ // promoting graceful GraphQL error reporting | ||
// Read more: https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation | ||
return [ | ||
args, | ||
function perform() { | ||
return { | ||
ctx: args.contextValue, | ||
perform() { | ||
return { errors: validationErrs }; | ||
}, | ||
]; | ||
}; | ||
} | ||
res | ||
.writeHead(400, { | ||
'Content-Type': req.headers.accept === 'application/json' | ||
? 'application/json; charset=utf-8' | ||
: 'application/graphql+json; charset=utf-8', | ||
}) | ||
// @ts-expect-error both ServerResponse and Http2ServerResponse have this write signature | ||
.write(JSON.stringify({ errors: validationErrs })); | ||
res.end(); | ||
return; | ||
return [ | ||
JSON.stringify({ errors: validationErrs }), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { 'content-type': 'application/json; charset=utf-8' }, | ||
}, | ||
]; | ||
} | ||
return [ | ||
args, | ||
async function perform() { | ||
let result = operation === 'subscription' ? subscribe(args) : execute(args); | ||
const maybeResult = await (onOperation === null || onOperation === void 0 ? void 0 : onOperation(req, res, args, result)); | ||
return { | ||
ctx: args.contextValue, | ||
async perform() { | ||
const result = await (operation === 'subscription' | ||
? subscribe(args) | ||
: execute(args)); | ||
const maybeResult = await (onOperation === null || onOperation === void 0 ? void 0 : onOperation(args.contextValue, req, args, result)); | ||
if (maybeResult) | ||
result = maybeResult; | ||
return maybeResult; | ||
return result; | ||
}, | ||
]; | ||
}; | ||
} | ||
return async function handler(req, res, body) { | ||
var _a, _b, _c, _d; | ||
// authenticate first and acquire unique identification token | ||
const token = await authenticate(req, res); | ||
if (res.writableEnded) | ||
return; | ||
if (typeof token !== 'string') | ||
throw new Error('Token was not supplied'); | ||
const accept = (_a = req.headers.accept) !== null && _a !== void 0 ? _a : '*/*'; | ||
const stream = streams[token]; | ||
return async function handler(req) { | ||
var _a, _b, _c; | ||
const token = await authenticate(req); | ||
if (isResponse(token)) | ||
return token; | ||
// TODO: make accept detection more resilient | ||
const accept = req.headers.get('accept') || '*/*'; | ||
const stream = typeof token === 'string' ? streams[token] : null; | ||
if (accept === 'text/event-stream') { | ||
const maybeResponse = await (onConnect === null || onConnect === void 0 ? void 0 : onConnect(req)); | ||
if (isResponse(maybeResponse)) | ||
return maybeResponse; | ||
// if event stream is not registered, process it directly. | ||
// this means that distinct connections are used for graphql operations | ||
if (!stream) { | ||
let params; | ||
try { | ||
params = await parseReq(req, body); | ||
} | ||
catch (err) { | ||
res.writeHead(400, err.message).end(); | ||
return; | ||
} | ||
const paramsOrResponse = await parseReq(req); | ||
if (isResponse(paramsOrResponse)) | ||
return paramsOrResponse; | ||
const params = paramsOrResponse; | ||
const distinctStream = createStream(null); | ||
// reserve space for the operation | ||
distinctStream.ops[''] = null; | ||
const prepared = await prepare(req, res, params); | ||
if (res.writableEnded) | ||
return; | ||
if (!prepared) | ||
throw new Error("Operation preparation didn't respond, yet it was not prepared"); | ||
const [args, perform] = prepared; | ||
const result = await perform(); | ||
if (res.writableEnded) { | ||
if (isAsyncGenerator(result)) | ||
result.return(undefined); | ||
return; // `onOperation` responded | ||
} | ||
if (isAsyncIterable(result)) | ||
const prepared = await prepare(req, params); | ||
if (isResponse(prepared)) | ||
return prepared; | ||
const result = await prepared.perform(); | ||
if ((0, common_1.isAsyncIterable)(result)) | ||
distinctStream.ops[''] = result; | ||
await (onConnecting === null || onConnecting === void 0 ? void 0 : onConnecting(req, res)); | ||
if (res.writableEnded) | ||
return; | ||
await distinctStream.use(req, res); | ||
await distinctStream.from(req, args, result); | ||
return; | ||
distinctStream.from(prepared.ctx, req, result, null); | ||
return [ | ||
distinctStream.subscribe(), | ||
{ | ||
status: 200, | ||
statusText: 'OK', | ||
headers: { | ||
connection: 'keep-alive', | ||
'cache-control': 'no-cache', | ||
'content-encoding': 'none', | ||
'content-type': 'text/event-stream; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
// open stream cant exist, only one per token is allowed | ||
if (stream.open) { | ||
res.writeHead(409, 'Stream already open').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ errors: [{ message: 'Stream already open' }] }), | ||
{ | ||
status: 409, | ||
statusText: 'Conflict', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
await (onConnecting === null || onConnecting === void 0 ? void 0 : onConnecting(req, res)); | ||
if (res.writableEnded) | ||
return; | ||
await stream.use(req, res); | ||
return; | ||
return [ | ||
stream.subscribe(), | ||
{ | ||
status: 200, | ||
statusText: 'OK', | ||
headers: { | ||
connection: 'keep-alive', | ||
'cache-control': 'no-cache', | ||
'content-encoding': 'none', | ||
'content-type': 'text/event-stream; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
// if there us no token supplied, exclusively use the "distinct connection mode" | ||
if (typeof token !== 'string') { | ||
return [null, { status: 404, statusText: 'Not Found' }]; | ||
} | ||
// method PUT prepares a stream for future incoming connections | ||
if (req.method === 'PUT') { | ||
// method PUT prepares a stream for future incoming connections. | ||
if (!['*/*', 'text/plain'].includes(accept)) { | ||
res.writeHead(406).end(); | ||
return; | ||
return [null, { status: 406, statusText: 'Not Acceptable' }]; | ||
} | ||
// streams mustnt exist if putting new one | ||
if (stream) { | ||
res.writeHead(409, 'Stream already registered').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Stream already registered' }], | ||
}), | ||
{ | ||
status: 409, | ||
statusText: 'Conflict', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
streams[token] = createStream(token); | ||
res | ||
.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' }) | ||
// @ts-expect-error both ServerResponse and Http2ServerResponse have this write signature | ||
.write(token); | ||
res.end(); | ||
return; | ||
return [ | ||
token, | ||
{ | ||
status: 201, | ||
statusText: 'Created', | ||
headers: { | ||
'content-type': 'text/plain; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
@@ -365,152 +469,247 @@ else if (req.method === 'DELETE') { | ||
if (!stream) { | ||
res.writeHead(404, 'Stream not found').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Stream not found' }], | ||
}), | ||
{ | ||
status: 404, | ||
statusText: 'Not Found', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
const opId = new URL((_b = req.url) !== null && _b !== void 0 ? _b : '', 'http://localhost/').searchParams.get('operationId'); | ||
const opId = new URL((_a = req.url) !== null && _a !== void 0 ? _a : '', 'http://localhost/').searchParams.get('operationId'); | ||
if (!opId) { | ||
res.writeHead(400, 'Operation ID is missing').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Operation ID is missing' }], | ||
}), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
const op = stream.ops[opId]; | ||
if (isAsyncGenerator(op)) | ||
if ((0, common_1.isAsyncGenerator)(op)) | ||
op.return(undefined); | ||
delete stream.ops[opId]; // deleting the operation means no further activity should take place | ||
res.writeHead(200).end(); | ||
return; | ||
return [ | ||
null, | ||
{ | ||
status: 200, | ||
statusText: 'OK', | ||
}, | ||
]; | ||
} | ||
else if (req.method !== 'GET' && req.method !== 'POST') { | ||
// only POSTs and GETs are accepted at this point | ||
res.writeHead(405, { Allow: 'GET, POST, PUT, DELETE' }).end(); | ||
return; | ||
return [ | ||
null, | ||
{ | ||
status: 405, | ||
statusText: 'Method Not Allowed', | ||
headers: { | ||
allow: 'GET, POST, PUT, DELETE', | ||
}, | ||
}, | ||
]; | ||
} | ||
else if (!stream) { | ||
// for all other requests, streams must exist to attach the result onto | ||
res.writeHead(404, 'Stream not found').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Stream not found' }], | ||
}), | ||
{ | ||
status: 404, | ||
statusText: 'Not Found', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
if (!['*/*', 'application/graphql+json', 'application/json'].includes(accept)) { | ||
res.writeHead(406).end(); | ||
return; | ||
if (!['*/*', 'application/*', 'application/json'].includes(accept)) { | ||
return [ | ||
null, | ||
{ | ||
status: 406, | ||
statusText: 'Not Acceptable', | ||
}, | ||
]; | ||
} | ||
let params; | ||
try { | ||
params = await parseReq(req, body); | ||
} | ||
catch (err) { | ||
res.writeHead(400, err.message).end(); | ||
return; | ||
} | ||
const opId = String((_d = (_c = params.extensions) === null || _c === void 0 ? void 0 : _c.operationId) !== null && _d !== void 0 ? _d : ''); | ||
const paramsOrResponse = await parseReq(req); | ||
if (isResponse(paramsOrResponse)) | ||
return paramsOrResponse; | ||
const params = paramsOrResponse; | ||
const opId = String((_c = (_b = params.extensions) === null || _b === void 0 ? void 0 : _b.operationId) !== null && _c !== void 0 ? _c : ''); | ||
if (!opId) { | ||
res.writeHead(400, 'Operation ID is missing').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Operation ID is missing' }], | ||
}), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
if (opId in stream.ops) { | ||
res.writeHead(409, 'Operation with ID already exists').end(); | ||
return; | ||
return [ | ||
JSON.stringify({ | ||
errors: [{ message: 'Operation with ID already exists' }], | ||
}), | ||
{ | ||
status: 409, | ||
statusText: 'Conflict', | ||
headers: { | ||
'content-type': 'application/json; charset=utf-8', | ||
}, | ||
}, | ||
]; | ||
} | ||
// reserve space for the operation through ID | ||
stream.ops[opId] = null; | ||
const prepared = await prepare(req, res, params); | ||
if (res.writableEnded) | ||
return; | ||
if (!prepared) | ||
throw new Error("Operation preparation didn't respond, yet it was not prepared"); | ||
const [args, perform] = prepared; | ||
const prepared = await prepare(req, params); | ||
if (isResponse(prepared)) | ||
return prepared; | ||
// operation might have completed before prepared | ||
if (!(opId in stream.ops)) { | ||
res.writeHead(204).end(); | ||
return; | ||
return [ | ||
null, | ||
{ | ||
status: 204, | ||
statusText: 'No Content', | ||
}, | ||
]; | ||
} | ||
const result = await perform(); | ||
if (res.writableEnded) { | ||
if (isAsyncGenerator(result)) | ||
result.return(undefined); | ||
delete stream.ops[opId]; | ||
return; // `onOperation` responded | ||
} | ||
const result = await prepared.perform(); | ||
// operation might have completed before performed | ||
if (!(opId in stream.ops)) { | ||
if (isAsyncGenerator(result)) | ||
if ((0, common_1.isAsyncGenerator)(result)) | ||
result.return(undefined); | ||
res.writeHead(204).end(); | ||
return; | ||
if (!(opId in stream.ops)) { | ||
return [ | ||
null, | ||
{ | ||
status: 204, | ||
statusText: 'No Content', | ||
}, | ||
]; | ||
} | ||
} | ||
if (isAsyncIterable(result)) | ||
if ((0, common_1.isAsyncIterable)(result)) | ||
stream.ops[opId] = result; | ||
res.writeHead(202).end(); | ||
// streaming to an empty reservation is ok (will be flushed on connect) | ||
await stream.from(req, args, result, opId); | ||
stream.from(prepared.ctx, req, result, opId); | ||
return [null, { status: 202, statusText: 'Accepted' }]; | ||
}; | ||
} | ||
exports.createHandler = createHandler; | ||
async function parseReq(req, body) { | ||
async function parseReq(req) { | ||
var _a, _b, _c; | ||
const params = {}; | ||
if (req.method === 'GET') { | ||
await new Promise((resolve, reject) => { | ||
var _a, _b, _c; | ||
try { | ||
const url = new URL((_a = req.url) !== null && _a !== void 0 ? _a : '', 'http://localhost/'); | ||
params.operationName = | ||
(_b = url.searchParams.get('operationName')) !== null && _b !== void 0 ? _b : undefined; | ||
params.query = (_c = url.searchParams.get('query')) !== null && _c !== void 0 ? _c : undefined; | ||
const variables = url.searchParams.get('variables'); | ||
if (variables) | ||
params.variables = JSON.parse(variables); | ||
const extensions = url.searchParams.get('extensions'); | ||
if (extensions) | ||
params.extensions = JSON.parse(extensions); | ||
resolve(); | ||
try { | ||
switch (true) { | ||
case req.method === 'GET': { | ||
try { | ||
const [, search] = req.url.split('?'); | ||
const searchParams = new URLSearchParams(search); | ||
params.operationName = (_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined; | ||
params.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined; | ||
const variables = searchParams.get('variables'); | ||
if (variables) | ||
params.variables = JSON.parse(variables); | ||
const extensions = searchParams.get('extensions'); | ||
if (extensions) | ||
params.extensions = JSON.parse(extensions); | ||
} | ||
catch (_d) { | ||
throw new Error('Unparsable URL'); | ||
} | ||
break; | ||
} | ||
catch (_d) { | ||
reject(new Error('Unparsable URL')); | ||
} | ||
}); | ||
} | ||
else if (req.method === 'POST') { | ||
await new Promise((resolve, reject) => { | ||
const end = (body) => { | ||
try { | ||
case req.method === 'POST' && | ||
((_c = req.headers.get('content-type')) === null || _c === void 0 ? void 0 : _c.includes('application/json')): | ||
{ | ||
if (!req.body) { | ||
throw new Error('Missing body'); | ||
} | ||
const body = typeof req.body === 'function' ? await req.body() : req.body; | ||
const data = typeof body === 'string' ? JSON.parse(body) : body; | ||
if (!(0, utils_1.isObject)(data)) { | ||
throw new Error('JSON body must be an object'); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Any is ok because values will be chacked below. | ||
params.operationName = data.operationName; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Any is ok because values will be chacked below. | ||
params.query = data.query; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Any is ok because values will be chacked below. | ||
params.variables = data.variables; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Any is ok because values will be chacked below. | ||
params.extensions = data.extensions; | ||
resolve(); | ||
break; | ||
} | ||
catch (_a) { | ||
reject(new Error('Unparsable body')); | ||
} | ||
}; | ||
if (typeof body === 'string' || (0, utils_1.isObject)(body)) | ||
end(body); | ||
else { | ||
let body = ''; | ||
req.on('data', (chunk) => (body += chunk)); | ||
req.on('end', () => end(body)); | ||
} | ||
}); | ||
default: | ||
return [ | ||
null, | ||
{ | ||
status: 415, | ||
statusText: 'Unsupported Media Type', | ||
}, | ||
]; | ||
} | ||
if (params.query == null) | ||
throw new Error('Missing query'); | ||
if (typeof params.query !== 'string') | ||
throw new Error('Invalid query'); | ||
if (params.variables != null && | ||
(typeof params.variables !== 'object' || Array.isArray(params.variables))) { | ||
throw new Error('Invalid variables'); | ||
} | ||
if (params.extensions != null && | ||
(typeof params.extensions !== 'object' || | ||
Array.isArray(params.extensions))) { | ||
throw new Error('Invalid extensions'); | ||
} | ||
// request parameters are checked and now complete | ||
return params; | ||
} | ||
else | ||
throw new Error(`Unsupported method ${req.method}`); // should never happen | ||
if (!params.query) | ||
throw new Error('Missing query'); | ||
if (params.variables && typeof params.variables !== 'object') | ||
throw new Error('Invalid variables'); | ||
if (params.extensions && typeof params.extensions !== 'object') | ||
throw new Error('Invalid extensions'); | ||
return params; | ||
catch (err) { | ||
return [ | ||
JSON.stringify({ | ||
errors: [ | ||
err instanceof Error | ||
? { | ||
message: err.message, | ||
// TODO: stack might leak sensitive information | ||
// stack: err.stack, | ||
} | ||
: err, | ||
], | ||
}), | ||
{ | ||
status: 400, | ||
statusText: 'Bad Request', | ||
headers: { 'content-type': 'application/json; charset=utf-8' }, | ||
}, | ||
]; | ||
} | ||
} | ||
function isAsyncIterable(val) { | ||
return typeof Object(val)[Symbol.asyncIterator] === 'function'; | ||
function isResponse(val) { | ||
// TODO: comprehensive check | ||
return Array.isArray(val); | ||
} | ||
function isAsyncGenerator(val) { | ||
return ((0, utils_1.isObject)(val) && | ||
typeof Object(val)[Symbol.asyncIterator] === 'function' && | ||
typeof val.return === 'function' | ||
// for lazy ones, we only need the return anyway | ||
// typeof val.throw === 'function' && | ||
// typeof val.next === 'function' | ||
); | ||
function isExecutionResult(val) { | ||
// TODO: comprehensive check | ||
return (0, utils_1.isObject)(val); | ||
} | ||
exports.isAsyncGenerator = isAsyncGenerator; |
{ | ||
"name": "graphql-sse", | ||
"version": "1.3.2", | ||
"version": "2.0.0", | ||
"description": "Zero-dependency, HTTP/1 safe, simple, GraphQL over Server-Sent Events Protocol server and client", | ||
@@ -40,2 +40,27 @@ "keywords": [ | ||
}, | ||
"./lib/use/fetch": { | ||
"types": "./lib/use/fetch.d.ts", | ||
"require": "./lib/use/fetch.js", | ||
"import": "./lib/use/fetch.mjs" | ||
}, | ||
"./lib/use/http": { | ||
"types": "./lib/use/http.d.ts", | ||
"require": "./lib/use/http.js", | ||
"import": "./lib/use/http.mjs" | ||
}, | ||
"./lib/use/http2": { | ||
"types": "./lib/use/http2.d.ts", | ||
"require": "./lib/use/http2.js", | ||
"import": "./lib/use/http2.mjs" | ||
}, | ||
"./lib/use/express": { | ||
"types": "./lib/use/express.d.ts", | ||
"require": "./lib/use/express.js", | ||
"import": "./lib/use/express.mjs" | ||
}, | ||
"./lib/use/fastify": { | ||
"types": "./lib/use/fastify.d.ts", | ||
"require": "./lib/use/fastify.js", | ||
"import": "./lib/use/fastify.mjs" | ||
}, | ||
"./package.json": "./package.json" | ||
@@ -59,3 +84,5 @@ }, | ||
"gendocs": "typedoc --options typedoc.js src/", | ||
"lint": "eslint 'src'", | ||
"lint:eslint": "eslint 'src'", | ||
"lint:prettier": "prettier -c .", | ||
"lint": "yarn lint:eslint && yarn lint:prettier", | ||
"type-check": "tsc --noEmit", | ||
@@ -84,13 +111,12 @@ "test": "NODE_OPTIONS=--experimental-vm-modules jest", | ||
"@semantic-release/git": "^10.0.1", | ||
"@types/eslint": "^8.4.10", | ||
"@types/eventsource": "^1.1.10", | ||
"@types/express": "^4.17.14", | ||
"@types/express": "^4.17.15", | ||
"@types/glob": "^8.0.0", | ||
"@types/jest": "^28.1.8", | ||
"@typescript-eslint/eslint-plugin": "^5.45.1", | ||
"@typescript-eslint/parser": "^5.45.1", | ||
"babel-jest": "^28.1.3", | ||
"eslint": "^8.29.0", | ||
"@types/jest": "^29.2.4", | ||
"@typescript-eslint/eslint-plugin": "^5.47.0", | ||
"@typescript-eslint/parser": "^5.47.0", | ||
"babel-jest": "^29.3.1", | ||
"eslint": "^8.30.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"eventsource": "^2.0.2", | ||
"express": "^4.18.2", | ||
@@ -100,16 +126,11 @@ "fastify": "^4.10.2", | ||
"graphql": "^16.6.0", | ||
"jest": "^28.1.3", | ||
"jest-jasmine2": "^28.1.3", | ||
"node-fetch": "^3.3.0", | ||
"prettier": "^2.8.0", | ||
"rollup": "^3.6.0", | ||
"jest": "^29.3.1", | ||
"prettier": "^2.8.1", | ||
"rollup": "^3.7.5", | ||
"semantic-release": "^19.0.5", | ||
"tslib": "^2.4.1", | ||
"typedoc": "^0.23.21", | ||
"typedoc": "^0.23.23", | ||
"typedoc-plugin-markdown": "^3.14.0", | ||
"typescript": "^4.9.3" | ||
}, | ||
"resolutions": { | ||
"npm/chalk": "^4.1.2" | ||
"typescript": "^4.9.4" | ||
} | ||
} |
286
README.md
@@ -25,3 +25,3 @@ <div align="center"> | ||
```ts | ||
```js | ||
import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; | ||
@@ -69,15 +69,16 @@ | ||
```ts | ||
```js | ||
import http from 'http'; | ||
import { createHandler } from 'graphql-sse'; | ||
import { createHandler } from 'graphql-sse/lib/use/http'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE handler | ||
const handler = createHandler({ | ||
schema, // from the previous step | ||
}); | ||
const handler = createHandler({ schema }); | ||
// Create a HTTP server using the handler on `/graphql/stream` | ||
// Create an HTTP server using the handler on `/graphql/stream` | ||
const server = http.createServer((req, res) => { | ||
if (req.url.startsWith('/graphql/stream')) return handler(req, res); | ||
return res.writeHead(404).end(); | ||
if (req.url.startsWith('/graphql/stream')) { | ||
return handler(req, res); | ||
} | ||
res.writeHead(404).end(); | ||
}); | ||
@@ -98,24 +99,19 @@ | ||
```ts | ||
```js | ||
import fs from 'fs'; | ||
import http2 from 'http2'; | ||
import { createHandler } from 'graphql-sse'; | ||
import { createHandler } from 'graphql-sse/lib/use/http2'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE handler | ||
const handler = createHandler({ | ||
schema, // from the previous step | ||
const handler = createHandler({ schema }); | ||
// Create an HTTP server using the handler on `/graphql/stream` | ||
const server = http.createServer((req, res) => { | ||
if (req.url.startsWith('/graphql/stream')) { | ||
return handler(req, res); | ||
} | ||
res.writeHead(404).end(); | ||
}); | ||
// Create a HTTP/2 server using the handler on `/graphql/stream` | ||
const server = http2.createSecureServer( | ||
{ | ||
key: fs.readFileSync('localhost-privkey.pem'), | ||
cert: fs.readFileSync('localhost-cert.pem'), | ||
}, | ||
(req, res) => { | ||
if (req.url.startsWith('/graphql/stream')) return handler(req, res); | ||
return res.writeHead(404).end(); | ||
}, | ||
); | ||
server.listen(4000); | ||
@@ -125,7 +121,8 @@ console.log('Listening to port 4000'); | ||
##### With [`express`](https://expressjs.com/) | ||
##### With [`express`](https://expressjs.com) | ||
```ts | ||
```js | ||
import express from 'express'; // yarn add express | ||
import { createHandler } from 'graphql-sse'; | ||
import { createHandler } from 'graphql-sse/lib/use/express'; | ||
import { schema } from './previous-step'; | ||
@@ -135,15 +132,17 @@ // Create the GraphQL over SSE handler | ||
// Create an express app serving all methods on `/graphql/stream` | ||
// Create an express app | ||
const app = express(); | ||
// Serve all methods on `/graphql/stream` | ||
app.use('/graphql/stream', handler); | ||
app.listen(4000); | ||
server.listen(4000); | ||
console.log('Listening to port 4000'); | ||
``` | ||
##### With [`fastify`](https://www.fastify.io/) | ||
##### With [`fastify`](https://www.fastify.io) | ||
```ts | ||
```js | ||
import Fastify from 'fastify'; // yarn add fastify | ||
import { createHandler } from 'graphql-sse'; | ||
import { createHandler } from 'graphql-sse/lib/use/fastify'; | ||
@@ -153,19 +152,62 @@ // Create the GraphQL over SSE handler | ||
// Create a fastify instance serving all methods on `/graphql/stream` | ||
// Create a fastify app | ||
const fastify = Fastify(); | ||
fastify.all('/graphql/stream', (req, res) => | ||
handler( | ||
req.raw, | ||
res.raw, | ||
req.body, // fastify reads the body for you | ||
), | ||
); | ||
fastify.listen(4000); | ||
// Serve all methods on `/graphql/stream` | ||
fastify.all('/graphql/stream', handler); | ||
fastify.listen({ port: 4000 }); | ||
console.log('Listening to port 4000'); | ||
``` | ||
##### With [`Deno`](https://deno.land/) | ||
```ts | ||
import { serve } from 'https://deno.land/std/http/server.ts'; | ||
import { createHandler } from 'https://esm.sh/graphql-sse/lib/use/fetch'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE native fetch handler | ||
const handler = createHandler({ schema }); | ||
// Serve on `/graphql/stream` using the handler | ||
await serve( | ||
(req: Request) => { | ||
const [path, _search] = req.url.split('?'); | ||
if (path.endsWith('/graphql/stream')) { | ||
return await handler(req); | ||
} | ||
return new Response(null, { status: 404 }); | ||
}, | ||
{ | ||
port: 4000, // Listening to port 4000 | ||
}, | ||
); | ||
``` | ||
##### With [`Bun`](https://bun.sh/) | ||
```js | ||
import { createHandler } from 'graphql-sse/lib/use/fetch'; // bun install graphql-sse | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE native fetch handler | ||
const handler = createHandler({ schema }); | ||
// Serve on `/graphql/stream` using the handler | ||
export default { | ||
port: 4000, // Listening to port 4000 | ||
async fetch(req) { | ||
const [path, _search] = req.url.split('?'); | ||
if (path.endsWith('/graphql/stream')) { | ||
return await handler(req); | ||
} | ||
return new Response(null, { status: 404 }); | ||
}, | ||
}; | ||
``` | ||
#### Use the client | ||
```ts | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
@@ -230,3 +272,3 @@ | ||
```ts | ||
import { createClient, SubscribePayload } from 'graphql-sse'; | ||
import { createClient, RequestParams } from 'graphql-sse'; | ||
@@ -237,3 +279,3 @@ const client = createClient({ | ||
async function execute<T>(payload: SubscribePayload) { | ||
export async function execute<T>(payload: RequestParams) { | ||
return new Promise<T>((resolve, reject) => { | ||
@@ -268,4 +310,4 @@ let result: T; | ||
```ts | ||
import { createClient, SubscribePayload } from 'graphql-sse'; | ||
```js | ||
import { createClient, RequestParams } from 'graphql-sse'; | ||
@@ -276,11 +318,8 @@ const client = createClient({ | ||
function subscribe<T>(payload: SubscribePayload): AsyncGenerator<T> { | ||
let deferred: { | ||
resolve: (done: boolean) => void; | ||
reject: (err: unknown) => void; | ||
} | null = null; | ||
const pending: T[] = []; | ||
let throwMe: unknown = null, | ||
export function subscribe(payload) { | ||
let deferred = null; | ||
const pending = []; | ||
let throwMe = null, | ||
done = false; | ||
const dispose = client.subscribe<T>(payload, { | ||
const dispose = client.subscribe(payload, { | ||
next: (data) => { | ||
@@ -306,13 +345,17 @@ pending.push(data); | ||
if (throwMe) throw throwMe; | ||
if (pending.length) return { value: pending.shift()! }; | ||
return (await new Promise<boolean>( | ||
if (pending.length) return { value: pending.shift() }; | ||
return (await new Promise( | ||
(resolve, reject) => (deferred = { resolve, reject }), | ||
)) | ||
? { done: true, value: undefined } | ||
: { value: pending.shift()! }; | ||
: { value: pending.shift() }; | ||
}, | ||
async throw(err) { | ||
throw err; | ||
throwMe = err; | ||
deferred?.reject(throwMe); | ||
return { done: true, value: undefined }; | ||
}, | ||
async return() { | ||
done = true; | ||
deferred?.resolve(true); | ||
dispose(); | ||
@@ -342,3 +385,3 @@ return { done: true, value: undefined }; | ||
```ts | ||
```js | ||
import { Observable } from 'relay-runtime'; | ||
@@ -357,3 +400,3 @@ // or | ||
function toObservable(operation) { | ||
export function toObservable(operation) { | ||
return new Observable((observer) => | ||
@@ -386,3 +429,3 @@ client.subscribe(operation, { | ||
```ts | ||
```js | ||
import { GraphQLError } from 'graphql'; | ||
@@ -433,11 +476,11 @@ import { | ||
```ts | ||
```js | ||
import { createClient, defaultExchanges, subscriptionExchange } from 'urql'; | ||
import { createClient as createWSClient } from 'graphql-sse'; | ||
import { createClient as createSSEClient } from 'graphql-sse'; | ||
const sseClient = createWSClient({ | ||
const sseClient = createSSEClient({ | ||
url: 'http://its.urql:4000/graphql/stream', | ||
}); | ||
const client = createClient({ | ||
export const client = createClient({ | ||
url: '/graphql/stream', | ||
@@ -467,3 +510,3 @@ exchanges: [ | ||
```typescript | ||
```ts | ||
import { | ||
@@ -500,3 +543,3 @@ ApolloLink, | ||
const link = new SSELink({ | ||
export const link = new SSELink({ | ||
url: 'http://where.is:4000/graphql/stream', | ||
@@ -518,6 +561,6 @@ headers: () => { | ||
```typescript | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
const client = createClient({ | ||
export const client = createClient({ | ||
singleConnection: true, // this is literally it 😄 | ||
@@ -539,3 +582,3 @@ url: 'http://use.single:4000/connection/graphql/stream', | ||
```typescript | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
@@ -546,3 +589,3 @@ import { waitForHealthy } from './my-servers'; | ||
const client = createClient({ | ||
export const client = createClient({ | ||
url, | ||
@@ -568,6 +611,6 @@ retryWait: async function waitForServerHealthyBeforeRetry() { | ||
```typescript | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
const client = createClient({ | ||
export const client = createClient({ | ||
url: 'http://let-me-see.messages:4000/graphql/stream', | ||
@@ -611,3 +654,3 @@ onMessage: console.log, | ||
```ts | ||
```js | ||
const ws = require('ws'); // yarn add ws | ||
@@ -619,3 +662,3 @@ const fetch = require('node-fetch'); // yarn add node-fetch | ||
const client = createClient({ | ||
export const client = createClient({ | ||
url: 'http://no.browser:4000/graphql/stream', | ||
@@ -633,4 +676,2 @@ fetchFn: fetch, | ||
}); | ||
// consider other recipes for usage inspiration | ||
``` | ||
@@ -643,3 +684,3 @@ | ||
```typescript | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
@@ -653,6 +694,6 @@ import { | ||
const handler = createHandler({ | ||
export const handler = createHandler({ | ||
schema, | ||
authenticate: async (req, res) => { | ||
let token = req.headers['x-graphql-event-stream-token']; | ||
authenticate: async (req) => { | ||
let token = req.headers.get('x-graphql-event-stream-token'); | ||
if (token) { | ||
@@ -676,3 +717,3 @@ // When the client is working in a "single connection mode" | ||
// or | ||
token = processAuthorizationHeader(req.headers['authorization']); | ||
token = processAuthorizationHeader(req.headers.get('authorization')); | ||
// or | ||
@@ -683,3 +724,5 @@ token = await customAuthenticationTokenDiscovery(req); | ||
// authentication issues however he sees fit. | ||
if (!token) return res.writeHead(401, 'Unauthorized').end(); | ||
if (!token) { | ||
return [null, { status: 401, statusText: 'Unauthorized' }]; | ||
} | ||
@@ -691,3 +734,6 @@ // Clients that operate in "distinct connections mode" dont | ||
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#distinct-connections-mode | ||
if (req.method === 'POST' && req.headers.accept === 'text/event-stream') { | ||
if ( | ||
req.method === 'POST' && | ||
req.headers.get('accept') === 'text/event-stream' | ||
) { | ||
// "distinct connections mode" requests an event-stream with a POST | ||
@@ -707,4 +753,2 @@ // method. These two checks, together with the lack of `X-GraphQL-Event-Stream-Token` | ||
}); | ||
// use `handler` with your favourite http library | ||
``` | ||
@@ -717,7 +761,7 @@ | ||
```typescript | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema, checkIsAdmin, getDebugSchema } from './my-graphql'; | ||
const handler = createHandler({ | ||
export const handler = createHandler({ | ||
schema: async (req, executionArgsWithoutSchema) => { | ||
@@ -732,4 +776,2 @@ // will be called on every subscribe request | ||
}); | ||
// use `handler` with your favourite http library | ||
``` | ||
@@ -742,15 +784,13 @@ | ||
```typescript | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema, getDynamicContext } from './my-graphql'; | ||
const handler = createHandler({ | ||
export const handler = createHandler({ | ||
schema, | ||
// or static context by supplying the value direcly | ||
context: async (req, args) => { | ||
context: (req, args) => { | ||
return getDynamicContext(req, args); | ||
}, | ||
}); | ||
// use `handler` with your favourite http library | ||
``` | ||
@@ -763,3 +803,3 @@ | ||
```typescript | ||
```js | ||
import { parse } from 'graphql'; | ||
@@ -769,18 +809,15 @@ import { createHandler } from 'graphql-sse'; | ||
const handler = createHandler({ | ||
onSubscribe: async (req, _res, params) => { | ||
export const handler = createHandler({ | ||
onSubscribe: async (req, params) => { | ||
const schema = await getSchema(req); | ||
const args = { | ||
return { | ||
schema, | ||
operationName: params.operationName, | ||
document: parse(params.query), | ||
document: | ||
typeof params.query === 'string' ? parse(params.query) : params.query, | ||
variableValues: params.variables, | ||
contextValue: undefined, | ||
}; | ||
return args; | ||
}, | ||
}); | ||
// use `handler` with your favourite http library | ||
``` | ||
@@ -793,3 +830,3 @@ | ||
```typescript | ||
```ts | ||
// 🛸 server | ||
@@ -799,3 +836,3 @@ | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema } from './my-graphql-schema'; | ||
import { schema } from './my-graphql'; | ||
@@ -814,24 +851,21 @@ // a unique GraphQL execution ID used for representing | ||
const handler = createHandler( | ||
{ | ||
onSubscribe: (req, res, params) => { | ||
const persistedQuery = queriesStore[params.extensions?.persistedQuery]; | ||
if (persistedQuery) { | ||
return { | ||
...persistedQuery, | ||
variableValues: params.variables, // use the variables from the client | ||
}; | ||
} | ||
export const handler = createHandler({ | ||
onSubscribe: (_req, params) => { | ||
const persistedQuery = | ||
queriesStore[String(params.extensions?.persistedQuery)]; | ||
if (persistedQuery) { | ||
return { | ||
...persistedQuery, | ||
variableValues: params.variables, // use the variables from the client | ||
contextValue: undefined, | ||
}; | ||
} | ||
// for extra security only allow the queries from the store | ||
return res.writeHead(404, 'Query Not Found').end(); | ||
}, | ||
// for extra security only allow the queries from the store | ||
return [null, { status: 404, statusText: 'Not Found' }]; | ||
}, | ||
wsServer, | ||
); | ||
// use `handler` with your favourite http library | ||
}); | ||
``` | ||
```typescript | ||
```ts | ||
// 📺 client | ||
@@ -838,0 +872,0 @@ |
@@ -60,2 +60,12 @@ (function (global, factory) { | ||
* | ||
* utils | ||
* | ||
*/ | ||
/** @private */ | ||
function isObject(val) { | ||
return typeof val === 'object' && val !== null; | ||
} | ||
/** | ||
* | ||
* common | ||
@@ -90,2 +100,10 @@ * | ||
/** @category Common */ | ||
function print(msg) { | ||
let str = `event: ${msg.event}`; | ||
if (msg.data) | ||
str += `\ndata: ${JSON.stringify(msg.data)}`; | ||
str += '\n\n'; | ||
return str; | ||
} | ||
/** @category Common */ | ||
function parseStreamData(e, data) { | ||
@@ -104,2 +122,22 @@ if (data) { | ||
} | ||
/** | ||
* Checkes whether the provided value is an async iterable. | ||
* | ||
* @category Common | ||
*/ | ||
function isAsyncIterable(val) { | ||
return typeof Object(val)[Symbol.asyncIterator] === 'function'; | ||
} | ||
/** | ||
* Checkes whether the provided value is an async generator. | ||
* | ||
* @category Common | ||
*/ | ||
function isAsyncGenerator(val) { | ||
return (isObject(val) && | ||
typeof Object(val)[Symbol.asyncIterator] === 'function' && | ||
typeof val.return === 'function' && | ||
typeof val.throw === 'function' && | ||
typeof val.next === 'function'); | ||
} | ||
@@ -233,12 +271,2 @@ /** | ||
* | ||
* utils | ||
* | ||
*/ | ||
/** @private */ | ||
function isObject(val) { | ||
return typeof val === 'object' && val !== null; | ||
} | ||
/** | ||
* | ||
* client | ||
@@ -855,5 +883,8 @@ * | ||
exports.createClient = createClient; | ||
exports.isAsyncGenerator = isAsyncGenerator; | ||
exports.isAsyncIterable = isAsyncIterable; | ||
exports.parseStreamData = parseStreamData; | ||
exports.print = print; | ||
exports.validateStreamEvent = validateStreamEvent; | ||
})); |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlSse={})}(this,(function(e){"use strict";function t(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function r(e){return this instanceof r?(this.v=e,this):new r(e)}function n(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var o,a=n.apply(e,t||[]),i=[];return o={},s("next"),s("throw"),s("return"),o[Symbol.asyncIterator]=function(){return this},o;function s(e){a[e]&&(o[e]=function(t){return new Promise((function(r,n){i.push([e,t,r,n])>1||c(e,t)}))})}function c(e,t){try{(n=a[e](t)).value instanceof r?Promise.resolve(n.value.v).then(l,f):u(i[0][2],n)}catch(e){u(i[0][3],e)}var n}function l(e){c("next",e)}function f(e){c("throw",e)}function u(e,t){e(t),i.shift(),i.length&&c(i[0][0],i[0][1])}}function o(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r,n=e[Symbol.asyncIterator];return n?n.call(e):(e=t(e),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(t){r[t]=e[t]&&function(r){return new Promise((function(n,o){(function(e,t,r,n){Promise.resolve(n).then((function(t){e({value:t,done:r})}),t)})(n,o,(r=e[t](r)).done,r.value)}))}}}const a="x-graphql-event-stream-token";function i(e){if("next"!==e&&"complete"!==e)throw new Error(`Invalid stream event "${e}"`);return e}function s(e,t){if(t)try{t=JSON.parse(t)}catch(e){throw new Error("Invalid stream data")}if("next"===e&&!t)throw new Error('Stream data must be an object for "next" events');return t||null}var c;!function(e){e[e.NewLine=10]="NewLine",e[e.CchunkiageReturn=13]="CchunkiageReturn",e[e.Space=32]="Space",e[e.Colon=58]="Colon"}(c||(c={}));class l extends Error{constructor(e){let t,r;var n;(function(e){return"object"==typeof e&&null!==e})(n=e)&&"boolean"==typeof n.ok&&"number"==typeof n.status&&"string"==typeof n.statusText?(r=e,t="Server responded with "+e.status+": "+e.statusText):t=e instanceof Error?e.message:String(e),super(t),this.name=this.constructor.name,this.response=r}}async function f(e){const{signal:t,url:a,credentials:f,headers:u,body:d,referrer:h,referrerPolicy:y,fetchFn:w,onMessage:b}=e,p={},g={};let v;try{v=await w(a,{signal:t,method:d?"POST":"GET",credentials:f,referrer:h,referrerPolicy:y,headers:Object.assign(Object.assign({},u),{accept:"text/event-stream"}),body:d})}catch(e){throw new l(e)}if(!v.ok)throw new l(v);if(!v.body)throw new Error("Missing response body");let x,m=null;return(async()=>{var e,t,a,f,u;try{const l=function(){let e,t,r,n=!1,o={event:"",data:""},a=[];const l=new TextDecoder;return function(f){if(void 0===e)e=f,t=0,r=-1;else{const t=new Uint8Array(e.length+f.length);t.set(e),t.set(f,e.length),e=t}const u=e.length;let d=0;for(;t<u;){n&&(e[t]===c.NewLine&&(d=++t),n=!1);let f=-1;for(;t<u&&-1===f;++t)switch(e[t]){case c.Colon:-1===r&&(r=t-d);break;case c.CchunkiageReturn:n=!0;case c.NewLine:f=t}if(-1===f)break;if(d===f){if(o.event||o.data){if(!o.event)throw new Error("Missing message event");const e=i(o.event),t=s(e,o.data);a.push({event:e,data:t}),o={event:"",data:""}}}else if(r>0){const t=e.subarray(d,f),n=l.decode(t.subarray(0,r)),a=r+(t[r+1]===c.Space?2:1),i=l.decode(t.subarray(a));switch(n){case"event":o.event=i;break;case"data":o.data=o.data?o.data+"\n"+i:i}}d=t,r=-1}if(d===u){e=void 0;const t=[...a];return a=[],t}0!==d&&(e=e.subarray(d),t-=d)}}();try{for(var d,h=!0,y=o(function(e){if("function"==typeof Object(e)[Symbol.asyncIterator])return e[Symbol.asyncIterator]();return function(){return n(this,arguments,(function*(){const t=e.getReader();let n;do{n=yield r(t.read()),void 0!==n.value&&(yield yield r(n.value))}while(!n.done)}))}()}(v.body));!(e=(d=await y.next()).done);){f=d.value,h=!1;try{const e=f;if("string"==typeof e)throw m=new Error(`Unexpected string chunk "${e}"`);let t;try{t=l(e)}catch(e){throw m=e}if(!t)continue;for(const e of t){try{null==b||b(e)}catch(e){throw m=e}const t=e.data&&"id"in e.data?e.data.id:"";switch(t in g||(g[t]=[]),e.event){case"next":t?g[t].push(e.data.payload):g[t].push(e.data);break;case"complete":g[t].push("complete");break;default:throw m=new Error(`Unexpected message event "${e.event}"`)}null===(u=p[t])||void 0===u||u.proceed()}}finally{h=!0}}}catch(e){t={error:e}}finally{try{h||e||!(a=y.return)||await a.call(y)}finally{if(t)throw t.error}}if(Object.keys(p).length)throw new Error("Connection closed while having active streams")}catch(e){m=!m&&Object.keys(p).length?new l(e):e,null==x||x(m)}finally{Object.values(p).forEach((({proceed:e})=>e()))}})(),{url:a,headers:u,waitForThrow:()=>new Promise(((e,t)=>{if(m)return t(m);x=t})),getResults(e){var t;return n(this,arguments,(function*(){const{signal:n,operationId:o=""}=null!=e?e:{};try{for(;;){for(;null===(t=g[o])||void 0===t?void 0:t.length;){const e=g[o].shift();if("complete"===e)return yield r(void 0);yield yield r(e)}if(m)throw m;if(null==n?void 0:n.aborted)throw new Error("Getting results aborted by the client");yield r(new Promise((e=>{const t=()=>{null==n||n.removeEventListener("abort",t),delete p[o],e()};null==n||n.addEventListener("abort",t),p[o]={proceed:t}})))}}finally{delete g[o]}}))}}}e.NetworkError=l,e.TOKEN_HEADER_KEY=a,e.TOKEN_QUERY_KEY="token",e.createClient=function(e){const{singleConnection:t=!1,lazy:r=!0,lazyCloseTimeout:n=0,onNonLazyError:i=console.error,generateID:s=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(e=>{const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))},retryAttempts:c=5,retry:u=async function(e){let t=1e3;for(let r=0;r<e;r++)t*=2;await new Promise((e=>setTimeout(e,t+Math.floor(2700*Math.random()+300))))},credentials:d="same-origin",referrer:h,referrerPolicy:y,onMessage:w}=e,b=e.fetchFn||fetch,p=e.abortControllerImpl||AbortController,g=(()=>{let e=!1;const t=[];return{get disposed(){return e},onDispose:r=>e?(setTimeout((()=>r()),0),()=>{}):(t.push(r),()=>{t.splice(t.indexOf(r),1)}),dispose(){if(!e){e=!0;for(const e of[...t])e()}}}})();let v,x,m=0,E=null,S=0;async function O(){try{if(g.disposed)throw new Error("Client has been disposed");return await(null!=x?x:x=(async()=>{var t;if(E){if(await u(S),v.signal.aborted)throw new Error("Connection aborted by the client");S++}v=new p;const r=g.onDispose((()=>v.abort()));v.signal.addEventListener("abort",(()=>{r(),x=void 0}));const n="function"==typeof e.url?await e.url():e.url;if(v.signal.aborted)throw new Error("Connection aborted by the client");const o="function"==typeof e.headers?await e.headers():null!==(t=e.headers)&&void 0!==t?t:{};if(v.signal.aborted)throw new Error("Connection aborted by the client");let i;try{i=await b(n,{signal:v.signal,method:"PUT",credentials:d,referrer:h,referrerPolicy:y,headers:o})}catch(e){throw new l(e)}if(201!==i.status)throw new l(i);const s=await i.text();o[a]=s;const c=await f({signal:v.signal,headers:o,credentials:d,referrer:h,referrerPolicy:y,url:n,fetchFn:b,onMessage:w});return E=null,S=0,c.waitForThrow().catch((()=>x=void 0)),c})())}catch(e){throw x=void 0,e}}return t&&!r&&(async()=>{for(m++;;)try{const{waitForThrow:e}=await O();await e()}catch(e){if(g.disposed)return;if(!(e instanceof l))return null==i?void 0:i(e);if(x=void 0,!c||S>=c)return null==i?void 0:i(e);E=e}})(),{subscribe(a,i){if(!t){const t=new p,r=g.onDispose((()=>{r(),t.abort()}));return(async()=>{var r,n,s,p,g;let v=null,x=0;for(;;)try{if(v){if(await u(x),t.signal.aborted)throw new Error("Connection aborted by the client");x++}const c="function"==typeof e.url?await e.url():e.url;if(t.signal.aborted)throw new Error("Connection aborted by the client");const l="function"==typeof e.headers?await e.headers():null!==(g=e.headers)&&void 0!==g?g:{};if(t.signal.aborted)throw new Error("Connection aborted by the client");const{getResults:O}=await f({signal:t.signal,headers:Object.assign(Object.assign({},l),{"content-type":"application/json; charset=utf-8"}),credentials:d,referrer:h,referrerPolicy:y,url:c,body:JSON.stringify(a),fetchFn:b,onMessage:w});v=null,x=0;try{for(var m,E=!0,S=(n=void 0,o(O()));!(r=(m=await S.next()).done);){p=m.value,E=!1;try{const e=p;i.next(e)}finally{E=!0}}}catch(e){n={error:e}}finally{try{E||r||!(s=S.return)||await s.call(S)}finally{if(n)throw n.error}}return t.abort()}catch(e){if(t.signal.aborted)return;if(!(e instanceof l))throw e;if(!c||x>=c)throw e;v=e}})().then((()=>i.complete())).catch((e=>i.error(e))),()=>t.abort()}m++;const T=new p,j=g.onDispose((()=>{j(),T.abort()}));return(async()=>{var e,t,f,u;const w=s();a=Object.assign(Object.assign({},a),{extensions:Object.assign(Object.assign({},a.extensions),{operationId:w})});let j=null;for(;;){j=null;try{const{url:r,headers:s,getResults:c}=await O();let x;try{x=await b(r,{signal:T.signal,method:"POST",credentials:d,referrer:h,referrerPolicy:y,headers:Object.assign(Object.assign({},s),{"content-type":"application/json; charset=utf-8"}),body:JSON.stringify(a)})}catch(e){throw new l(e)}if(202!==x.status)throw new l(x);j=async()=>{let e;try{const t=new p,n=g.onDispose((()=>{n(),t.abort()}));e=await b(r+"?operationId="+w,{signal:t.signal,method:"DELETE",credentials:d,referrer:h,referrerPolicy:y,headers:s})}catch(e){throw new l(e)}if(200!==e.status)throw new l(e)};try{for(var C,P=!0,I=(t=void 0,o(c({signal:T.signal,operationId:w})));!(e=(C=await I.next()).done);){u=C.value,P=!1;try{const e=u;i.next(e)}finally{P=!0}}}catch(e){t={error:e}}finally{try{P||e||!(f=I.return)||await f.call(I)}finally{if(t)throw t.error}}return j=null,T.abort()}catch(e){if(T.signal.aborted)return await(null==j?void 0:j());if(!(e instanceof l))throw T.abort(),e;if(r&&(x=void 0),!c||S>=c)throw T.abort(),e;E=e}finally{T.signal.aborted&&0==--m&&(isFinite(n)&&n>0?setTimeout((()=>{m||v.abort()}),n):v.abort())}}})().then((()=>i.complete())).catch((e=>i.error(e))),()=>T.abort()},dispose(){g.dispose()}}},e.parseStreamData=s,e.validateStreamEvent=i})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlSse={})}(this,(function(e){"use strict";function t(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function r(e){return this instanceof r?(this.v=e,this):new r(e)}function n(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var o,a=n.apply(e,t||[]),i=[];return o={},s("next"),s("throw"),s("return"),o[Symbol.asyncIterator]=function(){return this},o;function s(e){a[e]&&(o[e]=function(t){return new Promise((function(r,n){i.push([e,t,r,n])>1||c(e,t)}))})}function c(e,t){try{(n=a[e](t)).value instanceof r?Promise.resolve(n.value.v).then(l,f):u(i[0][2],n)}catch(e){u(i[0][3],e)}var n}function l(e){c("next",e)}function f(e){c("throw",e)}function u(e,t){e(t),i.shift(),i.length&&c(i[0][0],i[0][1])}}function o(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r,n=e[Symbol.asyncIterator];return n?n.call(e):(e=t(e),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(t){r[t]=e[t]&&function(r){return new Promise((function(n,o){(function(e,t,r,n){Promise.resolve(n).then((function(t){e({value:t,done:r})}),t)})(n,o,(r=e[t](r)).done,r.value)}))}}}function a(e){return"object"==typeof e&&null!==e}const i="x-graphql-event-stream-token";function s(e){if("next"!==e&&"complete"!==e)throw new Error(`Invalid stream event "${e}"`);return e}function c(e,t){if(t)try{t=JSON.parse(t)}catch(e){throw new Error("Invalid stream data")}if("next"===e&&!t)throw new Error('Stream data must be an object for "next" events');return t||null}var l;!function(e){e[e.NewLine=10]="NewLine",e[e.CchunkiageReturn=13]="CchunkiageReturn",e[e.Space=32]="Space",e[e.Colon=58]="Colon"}(l||(l={}));class f extends Error{constructor(e){let t,r;var n;a(n=e)&&"boolean"==typeof n.ok&&"number"==typeof n.status&&"string"==typeof n.statusText?(r=e,t="Server responded with "+e.status+": "+e.statusText):t=e instanceof Error?e.message:String(e),super(t),this.name=this.constructor.name,this.response=r}}async function u(e){const{signal:t,url:a,credentials:i,headers:u,body:d,referrer:h,referrerPolicy:y,fetchFn:w,onMessage:b}=e,p={},g={};let v;try{v=await w(a,{signal:t,method:d?"POST":"GET",credentials:i,referrer:h,referrerPolicy:y,headers:Object.assign(Object.assign({},u),{accept:"text/event-stream"}),body:d})}catch(e){throw new f(e)}if(!v.ok)throw new f(v);if(!v.body)throw new Error("Missing response body");let x,m=null;return(async()=>{var e,t,a,i,u;try{const f=function(){let e,t,r,n=!1,o={event:"",data:""},a=[];const i=new TextDecoder;return function(f){if(void 0===e)e=f,t=0,r=-1;else{const t=new Uint8Array(e.length+f.length);t.set(e),t.set(f,e.length),e=t}const u=e.length;let d=0;for(;t<u;){n&&(e[t]===l.NewLine&&(d=++t),n=!1);let f=-1;for(;t<u&&-1===f;++t)switch(e[t]){case l.Colon:-1===r&&(r=t-d);break;case l.CchunkiageReturn:n=!0;case l.NewLine:f=t}if(-1===f)break;if(d===f){if(o.event||o.data){if(!o.event)throw new Error("Missing message event");const e=s(o.event),t=c(e,o.data);a.push({event:e,data:t}),o={event:"",data:""}}}else if(r>0){const t=e.subarray(d,f),n=i.decode(t.subarray(0,r)),a=r+(t[r+1]===l.Space?2:1),s=i.decode(t.subarray(a));switch(n){case"event":o.event=s;break;case"data":o.data=o.data?o.data+"\n"+s:s}}d=t,r=-1}if(d===u){e=void 0;const t=[...a];return a=[],t}0!==d&&(e=e.subarray(d),t-=d)}}();try{for(var d,h=!0,y=o(function(e){if("function"==typeof Object(e)[Symbol.asyncIterator])return e[Symbol.asyncIterator]();return function(){return n(this,arguments,(function*(){const t=e.getReader();let n;do{n=yield r(t.read()),void 0!==n.value&&(yield yield r(n.value))}while(!n.done)}))}()}(v.body));!(e=(d=await y.next()).done);){i=d.value,h=!1;try{const e=i;if("string"==typeof e)throw m=new Error(`Unexpected string chunk "${e}"`);let t;try{t=f(e)}catch(e){throw m=e}if(!t)continue;for(const e of t){try{null==b||b(e)}catch(e){throw m=e}const t=e.data&&"id"in e.data?e.data.id:"";switch(t in g||(g[t]=[]),e.event){case"next":t?g[t].push(e.data.payload):g[t].push(e.data);break;case"complete":g[t].push("complete");break;default:throw m=new Error(`Unexpected message event "${e.event}"`)}null===(u=p[t])||void 0===u||u.proceed()}}finally{h=!0}}}catch(e){t={error:e}}finally{try{h||e||!(a=y.return)||await a.call(y)}finally{if(t)throw t.error}}if(Object.keys(p).length)throw new Error("Connection closed while having active streams")}catch(e){m=!m&&Object.keys(p).length?new f(e):e,null==x||x(m)}finally{Object.values(p).forEach((({proceed:e})=>e()))}})(),{url:a,headers:u,waitForThrow:()=>new Promise(((e,t)=>{if(m)return t(m);x=t})),getResults(e){var t;return n(this,arguments,(function*(){const{signal:n,operationId:o=""}=null!=e?e:{};try{for(;;){for(;null===(t=g[o])||void 0===t?void 0:t.length;){const e=g[o].shift();if("complete"===e)return yield r(void 0);yield yield r(e)}if(m)throw m;if(null==n?void 0:n.aborted)throw new Error("Getting results aborted by the client");yield r(new Promise((e=>{const t=()=>{null==n||n.removeEventListener("abort",t),delete p[o],e()};null==n||n.addEventListener("abort",t),p[o]={proceed:t}})))}}finally{delete g[o]}}))}}}e.NetworkError=f,e.TOKEN_HEADER_KEY=i,e.TOKEN_QUERY_KEY="token",e.createClient=function(e){const{singleConnection:t=!1,lazy:r=!0,lazyCloseTimeout:n=0,onNonLazyError:a=console.error,generateID:s=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(e=>{const t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16)}))},retryAttempts:c=5,retry:l=async function(e){let t=1e3;for(let r=0;r<e;r++)t*=2;await new Promise((e=>setTimeout(e,t+Math.floor(2700*Math.random()+300))))},credentials:d="same-origin",referrer:h,referrerPolicy:y,onMessage:w}=e,b=e.fetchFn||fetch,p=e.abortControllerImpl||AbortController,g=(()=>{let e=!1;const t=[];return{get disposed(){return e},onDispose:r=>e?(setTimeout((()=>r()),0),()=>{}):(t.push(r),()=>{t.splice(t.indexOf(r),1)}),dispose(){if(!e){e=!0;for(const e of[...t])e()}}}})();let v,x,m=0,E=null,S=0;async function O(){try{if(g.disposed)throw new Error("Client has been disposed");return await(null!=x?x:x=(async()=>{var t;if(E){if(await l(S),v.signal.aborted)throw new Error("Connection aborted by the client");S++}v=new p;const r=g.onDispose((()=>v.abort()));v.signal.addEventListener("abort",(()=>{r(),x=void 0}));const n="function"==typeof e.url?await e.url():e.url;if(v.signal.aborted)throw new Error("Connection aborted by the client");const o="function"==typeof e.headers?await e.headers():null!==(t=e.headers)&&void 0!==t?t:{};if(v.signal.aborted)throw new Error("Connection aborted by the client");let a;try{a=await b(n,{signal:v.signal,method:"PUT",credentials:d,referrer:h,referrerPolicy:y,headers:o})}catch(e){throw new f(e)}if(201!==a.status)throw new f(a);const s=await a.text();o[i]=s;const c=await u({signal:v.signal,headers:o,credentials:d,referrer:h,referrerPolicy:y,url:n,fetchFn:b,onMessage:w});return E=null,S=0,c.waitForThrow().catch((()=>x=void 0)),c})())}catch(e){throw x=void 0,e}}return t&&!r&&(async()=>{for(m++;;)try{const{waitForThrow:e}=await O();await e()}catch(e){if(g.disposed)return;if(!(e instanceof f))return null==a?void 0:a(e);if(x=void 0,!c||S>=c)return null==a?void 0:a(e);E=e}})(),{subscribe(a,i){if(!t){const t=new p,r=g.onDispose((()=>{r(),t.abort()}));return(async()=>{var r,n,s,p,g;let v=null,x=0;for(;;)try{if(v){if(await l(x),t.signal.aborted)throw new Error("Connection aborted by the client");x++}const c="function"==typeof e.url?await e.url():e.url;if(t.signal.aborted)throw new Error("Connection aborted by the client");const f="function"==typeof e.headers?await e.headers():null!==(g=e.headers)&&void 0!==g?g:{};if(t.signal.aborted)throw new Error("Connection aborted by the client");const{getResults:O}=await u({signal:t.signal,headers:Object.assign(Object.assign({},f),{"content-type":"application/json; charset=utf-8"}),credentials:d,referrer:h,referrerPolicy:y,url:c,body:JSON.stringify(a),fetchFn:b,onMessage:w});v=null,x=0;try{for(var m,E=!0,S=(n=void 0,o(O()));!(r=(m=await S.next()).done);){p=m.value,E=!1;try{const e=p;i.next(e)}finally{E=!0}}}catch(e){n={error:e}}finally{try{E||r||!(s=S.return)||await s.call(S)}finally{if(n)throw n.error}}return t.abort()}catch(e){if(t.signal.aborted)return;if(!(e instanceof f))throw e;if(!c||x>=c)throw e;v=e}})().then((()=>i.complete())).catch((e=>i.error(e))),()=>t.abort()}m++;const j=new p,T=g.onDispose((()=>{T(),j.abort()}));return(async()=>{var e,t,l,u;const w=s();a=Object.assign(Object.assign({},a),{extensions:Object.assign(Object.assign({},a.extensions),{operationId:w})});let T=null;for(;;){T=null;try{const{url:r,headers:s,getResults:c}=await O();let x;try{x=await b(r,{signal:j.signal,method:"POST",credentials:d,referrer:h,referrerPolicy:y,headers:Object.assign(Object.assign({},s),{"content-type":"application/json; charset=utf-8"}),body:JSON.stringify(a)})}catch(e){throw new f(e)}if(202!==x.status)throw new f(x);T=async()=>{let e;try{const t=new p,n=g.onDispose((()=>{n(),t.abort()}));e=await b(r+"?operationId="+w,{signal:t.signal,method:"DELETE",credentials:d,referrer:h,referrerPolicy:y,headers:s})}catch(e){throw new f(e)}if(200!==e.status)throw new f(e)};try{for(var I,C=!0,P=(t=void 0,o(c({signal:j.signal,operationId:w})));!(e=(I=await P.next()).done);){u=I.value,C=!1;try{const e=u;i.next(e)}finally{C=!0}}}catch(e){t={error:e}}finally{try{C||e||!(l=P.return)||await l.call(P)}finally{if(t)throw t.error}}return T=null,j.abort()}catch(e){if(j.signal.aborted)return await(null==T?void 0:T());if(!(e instanceof f))throw j.abort(),e;if(r&&(x=void 0),!c||S>=c)throw j.abort(),e;E=e}finally{j.signal.aborted&&0==--m&&(isFinite(n)&&n>0?setTimeout((()=>{m||v.abort()}),n):v.abort())}}})().then((()=>i.complete())).catch((e=>i.error(e))),()=>j.abort()},dispose(){g.dispose()}}},e.isAsyncGenerator=function(e){return a(e)&&"function"==typeof Object(e)[Symbol.asyncIterator]&&"function"==typeof e.return&&"function"==typeof e.throw&&"function"==typeof e.next},e.isAsyncIterable=function(e){return"function"==typeof Object(e)[Symbol.asyncIterator]},e.parseStreamData=c,e.print=function(e){let t=`event: ${e.event}`;return e.data&&(t+=`\ndata: ${JSON.stringify(e.data)}`),t+="\n\n",t},e.validateStreamEvent=s})); |
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
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
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
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
331880
33
51
5850
879
5