Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

graphql-sse

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-sse - npm Package Compare versions

Comparing version 2.1.1 to 2.1.2

9

lib/client.js

@@ -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"
}
}

@@ -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

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc