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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5377
277467
36