Commit 0cf0076b authored by Adam Parák's avatar Adam Parák 💬
Browse files

feat(graphql-ws): add theoretical support for graphql-ws in @inject/graphql

parent 741b17dd
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
    "@graphql-tools/batch-execute": "^9.0.11",
    "@graphql-tools/delegate": "^10.2.9",
    "@graphql-tools/executor": "^1.3.12",
    "@graphql-tools/executor-graphql-ws": "^2.0.4",
    "@graphql-tools/executor-http": "^1.2.4",
    "@graphql-tools/schema": "^10.0.15",
    "@graphql-tools/stitch": "^9.4.13",
@@ -34,6 +35,7 @@
    "@urql/exchange-retry": "^1.3.0",
    "gql.tada": "^1.8.10",
    "graphql": "16.10.0",
    "graphql-ws": "^6.0.4",
    "sanitize-html": "^2.14.0",
    "subscriptions-transport-ws": "0.11.0",
    "urql": "4.2.1",
+95 −54
Original line number Diff line number Diff line
import { createBatchingExecutor } from '@graphql-tools/batch-execute'
import { buildHTTPExecutor } from '@graphql-tools/executor-http'
import type { AsyncExecutor } from '@graphql-tools/utils'
import type { AsyncExecutor, Executor } from '@graphql-tools/utils'
import { observableToAsyncIterable } from '@graphql-tools/utils'
import {
  authenticatedFetch,
@@ -13,12 +13,15 @@ import { SubscriptionClient } from 'subscriptions-transport-ws'
import remoteSchemaQL from '../../schema.remote.json'
import { netvar } from '../networkEventVar'
import type { SubschemaConfig } from './typing'
import { buildGraphQLWSExecutor } from '@graphql-tools/executor-graphql-ws'

const ws: {
  current: SubscriptionClient | null
  currentV1: SubscriptionClient | null
  currentV2: ReturnType<typeof buildGraphQLWSExecutor> | null
  sessionid: string | null
} = {
  current: null,
  currentV1: null,
  currentV2: null,
  sessionid: null,
}

@@ -38,22 +41,28 @@ export const httpExecutor = createBatchingExecutor(
  })
)

declare global {
  interface Window {
    VITE_ENABLE_GRAPHQL_WS?: boolean,
  }
}

const constructRemoteSchema = (): SubschemaConfig => {
  /*
   * Custom executor which executes the subscriptions in subscriptions-transport-ws commlayer
   * the layer is coded in such a way that it rationally keeps one subscription for all comms, welcome to 2024
   */
  const wsExecutor: AsyncExecutor = async ({
  const wsExecutorV1: AsyncExecutor = async ({
    document,
    variables,
    operationName,
  }) =>
    observableToAsyncIterable({
      subscribe: observer => {
        if (ws.current === null) {
        if (ws.currentV1 === null) {
          throw Error('WS socket unitialized')
        }
        const { unsubscribe } = ws.current
        const { unsubscribe } = ws.currentV1
          .request({
            operationName,
            query: print(document),
@@ -75,26 +84,24 @@ const constructRemoteSchema = (): SubschemaConfig => {
      },
    })

  return {
    //@ts-ignore
    schema: buildClientSchema(remoteSchemaQL),
    executor: async executorRequest => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const executorV1: Executor<any> = async executorRequest => {
    if (executorRequest.operationType === 'subscription') {
      const sessionId = getSessionId()
        if (ws.current === null && sessionId === null) {
      if (ws.currentV1 === null && sessionId === null) {
        throw Error('No subscription to make without a sessionId')
      }
      if (
          ws.current === null ||
          (ws.current !== null && sessionId !== ws.sessionid) ||
          ws.current.status === WebSocket.CLOSED ||
          ws.current.status === WebSocket.CLOSING
        ws.currentV1 === null ||
        (ws.currentV1 !== null && sessionId !== ws.sessionid) ||
        ws.currentV1.status === WebSocket.CLOSED ||
        ws.currentV1.status === WebSocket.CLOSING
      ) {
          if (ws.current !== null) {
            ws.current.close(true, false)
            ws.current = null
        if (ws.currentV1 !== null) {
          ws.currentV1.close(true, false)
          ws.currentV1 = null
        }
          ws.current = new SubscriptionClient(
        ws.currentV1 = new SubscriptionClient(
          wsGraphql(window.VITE_HTTPS_HOST ?? 'localhost:8000'),
          {
            reconnect: true,
@@ -107,30 +114,64 @@ const constructRemoteSchema = (): SubschemaConfig => {
        )
        ws.sessionid = sessionId
      }
        if (ws.current.status === WebSocket.OPEN) {
          return wsExecutor(executorRequest)
      if (ws.currentV1.status === WebSocket.OPEN) {
        return wsExecutorV1(executorRequest)
      } else {
        await new Promise<void>(resolve => {
            if (ws.current === null) {
          if (ws.currentV1 === null) {
            throw Error('WS not initialized anyway, wtf')
          }
            ws.current.on('connected', () => {
          ws.currentV1.on('connected', () => {
            resolve()
          })
        })
          return wsExecutor(executorRequest)
        return wsExecutorV1(executorRequest)
      }
    }
    console.log('http:', executorRequest)
    return httpExecutor(executorRequest)
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const executorV2: Executor<any> =  async executorRequest => {
    if (executorRequest.operationType === 'subscription') {
      const sessionId = getSessionId()
      if (ws.currentV2 === null && sessionId === null) {
        throw Error('No subscription to make without a sessionId')
      }
      if (
        ws.currentV2 === null ||
        (ws.currentV2 !== null && sessionId !== ws.sessionid)
      ) {
        ws.currentV2 = buildGraphQLWSExecutor({
          url: wsGraphql(window.VITE_HTTPS_HOST ?? 'localhost:8000'),
          connectionParams: {
            sessionId: sessionId
          },
          lazy: false,
        })
        ws.sessionid = sessionId
      }
      return ws.currentV2(executorRequest)
    }
    console.log('http:', executorRequest)
    return httpExecutor(executorRequest)
  }

  return {
    //@ts-ignore
    schema: buildClientSchema(remoteSchemaQL),
    executor: window.VITE_ENABLE_GRAPHQL_WS ? executorV2 : executorV1
  }
}

window.addEventListener('killWs', () => {
  if (ws.current !== null) {
    ws.current.close(true, true)
    ws.current = null
  if (ws.currentV1 !== null) {
    ws.currentV1.close(true, true)
    ws.currentV1 = null
  }
  if (ws.currentV2 !== null) {
    ws.currentV2 = null
  }
})

+140 −0
Original line number Diff line number Diff line
@@ -779,6 +779,28 @@ __metadata:
  languageName: node
  linkType: hard

"@envelop/core@npm:^5.2.3":
  version: 5.2.3
  resolution: "@envelop/core@npm:5.2.3"
  dependencies:
    "@envelop/instrumentation": "npm:^1.0.0"
    "@envelop/types": "npm:^5.2.1"
    "@whatwg-node/promise-helpers": "npm:^1.2.4"
    tslib: "npm:^2.5.0"
  checksum: 10c0/77ba5807ddee5d08d6a4f47b2787735f0ad5aef71dcd809d51f5004f937de4c6a0b9a32651f2c6b81a0b9ef0510a917af408813c485e93da151c91d33b453061
  languageName: node
  linkType: hard

"@envelop/instrumentation@npm:^1.0.0":
  version: 1.0.0
  resolution: "@envelop/instrumentation@npm:1.0.0"
  dependencies:
    "@whatwg-node/promise-helpers": "npm:^1.2.1"
    tslib: "npm:^2.5.0"
  checksum: 10c0/134df1ac481fb392aafc4522a22bcdc6ef0701f2d15d51b16207f3c9a4c7d3760adfa5f5bcc84f0c0ec7b011d84bcd40fff671eb471bed54bd928c165994b4e3
  languageName: node
  linkType: hard

"@envelop/types@npm:5.0.0":
  version: 5.0.0
  resolution: "@envelop/types@npm:5.0.0"
@@ -788,6 +810,16 @@ __metadata:
  languageName: node
  linkType: hard

"@envelop/types@npm:^5.2.1":
  version: 5.2.1
  resolution: "@envelop/types@npm:5.2.1"
  dependencies:
    "@whatwg-node/promise-helpers": "npm:^1.0.0"
    tslib: "npm:^2.5.0"
  checksum: 10c0/2cdbb29d98350d957e18aff38ddf95670c249df894afab7fc888e2a02b43ca029fde96ca2829e5350bf83b982bc0267a5c8f7ee3ed9d353d4f145ebc0dc0b1e0
  languageName: node
  linkType: hard

"@esbuild/aix-ppc64@npm:0.24.2":
  version: 0.24.2
  resolution: "@esbuild/aix-ppc64@npm:0.24.2"
@@ -1458,6 +1490,35 @@ __metadata:
  languageName: node
  linkType: hard

"@graphql-tools/executor-common@npm:^0.0.4":
  version: 0.0.4
  resolution: "@graphql-tools/executor-common@npm:0.0.4"
  dependencies:
    "@envelop/core": "npm:^5.2.3"
    "@graphql-tools/utils": "npm:^10.8.1"
  peerDependencies:
    graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
  checksum: 10c0/4cda40687e3c42f0fefc9950f5e3d89021007101c3d6aa5dba2a07e6a0ef14cc0d04424aa8777e74476d5165fb22219de175dff8a4826e520fdbee8be0d4a81d
  languageName: node
  linkType: hard

"@graphql-tools/executor-graphql-ws@npm:^2.0.4":
  version: 2.0.4
  resolution: "@graphql-tools/executor-graphql-ws@npm:2.0.4"
  dependencies:
    "@graphql-tools/executor-common": "npm:^0.0.4"
    "@graphql-tools/utils": "npm:^10.8.1"
    "@whatwg-node/disposablestack": "npm:^0.0.6"
    graphql-ws: "npm:^6.0.3"
    isomorphic-ws: "npm:^5.0.0"
    tslib: "npm:^2.8.1"
    ws: "npm:^8.17.1"
  peerDependencies:
    graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
  checksum: 10c0/5cfa820ecb112629d18088a3c3814cf85464ae2c68839eaa8048984e28941302e812ccd1d0c7061f77747c2a39c7a9378c09147e212dc45c1472ba622bf13620
  languageName: node
  linkType: hard

"@graphql-tools/executor-http@npm:^1.2.4":
  version: 1.2.4
  resolution: "@graphql-tools/executor-http@npm:1.2.4"
@@ -1552,6 +1613,21 @@ __metadata:
  languageName: node
  linkType: hard

"@graphql-tools/utils@npm:^10.8.1":
  version: 10.8.4
  resolution: "@graphql-tools/utils@npm:10.8.4"
  dependencies:
    "@graphql-typed-document-node/core": "npm:^3.1.1"
    "@whatwg-node/promise-helpers": "npm:^1.0.0"
    cross-inspect: "npm:1.0.1"
    dset: "npm:^3.1.4"
    tslib: "npm:^2.4.0"
  peerDependencies:
    graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
  checksum: 10c0/c9bd473b79a6029194934965284df0c752cd44d607c65ebcfc20a2cacb9351859e97bab759faac1282db72e31ea6a7a87b0007671064bf3cbc89d907510e503a
  languageName: node
  linkType: hard

"@graphql-tools/wrap@npm:^10.0.27":
  version: 10.0.27
  resolution: "@graphql-tools/wrap@npm:10.0.27"
@@ -1816,6 +1892,7 @@ __metadata:
    "@graphql-tools/batch-execute": "npm:^9.0.11"
    "@graphql-tools/delegate": "npm:^10.2.9"
    "@graphql-tools/executor": "npm:^1.3.12"
    "@graphql-tools/executor-graphql-ws": "npm:^2.0.4"
    "@graphql-tools/executor-http": "npm:^1.2.4"
    "@graphql-tools/schema": "npm:^10.0.15"
    "@graphql-tools/stitch": "npm:^9.4.13"
@@ -1837,6 +1914,7 @@ __metadata:
    eslint-formatter-gitlab: "npm:^5.1.0"
    gql.tada: "npm:^1.8.10"
    graphql: "npm:16.10.0"
    graphql-ws: "npm:^6.0.4"
    lodash: "npm:4.17.21"
    react: "npm:18.3.1"
    react-dom: "npm:18.3.1"
@@ -4335,6 +4413,16 @@ __metadata:
  languageName: node
  linkType: hard

"@whatwg-node/disposablestack@npm:^0.0.6":
  version: 0.0.6
  resolution: "@whatwg-node/disposablestack@npm:0.0.6"
  dependencies:
    "@whatwg-node/promise-helpers": "npm:^1.0.0"
    tslib: "npm:^2.6.3"
  checksum: 10c0/e751da9f8552728f28a140fd78c1da88be167ee8a5688371da88e024a2bf151298d194a61c9750b44bbbb4cf5c687959d495d41b1388e4cfcfe9dbe3584c79b3
  languageName: node
  linkType: hard

"@whatwg-node/fetch@npm:^0.10.1":
  version: 0.10.3
  resolution: "@whatwg-node/fetch@npm:0.10.3"
@@ -4356,6 +4444,15 @@ __metadata:
  languageName: node
  linkType: hard

"@whatwg-node/promise-helpers@npm:^1.0.0, @whatwg-node/promise-helpers@npm:^1.2.1, @whatwg-node/promise-helpers@npm:^1.2.4":
  version: 1.2.4
  resolution: "@whatwg-node/promise-helpers@npm:1.2.4"
  dependencies:
    tslib: "npm:^2.6.3"
  checksum: 10c0/df62da1c9871506736942d704422436c56d77b0b0110203b8fffffd5750930a8d199eb5d93218a61aeb1f09bf6b315276e7257e19bcebb6f639b4d3a52171870
  languageName: node
  linkType: hard

"@yarnpkg/core@npm:^4.1.4, @yarnpkg/core@npm:^4.1.6":
  version: 4.2.0
  resolution: "@yarnpkg/core@npm:4.2.0"
@@ -7085,6 +7182,25 @@ __metadata:
  languageName: node
  linkType: hard

"graphql-ws@npm:^6.0.3, graphql-ws@npm:^6.0.4":
  version: 6.0.4
  resolution: "graphql-ws@npm:6.0.4"
  peerDependencies:
    "@fastify/websocket": ^10 || ^11
    graphql: ^15.10.1 || ^16
    uWebSockets.js: ^20
    ws: ^8
  peerDependenciesMeta:
    "@fastify/websocket":
      optional: true
    uWebSockets.js:
      optional: true
    ws:
      optional: true
  checksum: 10c0/ed17502300c702d42820ca2acc593d82acbcbec91fa93e588dc008d07d7b6914b4b22062f1ee181cff6ac62f69ea0052555ee75f270601311b943a6b7ef709dc
  languageName: node
  linkType: hard

"graphql@npm:16.10.0, graphql@npm:^15.5.0 || ^16.0.0 || ^17.0.0, graphql@npm:^16.10.0, graphql@npm:^16.8.1":
  version: 16.10.0
  resolution: "graphql@npm:16.10.0"
@@ -7686,6 +7802,15 @@ __metadata:
  languageName: node
  linkType: hard

"isomorphic-ws@npm:^5.0.0":
  version: 5.0.0
  resolution: "isomorphic-ws@npm:5.0.0"
  peerDependencies:
    ws: "*"
  checksum: 10c0/a058ac8b5e6efe9e46252cb0bc67fd325005d7216451d1a51238bc62d7da8486f828ef017df54ddf742e0fffcbe4b1bcc2a66cc115b027ed0180334cd18df252
  languageName: node
  linkType: hard

"iterall@npm:^1.2.1":
  version: 1.3.0
  resolution: "iterall@npm:1.3.0"
@@ -11359,6 +11484,21 @@ __metadata:
  languageName: node
  linkType: hard

"ws@npm:^8.17.1":
  version: 8.18.1
  resolution: "ws@npm:8.18.1"
  peerDependencies:
    bufferutil: ^4.0.1
    utf-8-validate: ">=5.0.2"
  peerDependenciesMeta:
    bufferutil:
      optional: true
    utf-8-validate:
      optional: true
  checksum: 10c0/e498965d6938c63058c4310ffb6967f07d4fa06789d3364829028af380d299fe05762961742971c764973dce3d1f6a2633fe8b2d9410c9b52e534b4b882a99fa
  languageName: node
  linkType: hard

"y18n@npm:^5.0.5":
  version: 5.0.8
  resolution: "y18n@npm:5.0.8"