graphql-sse
Advanced tools
Comparing version 2.1.1 to 2.1.2
@@ -243,5 +243,10 @@ "use strict"; | ||
}); | ||
retryingErr = null; // future connects are not retries | ||
retries = 0; // reset the retries on connect | ||
for await (const result of getResults()) { | ||
// only after receiving results are future connects not considered retries. | ||
// this is because a client might successfully connect, but the server | ||
// ends up terminating the connection afterwards before streaming anything. | ||
// of course, if the client completes the subscription, this loop will | ||
// break and therefore stop the stream (it wont reconnect) | ||
retryingErr = null; | ||
retries = 0; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -248,0 +253,0 @@ sink.next(result); |
{ | ||
"name": "graphql-sse", | ||
"version": "2.1.1", | ||
"version": "2.1.2", | ||
"description": "Zero-dependency, HTTP/1 safe, simple, GraphQL over Server-Sent Events Protocol server and client", | ||
@@ -29,3 +29,3 @@ "keywords": [ | ||
}, | ||
"packageManager": "yarn@3.5.0", | ||
"packageManager": "yarn@3.5.1", | ||
"main": "lib/index.js", | ||
@@ -82,4 +82,7 @@ "module": "lib/index.mjs", | ||
}, | ||
"workspaces": [ | ||
"website" | ||
], | ||
"scripts": { | ||
"gendocs": "typedoc --options typedoc.js src/", | ||
"gendocs": "typedoc --options typedoc.js src/ && node scripts/post-gendocs.mjs", | ||
"lint:eslint": "eslint 'src'", | ||
@@ -100,3 +103,3 @@ "lint:prettier": "prettier -c .", | ||
"devDependencies": { | ||
"@babel/core": "^7.21.4", | ||
"@babel/core": "^7.21.8", | ||
"@babel/plugin-proposal-class-properties": "^7.18.6", | ||
@@ -106,6 +109,6 @@ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", | ||
"@babel/plugin-proposal-optional-chaining": "^7.21.0", | ||
"@babel/preset-env": "^7.21.4", | ||
"@babel/preset-typescript": "^7.21.4", | ||
"@rollup/plugin-terser": "^0.4.0", | ||
"@rollup/plugin-typescript": "^11.0.0", | ||
"@babel/preset-env": "^7.21.5", | ||
"@babel/preset-typescript": "^7.21.5", | ||
"@rollup/plugin-terser": "^0.4.1", | ||
"@rollup/plugin-typescript": "^11.1.0", | ||
"@semantic-release/changelog": "^6.0.3", | ||
@@ -117,22 +120,22 @@ "@semantic-release/git": "^10.0.1", | ||
"@types/glob": "^8.1.0", | ||
"@types/jest": "^29.5.0", | ||
"@typescript-eslint/eslint-plugin": "^5.57.0", | ||
"@typescript-eslint/parser": "^5.57.0", | ||
"@types/jest": "^29.5.1", | ||
"@typescript-eslint/eslint-plugin": "^5.59.5", | ||
"@typescript-eslint/parser": "^5.59.5", | ||
"babel-jest": "^29.5.0", | ||
"eslint": "^8.37.0", | ||
"eslint": "^8.40.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"express": "^4.18.2", | ||
"fastify": "^4.15.0", | ||
"glob": "^9.3.2", | ||
"fastify": "^4.17.0", | ||
"glob": "^10.2.3", | ||
"graphql": "^16.6.0", | ||
"jest": "^29.5.0", | ||
"prettier": "^2.8.7", | ||
"rollup": "^3.20.2", | ||
"prettier": "^2.8.8", | ||
"rollup": "^3.21.6", | ||
"rollup-plugin-gzip": "^3.1.0", | ||
"semantic-release": "^21.0.0", | ||
"semantic-release": "^21.0.2", | ||
"tslib": "^2.5.0", | ||
"typedoc": "^0.23.28", | ||
"typedoc-plugin-markdown": "^3.14.0", | ||
"typescript": "^5.0.3" | ||
"typedoc": "^0.24.7", | ||
"typedoc-plugin-markdown": "^3.15.3", | ||
"typescript": "^5.0.4" | ||
} | ||
} |
857
README.md
@@ -15,861 +15,16 @@ <div align="center"> | ||
## Getting started | ||
## [Get started](https://the-guild.dev/graphql/sse/get-started) | ||
#### Install | ||
Swiftly start with the [get started guide on the website](https://the-guild.dev/graphql/sse/get-started). | ||
```shell | ||
yarn add graphql-sse | ||
``` | ||
## [Recipes](https://the-guild.dev/graphql/sse/recipes) | ||
#### Create a GraphQL schema | ||
Short and concise code snippets for starting with common use-cases. [Available on the website.](https://the-guild.dev/graphql/sse/recipes) | ||
```js | ||
import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; | ||
/** | ||
* Construct a GraphQL schema and define the necessary resolvers. | ||
* | ||
* type Query { | ||
* hello: String | ||
* } | ||
* type Subscription { | ||
* greetings: String | ||
* } | ||
*/ | ||
const schema = new GraphQLSchema({ | ||
query: new GraphQLObjectType({ | ||
name: 'Query', | ||
fields: { | ||
hello: { | ||
type: GraphQLString, | ||
resolve: () => 'world', | ||
}, | ||
}, | ||
}), | ||
subscription: new GraphQLObjectType({ | ||
name: 'Subscription', | ||
fields: { | ||
greetings: { | ||
type: GraphQLString, | ||
subscribe: async function* () { | ||
for (const hi of ['Hi', 'Bonjour', 'Hola', 'Ciao', 'Zdravo']) { | ||
yield { greetings: hi }; | ||
} | ||
}, | ||
}, | ||
}, | ||
}), | ||
}); | ||
``` | ||
#### Start the server | ||
##### With [`http`](https://nodejs.org/api/http.html) | ||
```js | ||
import http from 'http'; | ||
import { createHandler } from 'graphql-sse/lib/use/http'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE handler | ||
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(); | ||
}); | ||
server.listen(4000); | ||
console.log('Listening to port 4000'); | ||
``` | ||
##### With [`http2`](https://nodejs.org/api/http2.html) | ||
_Browsers might complain about self-signed SSL/TLS certificates. [Help can be found on StackOverflow.](https://stackoverflow.com/questions/7580508/getting-chrome-to-accept-self-signed-localhost-certificate)_ | ||
```shell | ||
$ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ | ||
-keyout localhost-privkey.pem -out localhost-cert.pem | ||
``` | ||
```js | ||
import fs from 'fs'; | ||
import http2 from 'http2'; | ||
import { createHandler } from 'graphql-sse/lib/use/http2'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE handler | ||
const handler = createHandler({ schema }); | ||
// Create an HTTP/2 server using the handler on `/graphql/stream` | ||
const server = http2.createServer((req, res) => { | ||
if (req.url.startsWith('/graphql/stream')) { | ||
return handler(req, res); | ||
} | ||
res.writeHead(404).end(); | ||
}); | ||
server.listen(4000); | ||
console.log('Listening to port 4000'); | ||
``` | ||
##### With [`express`](https://expressjs.com) | ||
```js | ||
import express from 'express'; // yarn add express | ||
import { createHandler } from 'graphql-sse/lib/use/express'; | ||
import { schema } from './previous-step'; | ||
// Create the GraphQL over SSE handler | ||
const handler = createHandler({ schema }); | ||
// Create an express app | ||
const app = express(); | ||
// Serve all methods on `/graphql/stream` | ||
app.use('/graphql/stream', handler); | ||
server.listen(4000); | ||
console.log('Listening to port 4000'); | ||
``` | ||
##### With [`fastify`](https://www.fastify.io) | ||
```js | ||
import Fastify from 'fastify'; // yarn add fastify | ||
import { createHandler } from 'graphql-sse/lib/use/fastify'; | ||
// Create the GraphQL over SSE handler | ||
const handler = createHandler({ schema }); | ||
// Create a fastify app | ||
const fastify = Fastify(); | ||
// 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@>=0.5.7`](https://bun.sh/) | ||
[⚠️ Bun's fetch does not support streaming.](https://github.com/oven-sh/bun/issues/2103#issuecomment-1435652020) | ||
```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 | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
const client = createClient({ | ||
// singleConnection: true, preferred for HTTP/1 enabled servers. read more below | ||
url: 'http://localhost:4000/graphql/stream', | ||
}); | ||
// query | ||
(async () => { | ||
const result = await new Promise((resolve, reject) => { | ||
let result; | ||
client.subscribe( | ||
{ | ||
query: '{ hello }', | ||
}, | ||
{ | ||
next: (data) => (result = data), | ||
error: reject, | ||
complete: () => resolve(result), | ||
}, | ||
); | ||
}); | ||
expect(result).toEqual({ hello: 'world' }); | ||
})(); | ||
// subscription | ||
(async () => { | ||
const onNext = () => { | ||
/* handle incoming values */ | ||
}; | ||
let unsubscribe = () => { | ||
/* complete the subscription */ | ||
}; | ||
await new Promise((resolve, reject) => { | ||
unsubscribe = client.subscribe( | ||
{ | ||
query: 'subscription { greetings }', | ||
}, | ||
{ | ||
next: onNext, | ||
error: reject, | ||
complete: resolve, | ||
}, | ||
); | ||
}); | ||
expect(onNext).toBeCalledTimes(5); // we say "Hi" in 5 languages | ||
})(); | ||
``` | ||
## Recipes | ||
<details id="promise"> | ||
<summary><a href="#promise">🔗</a> Client usage with Promise</summary> | ||
```ts | ||
import { createClient, RequestParams } from 'graphql-sse'; | ||
const client = createClient({ | ||
url: 'http://hey.there:4000/graphql/stream', | ||
}); | ||
export async function execute<T>(payload: RequestParams) { | ||
return new Promise<T>((resolve, reject) => { | ||
let result: T; | ||
client.subscribe<T>(payload, { | ||
next: (data) => (result = data), | ||
error: reject, | ||
complete: () => resolve(result), | ||
}); | ||
}); | ||
} | ||
// use | ||
(async () => { | ||
try { | ||
const result = await execute({ | ||
query: '{ hello }', | ||
}); | ||
// complete | ||
// next = result = { data: { hello: 'Hello World!' } } | ||
} catch (err) { | ||
// error | ||
} | ||
})(); | ||
``` | ||
</details> | ||
<details id="async-iterator"> | ||
<summary><a href="#async-iterator">🔗</a> Client usage with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator">AsyncIterator</a></summary> | ||
## [Documentation](https://the-guild.dev/graphql/sse/docs) | ||
```js | ||
import { createClient, RequestParams } from 'graphql-sse'; | ||
Auto-generated by [TypeDoc](https://typedoc.org) and then [rendered on the website](https://the-guild.dev/graphql/sse/docs). | ||
const client = createClient({ | ||
url: 'http://iterators.ftw:4000/graphql/stream', | ||
}); | ||
export function subscribe(payload) { | ||
let deferred = null; | ||
const pending = []; | ||
let throwMe = null, | ||
done = false; | ||
const dispose = client.subscribe(payload, { | ||
next: (data) => { | ||
pending.push(data); | ||
deferred?.resolve(false); | ||
}, | ||
error: (err) => { | ||
throwMe = err; | ||
deferred?.reject(throwMe); | ||
}, | ||
complete: () => { | ||
done = true; | ||
deferred?.resolve(true); | ||
}, | ||
}); | ||
return { | ||
[Symbol.asyncIterator]() { | ||
return this; | ||
}, | ||
async next() { | ||
if (done) return { done: true, value: undefined }; | ||
if (throwMe) throw throwMe; | ||
if (pending.length) return { value: pending.shift() }; | ||
return (await new Promise( | ||
(resolve, reject) => (deferred = { resolve, reject }), | ||
)) | ||
? { done: true, value: undefined } | ||
: { value: pending.shift() }; | ||
}, | ||
async throw(err) { | ||
throwMe = err; | ||
deferred?.reject(throwMe); | ||
return { done: true, value: undefined }; | ||
}, | ||
async return() { | ||
done = true; | ||
deferred?.resolve(true); | ||
dispose(); | ||
return { done: true, value: undefined }; | ||
}, | ||
}; | ||
} | ||
(async () => { | ||
const subscription = subscribe({ | ||
query: 'subscription { greetings }', | ||
}); | ||
// subscription.return() to dispose | ||
for await (const result of subscription) { | ||
// next = result = { data: { greetings: 5x } } | ||
} | ||
// complete | ||
})(); | ||
``` | ||
</details> | ||
<details id="observable"> | ||
<summary><a href="#observable">🔗</a> Client usage with <a href="https://github.com/tc39/proposal-observable">Observable</a></summary> | ||
```js | ||
import { Observable } from 'relay-runtime'; | ||
// or | ||
import { Observable } from '@apollo/client/core'; | ||
// or | ||
import { Observable } from 'rxjs'; | ||
// or | ||
import Observable from 'zen-observable'; | ||
// or any other lib which implements Observables as per the ECMAScript proposal: https://github.com/tc39/proposal-observable | ||
const client = createClient({ | ||
url: 'http://graphql.loves:4000/observables', | ||
}); | ||
export function toObservable(operation) { | ||
return new Observable((observer) => | ||
client.subscribe(operation, { | ||
next: (data) => observer.next(data), | ||
error: (err) => observer.error(err), | ||
complete: () => observer.complete(), | ||
}), | ||
); | ||
} | ||
const observable = toObservable({ query: `subscription { ping }` }); | ||
const subscription = observable.subscribe({ | ||
next: (data) => { | ||
expect(data).toBe({ data: { ping: 'pong' } }); | ||
}, | ||
}); | ||
// ⏱ | ||
subscription.unsubscribe(); | ||
``` | ||
</details> | ||
<details id="relay"> | ||
<summary><a href="#relay">🔗</a> Client usage with <a href="https://relay.dev">Relay</a></summary> | ||
```js | ||
import { GraphQLError } from 'graphql'; | ||
import { | ||
Network, | ||
Observable, | ||
RequestParameters, | ||
Variables, | ||
} from 'relay-runtime'; | ||
import { createClient } from 'graphql-sse'; | ||
const subscriptionsClient = createClient({ | ||
url: 'http://i.love:4000/graphql/stream', | ||
headers: () => { | ||
const session = getSession(); | ||
if (!session) return {}; | ||
return { | ||
Authorization: `Bearer ${session.token}`, | ||
}; | ||
}, | ||
}); | ||
// yes, both fetch AND subscribe can be handled in one implementation | ||
function fetchOrSubscribe(operation: RequestParameters, variables: Variables) { | ||
return Observable.create((sink) => { | ||
if (!operation.text) { | ||
return sink.error(new Error('Operation text cannot be empty')); | ||
} | ||
return subscriptionsClient.subscribe( | ||
{ | ||
operationName: operation.name, | ||
query: operation.text, | ||
variables, | ||
}, | ||
sink, | ||
); | ||
}); | ||
} | ||
export const network = Network.create(fetchOrSubscribe, fetchOrSubscribe); | ||
``` | ||
</details> | ||
<details id="urql"> | ||
<summary><a href="#urql">🔗</a> Client usage with <a href="https://formidable.com/open-source/urql/">urql</a></summary> | ||
```js | ||
import { createClient, defaultExchanges, subscriptionExchange } from 'urql'; | ||
import { createClient as createSSEClient } from 'graphql-sse'; | ||
const sseClient = createSSEClient({ | ||
url: 'http://its.urql:4000/graphql/stream', | ||
}); | ||
export const client = createClient({ | ||
url: '/graphql/stream', | ||
exchanges: [ | ||
...defaultExchanges, | ||
subscriptionExchange({ | ||
forwardSubscription(operation) { | ||
return { | ||
subscribe: (sink) => { | ||
const dispose = sseClient.subscribe(operation, sink); | ||
return { | ||
unsubscribe: dispose, | ||
}; | ||
}, | ||
}; | ||
}, | ||
}), | ||
], | ||
}); | ||
``` | ||
</details> | ||
<details id="apollo-client"> | ||
<summary><a href="#apollo-client">🔗</a> Client usage with <a href="https://www.apollographql.com">Apollo</a></summary> | ||
```ts | ||
import { | ||
ApolloLink, | ||
Operation, | ||
FetchResult, | ||
Observable, | ||
} from '@apollo/client/core'; | ||
import { print, GraphQLError } from 'graphql'; | ||
import { createClient, ClientOptions, Client } from 'graphql-sse'; | ||
class SSELink extends ApolloLink { | ||
private client: Client; | ||
constructor(options: ClientOptions) { | ||
super(); | ||
this.client = createClient(options); | ||
} | ||
public request(operation: Operation): Observable<FetchResult> { | ||
return new Observable((sink) => { | ||
return this.client.subscribe<FetchResult>( | ||
{ ...operation, query: print(operation.query) }, | ||
{ | ||
next: sink.next.bind(sink), | ||
complete: sink.complete.bind(sink), | ||
error: sink.error.bind(sink), | ||
}, | ||
); | ||
}); | ||
} | ||
} | ||
export const link = new SSELink({ | ||
url: 'http://where.is:4000/graphql/stream', | ||
headers: () => { | ||
const session = getSession(); | ||
if (!session) return {}; | ||
return { | ||
Authorization: `Bearer ${session.token}`, | ||
}; | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="single-connection-mode"> | ||
<summary><a href="#single-connection-mode">🔗</a> Client usage for HTTP/1 (aka. <a href="https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#single-connection-mode">single connection mode</a>)</summary> | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
export const client = createClient({ | ||
singleConnection: true, // this is literally it 😄 | ||
url: 'http://use.single:4000/connection/graphql/stream', | ||
// lazy: true (default) -> connect on first subscribe and disconnect on last unsubscribe | ||
// lazy: false -> connect as soon as the client is created | ||
}); | ||
// The client will now run in a "single connection mode" mode. Meaning, | ||
// a single SSE connection will be used to transmit all operation results | ||
// while separate HTTP requests will be issued to dictate the behaviour. | ||
``` | ||
</details> | ||
<details id="retry-strategy"> | ||
<summary><a href="#retry-strategy">🔗</a> Client usage with custom retry timeout strategy</summary> | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
import { waitForHealthy } from './my-servers'; | ||
const url = 'http://i.want.retry:4000/control/graphql/stream'; | ||
export const client = createClient({ | ||
url, | ||
retryWait: async function waitForServerHealthyBeforeRetry() { | ||
// if you have a server healthcheck, you can wait for it to become | ||
// healthy before retrying after an abrupt disconnect (most commonly a restart) | ||
await waitForHealthy(url); | ||
// after the server becomes ready, wait for a second + random 1-4s timeout | ||
// (avoid DDoSing yourself) and try connecting again | ||
await new Promise((resolve) => | ||
setTimeout(resolve, 1000 + Math.random() * 3000), | ||
); | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="client-debug-messages"> | ||
<summary><a href="#client-debug-messages">🔗</a> Client usage with logging of incoming messages (<a href="https://github.com/enisdenjo/graphql-sse/issues/20">browsers don't show them in the DevTools</a>)</summary> | ||
```js | ||
import { createClient } from 'graphql-sse'; | ||
export const client = createClient({ | ||
url: 'http://let-me-see.messages:4000/graphql/stream', | ||
onMessage: console.log, | ||
}); | ||
``` | ||
</details> | ||
<details id="browser"> | ||
<summary><a href="#browser">🔗</a> Client usage in browser</summary> | ||
```html | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<title>GraphQL over Server-Sent Events</title> | ||
<script | ||
type="text/javascript" | ||
src="https://unpkg.com/graphql-sse/umd/graphql-sse.min.js" | ||
></script> | ||
</head> | ||
<body> | ||
<script type="text/javascript"> | ||
const client = graphqlSse.createClient({ | ||
url: 'http://umdfor.the:4000/win/graphql/stream', | ||
}); | ||
// consider other recipes for usage inspiration | ||
</script> | ||
</body> | ||
</html> | ||
``` | ||
</details> | ||
<details id="node-client"> | ||
<summary><a href="#node-client">🔗</a> Client usage in Node</summary> | ||
```js | ||
const ws = require('ws'); // yarn add ws | ||
const fetch = require('node-fetch'); // yarn add node-fetch | ||
const { AbortController } = require('node-abort-controller'); // (node < v15) yarn add node-abort-controller | ||
const Crypto = require('crypto'); | ||
const { createClient } = require('graphql-sse'); | ||
export const client = createClient({ | ||
url: 'http://no.browser:4000/graphql/stream', | ||
fetchFn: fetch, | ||
abortControllerImpl: AbortController, // node < v15 | ||
/** | ||
* Generates a v4 UUID to be used as the ID. | ||
* Reference: https://gist.github.com/jed/982883 | ||
*/ | ||
generateID: () => | ||
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => | ||
(c ^ (Crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16), | ||
), | ||
}); | ||
``` | ||
</details> | ||
<details id="auth"> | ||
<summary><a href="#schema">🔗</a> Server handler usage with custom authentication</summary> | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
import { | ||
schema, | ||
getOrCreateTokenFromCookies, | ||
customAuthenticationTokenDiscovery, | ||
processAuthorizationHeader, | ||
} from './my-graphql'; | ||
export const handler = createHandler({ | ||
schema, | ||
authenticate: async (req) => { | ||
let token = req.headers.get('x-graphql-event-stream-token'); | ||
if (token) { | ||
// When the client is working in a "single connection mode" | ||
// all subsequent requests for operations will have the | ||
// stream token set in the `X-GraphQL-Event-Stream-Token` header. | ||
// | ||
// It is considered safe to accept the header token always | ||
// because if a stream reservation does not exist, or is already | ||
// fulfilled, the handler itself will reject the request. | ||
// | ||
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#single-connection-mode | ||
return Array.isArray(token) ? token.join('') : token; | ||
} | ||
// It is necessary to generate a unique token when dealing with | ||
// clients that operate in the "single connection mode". The process | ||
// of generating the token is completely up to the implementor. | ||
token = getOrCreateTokenFromCookies(req); | ||
// or | ||
token = processAuthorizationHeader(req.headers.get('authorization')); | ||
// or | ||
token = await customAuthenticationTokenDiscovery(req); | ||
// Using the response argument the implementor may respond to | ||
// authentication issues however he sees fit. | ||
if (!token) { | ||
return [null, { status: 401, statusText: 'Unauthorized' }]; | ||
} | ||
// Clients that operate in "distinct connections mode" dont | ||
// need a unique stream token. It is completely ok to simply | ||
// return an empty string for authenticated clients. | ||
// | ||
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#distinct-connections-mode | ||
if ( | ||
req.method === 'POST' && | ||
req.headers.get('accept') === 'text/event-stream' | ||
) { | ||
// "distinct connections mode" requests an event-stream with a POST | ||
// method. These two checks, together with the lack of `X-GraphQL-Event-Stream-Token` | ||
// header, are sufficient for accurate detection. | ||
return ''; // return token; is OK too | ||
} | ||
// On the other hand, clients operating in "single connection mode" | ||
// need a unique stream token which will be provided alongside the | ||
// incoming event stream request inside the `X-GraphQL-Event-Stream-Token` header. | ||
// | ||
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#single-connection-mode | ||
return token; | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="dynamic-schema"> | ||
<summary><a href="#dynamic-schema">🔗</a> Server handler usage with dynamic schema</summary> | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema, checkIsAdmin, getDebugSchema } from './my-graphql'; | ||
export const handler = createHandler({ | ||
schema: async (req, executionArgsWithoutSchema) => { | ||
// will be called on every subscribe request | ||
// allowing you to dynamically supply the schema | ||
// using the depending on the provided arguments | ||
const isAdmin = await checkIsAdmin(req); | ||
if (isAdmin) return getDebugSchema(req, executionArgsWithoutSchema); | ||
return schema; | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="context"> | ||
<summary><a href="#context">🔗</a> Server handler usage with custom context value</summary> | ||
```js | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema, getDynamicContext } from './my-graphql'; | ||
export const handler = createHandler({ | ||
schema, | ||
// or static context by supplying the value direcly | ||
context: (req, args) => { | ||
return getDynamicContext(req, args); | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="custom-exec"> | ||
<summary><a href="#custom-exec">🔗</a> Server handler usage with custom execution arguments</summary> | ||
```js | ||
import { parse } from 'graphql'; | ||
import { createHandler } from 'graphql-sse'; | ||
import { getSchema, myValidationRules } from './my-graphql'; | ||
export const handler = createHandler({ | ||
onSubscribe: async (req, params) => { | ||
const schema = await getSchema(req); | ||
return { | ||
schema, | ||
operationName: params.operationName, | ||
document: | ||
typeof params.query === 'string' ? parse(params.query) : params.query, | ||
variableValues: params.variables, | ||
contextValue: undefined, | ||
}; | ||
}, | ||
}); | ||
``` | ||
</details> | ||
<details id="persisted"> | ||
<summary><a href="#persisted">🔗</a> Server handler and client usage with persisted queries</summary> | ||
```ts | ||
// 🛸 server | ||
import { parse, ExecutionArgs } from 'graphql'; | ||
import { createHandler } from 'graphql-sse'; | ||
import { schema } from './my-graphql'; | ||
// a unique GraphQL execution ID used for representing | ||
// a query in the persisted queries store. when subscribing | ||
// you should use the `SubscriptionPayload.query` to transmit the id | ||
type QueryID = string; | ||
const queriesStore: Record<QueryID, ExecutionArgs> = { | ||
iWantTheGreetings: { | ||
schema, // you may even provide different schemas in the queries store | ||
document: parse('subscription Greetings { greetings }'), | ||
}, | ||
}; | ||
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 [null, { status: 404, statusText: 'Not Found' }]; | ||
}, | ||
}); | ||
``` | ||
```ts | ||
// 📺 client | ||
import { createClient } from 'graphql-sse'; | ||
const client = createClient({ | ||
url: 'http://persisted.graphql:4000/queries', | ||
}); | ||
(async () => { | ||
const onNext = () => { | ||
/**/ | ||
}; | ||
await new Promise((resolve, reject) => { | ||
client.subscribe( | ||
{ | ||
query: '', // query field is required, but you can leave it empty for persisted queries | ||
extensions: { | ||
persistedQuery: 'iWantTheGreetings', | ||
}, | ||
}, | ||
{ | ||
next: onNext, | ||
error: reject, | ||
complete: resolve, | ||
}, | ||
); | ||
}); | ||
expect(onNext).toBeCalledTimes(5); // greetings in 5 languages | ||
})(); | ||
``` | ||
</details> | ||
## [Documentation](docs/) | ||
Check the [docs folder](docs/) out for [TypeDoc](https://typedoc.org) generated documentation. | ||
## [How does it work?](PROTOCOL.md) | ||
@@ -876,0 +31,0 @@ |
@@ -435,5 +435,10 @@ (function (global, factory) { | ||
}); | ||
retryingErr = null; // future connects are not retries | ||
retries = 0; // reset the retries on connect | ||
for await (const result of getResults()) { | ||
// only after receiving results are future connects not considered retries. | ||
// this is because a client might successfully connect, but the server | ||
// ends up terminating the connection afterwards before streaming anything. | ||
// of course, if the client completes the subscription, this loop will | ||
// break and therefore stop the stream (it wont reconnect) | ||
retryingErr = null; | ||
retries = 0; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -440,0 +445,0 @@ sink.next(result); |
@@ -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){return"object"==typeof e&&null!==e}const r="x-graphql-event-stream-token";function n(e){if("next"!==e&&"complete"!==e)throw new Error(`Invalid stream event "${e}"`);return e}function o(e,t){if(t)try{t=JSON.parse(t)}catch{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 a;!function(e){e[e.NewLine=10]="NewLine",e[e.CchunkiageReturn=13]="CchunkiageReturn",e[e.Space=32]="Space",e[e.Colon=58]="Colon"}(a||(a={}));class i extends Error{constructor(e){let r,n;var o;t(o=e)&&"boolean"==typeof o.ok&&"number"==typeof o.status&&"string"==typeof o.statusText?(n=e,r="Server responded with "+e.status+": "+e.statusText):r=e instanceof Error?e.message:String(e),super(r),this.name=this.constructor.name,this.response=n}}async function s(e){const{signal:t,url:r,credentials:s,headers:c,body:l,referrer:f,referrerPolicy:d,fetchFn:u,onMessage:h}=e,w={},y={};let b;try{b=await u(r,{signal:t,method:l?"POST":"GET",credentials:s,referrer:f,referrerPolicy:d,headers:{...c,accept:"text/event-stream"},body:l})}catch(e){throw new i(e)}if(!b.ok)throw new i(b);if(!b.body)throw new Error("Missing response body");let p,g=null;return(async()=>{var e;try{const t=function(){let e,t,r,i=!1,s={event:"",data:""},c=[];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 d=e.length;let u=0;for(;t<d;){i&&(e[t]===a.NewLine&&(u=++t),i=!1);let f=-1;for(;t<d&&-1===f;++t)switch(e[t]){case a.Colon:-1===r&&(r=t-u);break;case a.CchunkiageReturn:i=!0;case a.NewLine:f=t}if(-1===f)break;if(u===f){if(s.event||s.data){if(!s.event)throw new Error("Missing message event");const e=n(s.event),t=o(e,s.data);c.push({event:e,data:t}),s={event:"",data:""}}}else if(r>0){const t=e.subarray(u,f),n=l.decode(t.subarray(0,r)),o=r+(t[r+1]===a.Space?2:1),i=l.decode(t.subarray(o));switch(n){case"event":s.event=i;break;case"data":s.data=s.data?s.data+"\n"+i:i}}u=t,r=-1}if(u===d){e=void 0;const t=[...c];return c=[],t}0!==u&&(e=e.subarray(u),t-=u)}}();for await(const r of function(e){if("function"==typeof Object(e)[Symbol.asyncIterator])return e[Symbol.asyncIterator]();return async function*(){const t=e.getReader();let r;do{r=await t.read(),void 0!==r.value&&(yield r.value)}while(!r.done)}()}(b.body)){if("string"==typeof r)throw g=new Error(`Unexpected string chunk "${r}"`);let n;try{n=t(r)}catch(e){throw g=e}if(n)for(const t of n){try{null==h||h(t)}catch(e){throw g=e}const r=t.data&&"id"in t.data?t.data.id:"";switch(r in y||(y[r]=[]),t.event){case"next":r?y[r].push(t.data.payload):y[r].push(t.data);break;case"complete":y[r].push("complete");break;default:throw g=new Error(`Unexpected message event "${t.event}"`)}null===(e=w[r])||void 0===e||e.proceed()}}if(Object.keys(w).length)throw new Error("Connection closed while having active streams")}catch(e){g=!g&&Object.keys(w).length?new i(e):e,null==p||p(g)}finally{Object.values(w).forEach((({proceed:e})=>e()))}})(),{url:r,headers:c,waitForThrow:()=>new Promise(((e,t)=>{if(g)return t(g);p=t})),async*getResults(e){var t;const{signal:r,operationId:n=""}=null!=e?e:{};try{for(;;){for(;null===(t=y[n])||void 0===t?void 0:t.length;){const e=y[n].shift();if("complete"===e)return;yield e}if(g)throw g;if(null==r?void 0:r.aborted)throw new Error("Getting results aborted by the client");await new Promise((e=>{const t=()=>{null==r||r.removeEventListener("abort",t),delete w[n],e()};null==r||r.addEventListener("abort",t),w[n]={proceed:t}}))}}finally{delete y[n]}}}}e.NetworkError=i,e.TOKEN_HEADER_KEY=r,e.TOKEN_QUERY_KEY="token",e.createClient=function(e){const{singleConnection:t=!1,lazy:n=!0,lazyCloseTimeout:o=0,onNonLazyError:a=console.error,generateID:c=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:l=5,retry:f=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:u,referrerPolicy:h,onMessage:w}=e,y=e.fetchFn||fetch,b=e.abortControllerImpl||AbortController,p=(()=>{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 g,v,x=0,m=null,E=0;async function S(){try{if(p.disposed)throw new Error("Client has been disposed");return await(null!=v?v:v=(async()=>{var t;if(m){if(await f(E),g.signal.aborted)throw new Error("Connection aborted by the client");E++}g=new b;const n=p.onDispose((()=>g.abort()));g.signal.addEventListener("abort",(()=>{n(),v=void 0}));const o="function"==typeof e.url?await e.url():e.url;if(g.signal.aborted)throw new Error("Connection aborted by the client");const a="function"==typeof e.headers?await e.headers():null!==(t=e.headers)&&void 0!==t?t:{};if(g.signal.aborted)throw new Error("Connection aborted by the client");let c;try{c=await y(o,{signal:g.signal,method:"PUT",credentials:d,referrer:u,referrerPolicy:h,headers:a})}catch(e){throw new i(e)}if(201!==c.status)throw new i(c);const l=await c.text();a[r]=l;const x=await s({signal:g.signal,headers:a,credentials:d,referrer:u,referrerPolicy:h,url:o,fetchFn:y,onMessage:w});return m=null,E=0,x.waitForThrow().catch((()=>v=void 0)),x})())}catch(e){throw v=void 0,e}}return t&&!n&&(async()=>{for(x++;;)try{const{waitForThrow:e}=await S();await e()}catch(e){if(p.disposed)return;if(!(e instanceof i))return null==a?void 0:a(e);if(v=void 0,!l||E>=l)return null==a?void 0:a(e);m=e}})(),{subscribe(r,a){if(!t){const t=new b,n=p.onDispose((()=>{n(),t.abort()}));return(async()=>{var n;let o=null,c=0;for(;;)try{if(o){if(await f(c),t.signal.aborted)throw new Error("Connection aborted by the client");c++}const i="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!==(n=e.headers)&&void 0!==n?n:{};if(t.signal.aborted)throw new Error("Connection aborted by the client");const{getResults:b}=await s({signal:t.signal,headers:{...l,"content-type":"application/json; charset=utf-8"},credentials:d,referrer:u,referrerPolicy:h,url:i,body:JSON.stringify(r),fetchFn:y,onMessage:w});o=null,c=0;for await(const e of b())a.next(e);return t.abort()}catch(e){if(t.signal.aborted)return;if(!(e instanceof i))throw e;if(!l||c>=l)throw e;o=e}})().then((()=>a.complete())).catch((e=>a.error(e))),()=>t.abort()}x++;const C=new b,T=p.onDispose((()=>{T(),C.abort()}));return(async()=>{const e=c();r={...r,extensions:{...r.extensions,operationId:e}};let t=null;for(;;){t=null;try{const{url:n,headers:o,getResults:s}=await S();let c;try{c=await y(n,{signal:C.signal,method:"POST",credentials:d,referrer:u,referrerPolicy:h,headers:{...o,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(r)})}catch(e){throw new i(e)}if(202!==c.status)throw new i(c);t=async()=>{let t;try{const r=new b,a=p.onDispose((()=>{a(),r.abort()}));t=await y(n+"?operationId="+e,{signal:r.signal,method:"DELETE",credentials:d,referrer:u,referrerPolicy:h,headers:o})}catch(e){throw new i(e)}if(200!==t.status)throw new i(t)};for await(const t of s({signal:C.signal,operationId:e}))a.next(t);return t=null,C.abort()}catch(e){if(C.signal.aborted)return await(null==t?void 0:t());if(!(e instanceof i))throw C.abort(),e;if(n&&(v=void 0),!l||E>=l)throw C.abort(),e;m=e}finally{C.signal.aborted&&0==--x&&(isFinite(o)&&o>0?setTimeout((()=>{x||g.abort()}),o):g.abort())}}})().then((()=>a.complete())).catch((e=>a.error(e))),()=>C.abort()},dispose(){p.dispose()}}},e.isAsyncGenerator=function(e){return t(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=o,e.print=function(e){let t=`event: ${e.event}`;return e.data&&(t+=`\ndata: ${JSON.stringify(e.data)}`),t+="\n\n",t},e.validateStreamEvent=n})); | ||
!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){return"object"==typeof e&&null!==e}const r="x-graphql-event-stream-token";function n(e){if("next"!==e&&"complete"!==e)throw new Error(`Invalid stream event "${e}"`);return e}function o(e,t){if(t)try{t=JSON.parse(t)}catch{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 a;!function(e){e[e.NewLine=10]="NewLine",e[e.CchunkiageReturn=13]="CchunkiageReturn",e[e.Space=32]="Space",e[e.Colon=58]="Colon"}(a||(a={}));class i extends Error{constructor(e){let r,n;var o;t(o=e)&&"boolean"==typeof o.ok&&"number"==typeof o.status&&"string"==typeof o.statusText?(n=e,r="Server responded with "+e.status+": "+e.statusText):r=e instanceof Error?e.message:String(e),super(r),this.name=this.constructor.name,this.response=n}}async function s(e){const{signal:t,url:r,credentials:s,headers:c,body:l,referrer:f,referrerPolicy:d,fetchFn:u,onMessage:h}=e,w={},y={};let b;try{b=await u(r,{signal:t,method:l?"POST":"GET",credentials:s,referrer:f,referrerPolicy:d,headers:{...c,accept:"text/event-stream"},body:l})}catch(e){throw new i(e)}if(!b.ok)throw new i(b);if(!b.body)throw new Error("Missing response body");let p,g=null;return(async()=>{var e;try{const t=function(){let e,t,r,i=!1,s={event:"",data:""},c=[];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 d=e.length;let u=0;for(;t<d;){i&&(e[t]===a.NewLine&&(u=++t),i=!1);let f=-1;for(;t<d&&-1===f;++t)switch(e[t]){case a.Colon:-1===r&&(r=t-u);break;case a.CchunkiageReturn:i=!0;case a.NewLine:f=t}if(-1===f)break;if(u===f){if(s.event||s.data){if(!s.event)throw new Error("Missing message event");const e=n(s.event),t=o(e,s.data);c.push({event:e,data:t}),s={event:"",data:""}}}else if(r>0){const t=e.subarray(u,f),n=l.decode(t.subarray(0,r)),o=r+(t[r+1]===a.Space?2:1),i=l.decode(t.subarray(o));switch(n){case"event":s.event=i;break;case"data":s.data=s.data?s.data+"\n"+i:i}}u=t,r=-1}if(u===d){e=void 0;const t=[...c];return c=[],t}0!==u&&(e=e.subarray(u),t-=u)}}();for await(const r of function(e){if("function"==typeof Object(e)[Symbol.asyncIterator])return e[Symbol.asyncIterator]();return async function*(){const t=e.getReader();let r;do{r=await t.read(),void 0!==r.value&&(yield r.value)}while(!r.done)}()}(b.body)){if("string"==typeof r)throw g=new Error(`Unexpected string chunk "${r}"`);let n;try{n=t(r)}catch(e){throw g=e}if(n)for(const t of n){try{null==h||h(t)}catch(e){throw g=e}const r=t.data&&"id"in t.data?t.data.id:"";switch(r in y||(y[r]=[]),t.event){case"next":r?y[r].push(t.data.payload):y[r].push(t.data);break;case"complete":y[r].push("complete");break;default:throw g=new Error(`Unexpected message event "${t.event}"`)}null===(e=w[r])||void 0===e||e.proceed()}}if(Object.keys(w).length)throw new Error("Connection closed while having active streams")}catch(e){g=!g&&Object.keys(w).length?new i(e):e,null==p||p(g)}finally{Object.values(w).forEach((({proceed:e})=>e()))}})(),{url:r,headers:c,waitForThrow:()=>new Promise(((e,t)=>{if(g)return t(g);p=t})),async*getResults(e){var t;const{signal:r,operationId:n=""}=null!=e?e:{};try{for(;;){for(;null===(t=y[n])||void 0===t?void 0:t.length;){const e=y[n].shift();if("complete"===e)return;yield e}if(g)throw g;if(null==r?void 0:r.aborted)throw new Error("Getting results aborted by the client");await new Promise((e=>{const t=()=>{null==r||r.removeEventListener("abort",t),delete w[n],e()};null==r||r.addEventListener("abort",t),w[n]={proceed:t}}))}}finally{delete y[n]}}}}e.NetworkError=i,e.TOKEN_HEADER_KEY=r,e.TOKEN_QUERY_KEY="token",e.createClient=function(e){const{singleConnection:t=!1,lazy:n=!0,lazyCloseTimeout:o=0,onNonLazyError:a=console.error,generateID:c=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:l=5,retry:f=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:u,referrerPolicy:h,onMessage:w}=e,y=e.fetchFn||fetch,b=e.abortControllerImpl||AbortController,p=(()=>{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 g,v,x=0,m=null,E=0;async function S(){try{if(p.disposed)throw new Error("Client has been disposed");return await(null!=v?v:v=(async()=>{var t;if(m){if(await f(E),g.signal.aborted)throw new Error("Connection aborted by the client");E++}g=new b;const n=p.onDispose((()=>g.abort()));g.signal.addEventListener("abort",(()=>{n(),v=void 0}));const o="function"==typeof e.url?await e.url():e.url;if(g.signal.aborted)throw new Error("Connection aborted by the client");const a="function"==typeof e.headers?await e.headers():null!==(t=e.headers)&&void 0!==t?t:{};if(g.signal.aborted)throw new Error("Connection aborted by the client");let c;try{c=await y(o,{signal:g.signal,method:"PUT",credentials:d,referrer:u,referrerPolicy:h,headers:a})}catch(e){throw new i(e)}if(201!==c.status)throw new i(c);const l=await c.text();a[r]=l;const x=await s({signal:g.signal,headers:a,credentials:d,referrer:u,referrerPolicy:h,url:o,fetchFn:y,onMessage:w});return m=null,E=0,x.waitForThrow().catch((()=>v=void 0)),x})())}catch(e){throw v=void 0,e}}return t&&!n&&(async()=>{for(x++;;)try{const{waitForThrow:e}=await S();await e()}catch(e){if(p.disposed)return;if(!(e instanceof i))return null==a?void 0:a(e);if(v=void 0,!l||E>=l)return null==a?void 0:a(e);m=e}})(),{subscribe(r,a){if(!t){const t=new b,n=p.onDispose((()=>{n(),t.abort()}));return(async()=>{var n;let o=null,c=0;for(;;)try{if(o){if(await f(c),t.signal.aborted)throw new Error("Connection aborted by the client");c++}const i="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!==(n=e.headers)&&void 0!==n?n:{};if(t.signal.aborted)throw new Error("Connection aborted by the client");const{getResults:b}=await s({signal:t.signal,headers:{...l,"content-type":"application/json; charset=utf-8"},credentials:d,referrer:u,referrerPolicy:h,url:i,body:JSON.stringify(r),fetchFn:y,onMessage:w});for await(const e of b())o=null,c=0,a.next(e);return t.abort()}catch(e){if(t.signal.aborted)return;if(!(e instanceof i))throw e;if(!l||c>=l)throw e;o=e}})().then((()=>a.complete())).catch((e=>a.error(e))),()=>t.abort()}x++;const C=new b,T=p.onDispose((()=>{T(),C.abort()}));return(async()=>{const e=c();r={...r,extensions:{...r.extensions,operationId:e}};let t=null;for(;;){t=null;try{const{url:n,headers:o,getResults:s}=await S();let c;try{c=await y(n,{signal:C.signal,method:"POST",credentials:d,referrer:u,referrerPolicy:h,headers:{...o,"content-type":"application/json; charset=utf-8"},body:JSON.stringify(r)})}catch(e){throw new i(e)}if(202!==c.status)throw new i(c);t=async()=>{let t;try{const r=new b,a=p.onDispose((()=>{a(),r.abort()}));t=await y(n+"?operationId="+e,{signal:r.signal,method:"DELETE",credentials:d,referrer:u,referrerPolicy:h,headers:o})}catch(e){throw new i(e)}if(200!==t.status)throw new i(t)};for await(const t of s({signal:C.signal,operationId:e}))a.next(t);return t=null,C.abort()}catch(e){if(C.signal.aborted)return await(null==t?void 0:t());if(!(e instanceof i))throw C.abort(),e;if(n&&(v=void 0),!l||E>=l)throw C.abort(),e;m=e}finally{C.signal.aborted&&0==--x&&(isFinite(o)&&o>0?setTimeout((()=>{x||g.abort()}),o):g.abort())}}})().then((()=>a.complete())).catch((e=>a.error(e))),()=>C.abort()},dispose(){p.dispose()}}},e.isAsyncGenerator=function(e){return t(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=o,e.print=function(e){let t=`event: ${e.event}`;return e.data&&(t+=`\ndata: ${JSON.stringify(e.data)}`),t+="\n\n",t},e.validateStreamEvent=n})); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
5377
277467
36