@operato/graphql 10.29.23 → 10.29.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ import { getMainDefinition } from '@apollo/client/utilities/index.js';
9
9
  import { decodeHTML } from '@operato/utils';
10
10
  import { activeRequestCounterLink, decreaseActiveRequestCounter } from './active-request-counter-link.js';
11
11
  import { forbiddenMeaning, sayAddressRefused } from './forbidden.js';
12
+ import { uploadFetch } from './upload-progress.js';
12
13
  export const GRAPHQL_URI = '/graphql';
13
14
  export const SUBSCRIPTION_URI = GRAPHQL_URI;
14
15
  var apolloLink;
@@ -17,7 +18,9 @@ const httpOptions = {
17
18
  credentials: 'include',
18
19
  headers: { 'Apollo-Require-Preflight': 'true' }
19
20
  };
20
- const httpLink = ApolloLink.split(operation => operation.getContext().hasUpload, createUploadLink(httpOptions), new HttpLink(httpOptions));
21
+ const httpLink = ApolloLink.split(operation => operation.getContext().hasUpload,
22
+ /* uploadFetch: an upload that asks for progress goes through XHR; everything else is plain fetch. */
23
+ createUploadLink({ ...httpOptions, fetch: uploadFetch }), new HttpLink(httpOptions));
21
24
  const wsLink = new GraphQLWsLink(createWebSocketClient({
22
25
  url: location.origin.replace(/^http/, 'ws') + SUBSCRIPTION_URI,
23
26
  keepAlive: 10000,
@@ -1 +1 @@
1
- {"version":3,"file":"graphql-env.js","sourceRoot":"","sources":["../../src/graphql-env.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,2CAA2C,CAAA;AACxE,OAAO,EAAE,YAAY,IAAI,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElE,OAAO,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,EAER,UAAU,EAEV,KAAK,EACN,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAa,OAAO,EAAE,MAAM,oCAAoC,CAAA;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,8CAA8C,CAAA;AAC1F,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAA;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAA;AACzG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEpE,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAA;AACrC,MAAM,CAAC,MAAM,gBAAgB,GAAG,WAAW,CAAA;AAE3C,IAAI,UAAsB,CAAA;AAE1B,MAAM,WAAW,GAAgB;IAC/B,GAAG,EAAE,WAAW;IAChB,WAAW,EAAE,SAAS;IACtB,OAAO,EAAE,EAAE,0BAA0B,EAAE,MAAM,EAAE;CAChD,CAAA;AAED,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAC/B,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,SAAS,EAC7C,gBAAgB,CAAC,WAAW,CAAQ,EACpC,IAAI,QAAQ,CAAC,WAAW,CAAC,CAC1B,CAAA;AAED,MAAM,MAAM,GAAG,IAAI,aAAa,CAC9B,qBAAqB,CAAC;IACpB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,gBAAgB;IAC9D,SAAS,EAAE,KAAM;IACjB,aAAa,EAAE,OAAS;IACxB,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI;IACtB,gBAAgB,EAAE;QAChB,OAAO,EAAE;YACP;;;;;;;YAOA;YACA,OAAO,EAAE,QAAQ,CAAC,IAAI;SACvB;KACF;CACF,CAAC,CACH,CAAA;AAED,MAAM,aAAa,GAA2B,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE,EAAE;;IAC3F,oFAAoF;IACpF,IAAI,aAAa,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC;QAC9E,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;YACxB,MAAM,EAAE;gBACN,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;gBAC7C,EAAE,EAAE,aAAa;gBACjB;;;;mBAIG;gBACH,MAAM,EAAE;oBACN,KAAK,EAAE,CAAC,CAAC,CAAC,qBAAqB;iBAChC;aACF;SACF,CAAC,CACH,CAAA;IACH,CAAC;IAED,IAAI,YAAY,EAAE,CAAC;QACjB,4BAA4B,EAAE,CAAA;QAE9B,4EAA4E;QAC5E,QAAS,YAAiC,CAAC,UAAU,EAAE,CAAC;YACtD,KAAK,SAAS,CAAC,mDAAmD;gBAChE,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;oBACxB,MAAM,EAAE;wBACN,KAAK,EAAE,OAAO;wBACd,OAAO,EAAE,YAAY,CAAC,OAAO;wBAC7B,EAAE,EAAE,YAAY;qBACjB;iBACF,CAAC,CACH,CAAA;gBACD,MAAK;YAEP,KAAK,GAAG;gBACN,+FAA+F;gBAC/F,QAAQ,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,eAAe,CAAC,CAAC,CAAA;gBACxD,MAAK;YAEP,KAAK,GAAG;gBACN;;;;mBAIG;gBACH,IAAI,gBAAgB,CAAC,MAAC,YAAiC,CAAC,QAAQ,0CAAE,OAAO,CAAC,KAAK,iBAAiB,EAAE,CAAC;oBACjG,iBAAiB,EAAE,CAAA;gBACrB,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,iBAAiB,CAAC,CAAC,CAAA;gBAC5D,CAAC;gBACD,MAAK;YAEP;gBACE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAgC,CAAA;gBACxF,IAAI,IAAI,IAAI,kBAAkB,EAAE,CAAC;oBAC/B,OAAO,GAAG,KAAK,UAAU,IAAI,EAAE,MAAM,QAAQ,CAAC,UAAU,MAAM,QAAQ,EAAE,CAAA;gBAC1E,CAAC;qBAAM,CAAC;oBACN,kDAAkD;oBAClD,OAAO,GAAG,KAAK,UAAU,IAAI,EAAE,MAAM,QAAQ,CAAC,UAAU,MAAM,OAAO,EAAE,CAAA;gBACzE,CAAC;gBAED,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,QAAQ,EAAE;oBACxB,MAAM,EAAE;wBACN,KAAK,EAAE,OAAO;wBACd,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC;wBAC5B,EAAE,EAAE,YAAY;qBACjB;iBACF,CAAC,CACH,CAAA;QACL,CAAC;IACH,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAgB,EAAE,EAAE;IAChD,UAAU,GAAG,IAAI,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE;IAChC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,KAAK,CAChB,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;YACZ,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;YACpC,OAAO,GAAG,CAAC,IAAI,KAAK,qBAAqB,IAAI,GAAG,CAAC,SAAS,KAAK,cAAc,CAAA;QAC/E,CAAC,EACD,MAAM,EACN,IAAI,CAAC;YACH,wBAAwB;YACxB,OAAO,CAAC,aAAa,CAAC;YACtB,6DAA6D;YAC7D,wDAAwD;YACxD,0BAA0B;YAC1B,2BAA2B,EAAE;YAC7B,QAAQ;SACT,CAAC,CACH,CAAA;IACH,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,kBAAuB,EAAE,EAAE,EAAE,CAC1D,IAAI,UAAU,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE;IACpC,OAAO,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC/B,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAA;QAE7C,IAAI,eAAe,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,eAAe,CAAC,aAAa,CAAC;aACrC,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,kCAAkC,aAAa,EAAE,CAAC,CAAC,CAAA;QAC9E,CAAC;QAED,QAAQ,CAAC,QAAQ,EAAE,CAAA;IACrB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'\nimport { createClient as createWebSocketClient } from 'graphql-ws'\n\nimport {\n ApolloLink,\n from,\n HttpLink,\n HttpOptions,\n Observable,\n ServerParseError,\n split\n} from '@apollo/client/core/index.js'\nimport { ErrorLink, onError } from '@apollo/client/link/error/index.js'\nimport { isHandledRefusal } from './handled-refusal.js'\nimport { removeTypenameFromVariables } from '@apollo/client/link/remove-typename/index.js'\nimport { GraphQLWsLink } from '@apollo/client/link/subscriptions/index.js'\nimport { getMainDefinition } from '@apollo/client/utilities/index.js'\n\nimport { decodeHTML } from '@operato/utils'\nimport { activeRequestCounterLink, decreaseActiveRequestCounter } from './active-request-counter-link.js'\nimport { forbiddenMeaning, sayAddressRefused } from './forbidden.js'\n\nexport const GRAPHQL_URI = '/graphql'\nexport const SUBSCRIPTION_URI = GRAPHQL_URI\n\nvar apolloLink: ApolloLink\n\nconst httpOptions: HttpOptions = {\n uri: GRAPHQL_URI,\n credentials: 'include',\n headers: { 'Apollo-Require-Preflight': 'true' }\n}\n\nconst httpLink = ApolloLink.split(\n operation => operation.getContext().hasUpload,\n createUploadLink(httpOptions) as any,\n new HttpLink(httpOptions)\n)\n\nconst wsLink = new GraphQLWsLink(\n createWebSocketClient({\n url: location.origin.replace(/^http/, 'ws') + SUBSCRIPTION_URI,\n keepAlive: 10_000,\n retryAttempts: 1_000_000,\n shouldRetry: e => true,\n connectionParams: {\n headers: {\n /* \n 특정 도메인의 데이타만 받고자 하는 경우에, referer 정보를 제공해서 서버에서 서브도메인 정보를 취득하도록 한다.\n referer: location.href\n 또는, 이미 서브도메인 정보를 알고 있다면,\n 'x-things-factory-domain': '[subdomain]'\n 을 보낼 수 있다.\n 관련 정보를 보내지 않는다면, 사용자가 권한을 가진 모든 도메인의 데이타를 수신하게 된다.\n */\n referer: location.href\n }\n }\n })\n)\n\nconst ERROR_HANDLER: ErrorLink.ErrorHandler = ({ operation, graphQLErrors, networkError }) => {\n /* A screen that names this refusal says it in its own body (handled-refusal.ts). */\n if (graphQLErrors && !isHandledRefusal(operation.getContext(), graphQLErrors)) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: decodeHTML(graphQLErrors[0].message),\n ex: graphQLErrors,\n /*\n * Closed by the snackbar's own ✕. A 'Close' action here did the same thing twice and took the one\n * button slot - a package that knows what an error means (auth-ui, for a refusal a role would open)\n * fills it through registerNotifyAction. This link reports errors; it does not interpret them.\n */\n option: {\n timer: -1 // disable auto close\n }\n }\n })\n )\n }\n\n if (networkError) {\n decreaseActiveRequestCounter()\n\n /* networkError가 ServerParseError 이거나 ServerError 인 경우에만 statusCode를 갖는다. */\n switch ((networkError as ServerParseError).statusCode) {\n case undefined /* in case this error is not a server side error */:\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: networkError.message,\n ex: networkError\n }\n })\n )\n break\n\n case 401:\n /* 401 에러가 리턴되면, 인증이 필요하다는 메시지를 dispatch 한다. 이 auth 모듈 등에서 이 메시지를 받아서 signin 프로세스를 진행할 수 있다. */\n document.dispatchEvent(new CustomEvent('auth-required'))\n break\n\n case 403:\n /*\n * 403 은 둘이다 — 도메인이 아직 없는 것과, 이 주소를 사업장이 받지 않는 것(forbidden.ts).\n * 앞의 것은 domain-required 를 보내 auth 가 체크인으로 보낸다. 뒤의 것은 체크인이 풀 수 없으므로\n * 보내지 않고 한 번 말한다 — 보내면 체크인과 화면 사이를 돈다.\n */\n if (forbiddenMeaning((networkError as ServerParseError).response?.headers) === 'address-refused') {\n sayAddressRefused()\n } else {\n document.dispatchEvent(new CustomEvent('domain-required'))\n }\n break\n\n default:\n var { name, response, statusCode, bodyText, message } = networkError as ServerParseError\n if (name == 'ServerParseError') {\n message = `[ ${statusCode || ''} : ${response.statusText} ] ${bodyText}`\n } else {\n /* in case this error is instanceof ServerError */\n message = `[ ${statusCode || ''} : ${response.statusText} ] ${message}`\n }\n\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: decodeHTML(message),\n ex: networkError\n }\n })\n )\n }\n }\n}\n\nexport const setClientLink = (link: ApolloLink) => {\n apolloLink = link\n}\n\nexport const getClientLink = () => {\n if (!apolloLink) {\n apolloLink = split(\n ({ query }) => {\n const def = getMainDefinition(query)\n return def.kind === 'OperationDefinition' && def.operation === 'subscription'\n },\n wsLink,\n from([\n activeRequestCounterLink,\n onError(ERROR_HANDLER),\n // Apollo 가 InMemoryCache 정규화를 위해 응답 객체에 자동 삽입한 __typename 이,\n // 호출자가 그 응답을 mutation input 변수로 재전송할 때 strict 검증에서 거부되는\n // 문제를 프레임워크 레벨에서 일괄 제거한다.\n removeTypenameFromVariables(),\n httpLink\n ])\n )\n }\n\n return apolloLink\n}\n\nexport const createMockLink = (mockedResponses: any = {}) =>\n new ApolloLink((operation, forward) => {\n return new Observable(observer => {\n const operationName = operation.operationName\n\n if (mockedResponses[operationName]) {\n observer.next({\n data: mockedResponses[operationName]\n })\n } else {\n observer.error(new Error(`No mock response for operation ${operationName}`))\n }\n\n observer.complete()\n })\n })\n"]}
1
+ {"version":3,"file":"graphql-env.js","sourceRoot":"","sources":["../../src/graphql-env.ts"],"names":[],"mappings":"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","sourcesContent":["import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'\nimport { createClient as createWebSocketClient } from 'graphql-ws'\n\nimport {\n ApolloLink,\n from,\n HttpLink,\n HttpOptions,\n Observable,\n ServerParseError,\n split\n} from '@apollo/client/core/index.js'\nimport { ErrorLink, onError } from '@apollo/client/link/error/index.js'\nimport { isHandledRefusal } from './handled-refusal.js'\nimport { removeTypenameFromVariables } from '@apollo/client/link/remove-typename/index.js'\nimport { GraphQLWsLink } from '@apollo/client/link/subscriptions/index.js'\nimport { getMainDefinition } from '@apollo/client/utilities/index.js'\n\nimport { decodeHTML } from '@operato/utils'\nimport { activeRequestCounterLink, decreaseActiveRequestCounter } from './active-request-counter-link.js'\nimport { forbiddenMeaning, sayAddressRefused } from './forbidden.js'\nimport { uploadFetch } from './upload-progress.js'\n\nexport const GRAPHQL_URI = '/graphql'\nexport const SUBSCRIPTION_URI = GRAPHQL_URI\n\nvar apolloLink: ApolloLink\n\nconst httpOptions: HttpOptions = {\n uri: GRAPHQL_URI,\n credentials: 'include',\n headers: { 'Apollo-Require-Preflight': 'true' }\n}\n\nconst httpLink = ApolloLink.split(\n operation => operation.getContext().hasUpload,\n /* uploadFetch: an upload that asks for progress goes through XHR; everything else is plain fetch. */\n createUploadLink({ ...httpOptions, fetch: uploadFetch }) as any,\n new HttpLink(httpOptions)\n)\n\nconst wsLink = new GraphQLWsLink(\n createWebSocketClient({\n url: location.origin.replace(/^http/, 'ws') + SUBSCRIPTION_URI,\n keepAlive: 10_000,\n retryAttempts: 1_000_000,\n shouldRetry: e => true,\n connectionParams: {\n headers: {\n /* \n 특정 도메인의 데이타만 받고자 하는 경우에, referer 정보를 제공해서 서버에서 서브도메인 정보를 취득하도록 한다.\n referer: location.href\n 또는, 이미 서브도메인 정보를 알고 있다면,\n 'x-things-factory-domain': '[subdomain]'\n 을 보낼 수 있다.\n 관련 정보를 보내지 않는다면, 사용자가 권한을 가진 모든 도메인의 데이타를 수신하게 된다.\n */\n referer: location.href\n }\n }\n })\n)\n\nconst ERROR_HANDLER: ErrorLink.ErrorHandler = ({ operation, graphQLErrors, networkError }) => {\n /* A screen that names this refusal says it in its own body (handled-refusal.ts). */\n if (graphQLErrors && !isHandledRefusal(operation.getContext(), graphQLErrors)) {\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: decodeHTML(graphQLErrors[0].message),\n ex: graphQLErrors,\n /*\n * Closed by the snackbar's own ✕. A 'Close' action here did the same thing twice and took the one\n * button slot - a package that knows what an error means (auth-ui, for a refusal a role would open)\n * fills it through registerNotifyAction. This link reports errors; it does not interpret them.\n */\n option: {\n timer: -1 // disable auto close\n }\n }\n })\n )\n }\n\n if (networkError) {\n decreaseActiveRequestCounter()\n\n /* networkError가 ServerParseError 이거나 ServerError 인 경우에만 statusCode를 갖는다. */\n switch ((networkError as ServerParseError).statusCode) {\n case undefined /* in case this error is not a server side error */:\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: networkError.message,\n ex: networkError\n }\n })\n )\n break\n\n case 401:\n /* 401 에러가 리턴되면, 인증이 필요하다는 메시지를 dispatch 한다. 이 auth 모듈 등에서 이 메시지를 받아서 signin 프로세스를 진행할 수 있다. */\n document.dispatchEvent(new CustomEvent('auth-required'))\n break\n\n case 403:\n /*\n * 403 은 둘이다 — 도메인이 아직 없는 것과, 이 주소를 사업장이 받지 않는 것(forbidden.ts).\n * 앞의 것은 domain-required 를 보내 auth 가 체크인으로 보낸다. 뒤의 것은 체크인이 풀 수 없으므로\n * 보내지 않고 한 번 말한다 — 보내면 체크인과 화면 사이를 돈다.\n */\n if (forbiddenMeaning((networkError as ServerParseError).response?.headers) === 'address-refused') {\n sayAddressRefused()\n } else {\n document.dispatchEvent(new CustomEvent('domain-required'))\n }\n break\n\n default:\n var { name, response, statusCode, bodyText, message } = networkError as ServerParseError\n if (name == 'ServerParseError') {\n message = `[ ${statusCode || ''} : ${response.statusText} ] ${bodyText}`\n } else {\n /* in case this error is instanceof ServerError */\n message = `[ ${statusCode || ''} : ${response.statusText} ] ${message}`\n }\n\n document.dispatchEvent(\n new CustomEvent('notify', {\n detail: {\n level: 'error',\n message: decodeHTML(message),\n ex: networkError\n }\n })\n )\n }\n }\n}\n\nexport const setClientLink = (link: ApolloLink) => {\n apolloLink = link\n}\n\nexport const getClientLink = () => {\n if (!apolloLink) {\n apolloLink = split(\n ({ query }) => {\n const def = getMainDefinition(query)\n return def.kind === 'OperationDefinition' && def.operation === 'subscription'\n },\n wsLink,\n from([\n activeRequestCounterLink,\n onError(ERROR_HANDLER),\n // Apollo 가 InMemoryCache 정규화를 위해 응답 객체에 자동 삽입한 __typename 이,\n // 호출자가 그 응답을 mutation input 변수로 재전송할 때 strict 검증에서 거부되는\n // 문제를 프레임워크 레벨에서 일괄 제거한다.\n removeTypenameFromVariables(),\n httpLink\n ])\n )\n }\n\n return apolloLink\n}\n\nexport const createMockLink = (mockedResponses: any = {}) =>\n new ApolloLink((operation, forward) => {\n return new Observable(observer => {\n const operationName = operation.operationName\n\n if (mockedResponses[operationName]) {\n observer.next({\n data: mockedResponses[operationName]\n })\n } else {\n observer.error(new Error(`No mock response for operation ${operationName}`))\n }\n\n observer.complete()\n })\n })\n"]}
@@ -5,3 +5,4 @@ export * from './active-request-counter-link.js';
5
5
  export * from './use-query.js';
6
6
  export * from './forbidden.js';
7
7
  export * from './handled-refusal.js';
8
+ export * from './upload-progress.js';
package/dist/src/index.js CHANGED
@@ -5,4 +5,5 @@ export * from './active-request-counter-link.js';
5
5
  export * from './use-query.js';
6
6
  export * from './forbidden.js';
7
7
  export * from './handled-refusal.js';
8
+ export * from './upload-progress.js';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kCAAkC,CAAA;AAChD,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './gql-context.js'\nexport * from './graphql-client.js'\nexport * from './json-to-grqphql-query.js'\nexport * from './active-request-counter-link.js'\nexport * from './use-query.js'\nexport * from './forbidden.js'\nexport * from './handled-refusal.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kCAAkC,CAAA;AAChD,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './gql-context.js'\nexport * from './graphql-client.js'\nexport * from './json-to-grqphql-query.js'\nexport * from './active-request-counter-link.js'\nexport * from './use-query.js'\nexport * from './forbidden.js'\nexport * from './handled-refusal.js'\nexport * from './upload-progress.js'\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Upload progress — the one place a file upload reports how far it got.
3
+ *
4
+ * `fetch` cannot tell how much of a request body has gone out; `XMLHttpRequest` can. The upload
5
+ * link (graphql-env.ts) sends with `uploadFetch`: a request that asks for progress goes through
6
+ * XHR, every other request goes through `fetch` exactly as before. Screens never write their own XHR.
7
+ *
8
+ * ```ts
9
+ * client.mutate({ mutation, variables: { files }, context: uploadContext(fraction => (picker.progress = fraction)) })
10
+ * ```
11
+ */
12
+ export type UploadProgress = (fraction: number) => void;
13
+ type UploadInit = RequestInit & {
14
+ onUploadProgress?: UploadProgress;
15
+ };
16
+ /** The mutation context for an upload whose progress a screen shows. */
17
+ export declare function uploadContext(onUploadProgress: UploadProgress): {
18
+ hasUpload: boolean;
19
+ fetchOptions: {
20
+ onUploadProgress: UploadProgress;
21
+ };
22
+ };
23
+ export declare function uploadFetch(uri: RequestInfo | URL, init?: UploadInit): Promise<Response>;
24
+ export {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Upload progress — the one place a file upload reports how far it got.
3
+ *
4
+ * `fetch` cannot tell how much of a request body has gone out; `XMLHttpRequest` can. The upload
5
+ * link (graphql-env.ts) sends with `uploadFetch`: a request that asks for progress goes through
6
+ * XHR, every other request goes through `fetch` exactly as before. Screens never write their own XHR.
7
+ *
8
+ * ```ts
9
+ * client.mutate({ mutation, variables: { files }, context: uploadContext(fraction => (picker.progress = fraction)) })
10
+ * ```
11
+ */
12
+ /** The mutation context for an upload whose progress a screen shows. */
13
+ export function uploadContext(onUploadProgress) {
14
+ return { hasUpload: true, fetchOptions: { onUploadProgress } };
15
+ }
16
+ const responseHeaders = (raw) => {
17
+ const headers = new Headers();
18
+ raw
19
+ .trim()
20
+ .split(/[\r\n]+/)
21
+ .filter(Boolean)
22
+ .forEach(line => {
23
+ const at = line.indexOf(':');
24
+ if (at > 0)
25
+ headers.append(line.slice(0, at).trim(), line.slice(at + 1).trim());
26
+ });
27
+ return headers;
28
+ };
29
+ export function uploadFetch(uri, init = {}) {
30
+ const { onUploadProgress, ...options } = init;
31
+ if (!onUploadProgress) {
32
+ return fetch(uri, options);
33
+ }
34
+ return new Promise((resolve, reject) => {
35
+ const xhr = new XMLHttpRequest();
36
+ const signal = options.signal;
37
+ const abort = () => xhr.abort();
38
+ xhr.open(options.method || 'POST', String(uri));
39
+ xhr.withCredentials = options.credentials === 'include';
40
+ new Headers(options.headers).forEach((value, name) => xhr.setRequestHeader(name, value));
41
+ xhr.upload.onprogress = e => {
42
+ if (e.lengthComputable && e.total > 0)
43
+ onUploadProgress(e.loaded / e.total);
44
+ };
45
+ xhr.onload = () => {
46
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', abort);
47
+ onUploadProgress(1);
48
+ resolve(new Response(xhr.responseText, {
49
+ status: xhr.status,
50
+ statusText: xhr.statusText,
51
+ headers: responseHeaders(xhr.getAllResponseHeaders())
52
+ }));
53
+ };
54
+ /* The same failure `fetch` gives, so the link reports it the same way. */
55
+ xhr.onerror = () => {
56
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', abort);
57
+ reject(new TypeError('Network request failed'));
58
+ };
59
+ xhr.onabort = () => {
60
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', abort);
61
+ reject(new DOMException('The upload was aborted.', 'AbortError'));
62
+ };
63
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
64
+ xhr.abort();
65
+ return;
66
+ }
67
+ signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', abort, { once: true });
68
+ xhr.send(options.body);
69
+ });
70
+ }
71
+ //# sourceMappingURL=upload-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-progress.js","sourceRoot":"","sources":["../../src/upload-progress.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Upload progress — the one place a file upload reports how far it got.\n *\n * `fetch` cannot tell how much of a request body has gone out; `XMLHttpRequest` can. The upload\n * link (graphql-env.ts) sends with `uploadFetch`: a request that asks for progress goes through\n * XHR, every other request goes through `fetch` exactly as before. Screens never write their own XHR.\n *\n * ```ts\n * client.mutate({ mutation, variables: { files }, context: uploadContext(fraction => (picker.progress = fraction)) })\n * ```\n */\n\nexport type UploadProgress = (fraction: number) => void\n\ntype UploadInit = RequestInit & { onUploadProgress?: UploadProgress }\n\n/** The mutation context for an upload whose progress a screen shows. */\nexport function uploadContext(onUploadProgress: UploadProgress) {\n return { hasUpload: true, fetchOptions: { onUploadProgress } }\n}\n\nconst responseHeaders = (raw: string) => {\n const headers = new Headers()\n\n raw\n .trim()\n .split(/[\\r\\n]+/)\n .filter(Boolean)\n .forEach(line => {\n const at = line.indexOf(':')\n if (at > 0) headers.append(line.slice(0, at).trim(), line.slice(at + 1).trim())\n })\n\n return headers\n}\n\nexport function uploadFetch(uri: RequestInfo | URL, init: UploadInit = {}): Promise<Response> {\n const { onUploadProgress, ...options } = init\n\n if (!onUploadProgress) {\n return fetch(uri, options)\n }\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest()\n const signal = options.signal\n\n const abort = () => xhr.abort()\n\n xhr.open(options.method || 'POST', String(uri))\n xhr.withCredentials = options.credentials === 'include'\n new Headers(options.headers).forEach((value, name) => xhr.setRequestHeader(name, value))\n\n xhr.upload.onprogress = e => {\n if (e.lengthComputable && e.total > 0) onUploadProgress(e.loaded / e.total)\n }\n\n xhr.onload = () => {\n signal?.removeEventListener('abort', abort)\n onUploadProgress(1)\n resolve(\n new Response(xhr.responseText, {\n status: xhr.status,\n statusText: xhr.statusText,\n headers: responseHeaders(xhr.getAllResponseHeaders())\n })\n )\n }\n\n /* The same failure `fetch` gives, so the link reports it the same way. */\n xhr.onerror = () => {\n signal?.removeEventListener('abort', abort)\n reject(new TypeError('Network request failed'))\n }\n\n xhr.onabort = () => {\n signal?.removeEventListener('abort', abort)\n reject(new DOMException('The upload was aborted.', 'AbortError'))\n }\n\n if (signal?.aborted) {\n xhr.abort()\n return\n }\n signal?.addEventListener('abort', abort, { once: true })\n\n xhr.send(options.body as XMLHttpRequestBodyInit)\n })\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,130 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import gql from 'graphql-tag';
3
+ import { execute } from '@apollo/client/core/index.js';
4
+ import { getClientLink } from '../src/graphql-env.js';
5
+ import { uploadContext, uploadFetch } from '../src/upload-progress.js';
6
+ /**
7
+ * Upload progress.
8
+ *
9
+ * ## Why this is tested
10
+ *
11
+ * Only XHR can tell how much of a body went out, so an upload that asks for progress leaves `fetch`.
12
+ * What must hold:
13
+ *
14
+ * ```
15
+ * unchanged a request that does not ask for progress — every query, every plain upload — still
16
+ * goes through fetch with the same arguments; no XHR is made
17
+ * progress an upload that asks gets fractions up to 1, and the same Response fetch would give
18
+ * credentials the session cookie still goes (credentials: include → withCredentials)
19
+ * abort the link's abort stops the XHR and fails like an aborted fetch
20
+ * ```
21
+ */
22
+ class FakeXHR {
23
+ constructor() {
24
+ this.method = '';
25
+ this.url = '';
26
+ this.withCredentials = false;
27
+ this.headers = {};
28
+ this.aborted = false;
29
+ this.status = 200;
30
+ this.statusText = 'OK';
31
+ this.responseText = '{"data":{"attach":{"id":"1"}}}';
32
+ this.upload = {};
33
+ FakeXHR.made.push(this);
34
+ }
35
+ open(method, url) {
36
+ this.method = method;
37
+ this.url = url;
38
+ }
39
+ setRequestHeader(name, value) {
40
+ this.headers[name] = value;
41
+ }
42
+ getAllResponseHeaders() {
43
+ return 'content-type: application/json\r\nx-one: 1\r\n';
44
+ }
45
+ send(body) {
46
+ this.body = body;
47
+ setTimeout(() => {
48
+ var _a, _b, _c;
49
+ (_b = (_a = this.upload).onprogress) === null || _b === void 0 ? void 0 : _b.call(_a, { lengthComputable: true, loaded: 50, total: 100 });
50
+ (_c = this.onload) === null || _c === void 0 ? void 0 : _c.call(this);
51
+ });
52
+ }
53
+ abort() {
54
+ var _a;
55
+ this.aborted = true;
56
+ (_a = this.onabort) === null || _a === void 0 ? void 0 : _a.call(this);
57
+ }
58
+ }
59
+ FakeXHR.made = [];
60
+ describe('upload progress', () => {
61
+ const realXHR = window.XMLHttpRequest;
62
+ const realFetch = window.fetch;
63
+ let fetched;
64
+ beforeEach(() => {
65
+ FakeXHR.made = [];
66
+ fetched = [];
67
+ window.XMLHttpRequest = FakeXHR;
68
+ window.fetch = (async (...args) => {
69
+ fetched.push(args);
70
+ return new Response('{"data":{"one":1}}', { status: 200, headers: { 'content-type': 'application/json' } });
71
+ });
72
+ });
73
+ afterEach(() => {
74
+ window.XMLHttpRequest = realXHR;
75
+ window.fetch = realFetch;
76
+ });
77
+ it('leaves a request without progress to fetch, as it was', async () => {
78
+ const init = { method: 'POST', body: 'x', credentials: 'include' };
79
+ await uploadFetch('/graphql', init);
80
+ expect(FakeXHR.made.length).to.equal(0);
81
+ expect(fetched.length).to.equal(1);
82
+ expect(fetched[0][0]).to.equal('/graphql');
83
+ expect(fetched[0][1]).to.deep.equal(init);
84
+ });
85
+ it('reports progress and answers with the response fetch would give', async () => {
86
+ const fractions = [];
87
+ const response = await uploadFetch('/graphql', {
88
+ method: 'POST',
89
+ credentials: 'include',
90
+ headers: { 'Apollo-Require-Preflight': 'true' },
91
+ body: 'form',
92
+ onUploadProgress: (fraction) => fractions.push(fraction)
93
+ });
94
+ const xhr = FakeXHR.made[0];
95
+ expect(xhr.withCredentials, 'the session cookie stays home').to.equal(true);
96
+ expect(xhr.headers['apollo-require-preflight']).to.equal('true');
97
+ expect(xhr.body).to.equal('form');
98
+ expect(fractions).to.deep.equal([0.5, 1]);
99
+ expect(response.status).to.equal(200);
100
+ expect(response.headers.get('x-one')).to.equal('1');
101
+ expect(await response.json()).to.deep.equal({ data: { attach: { id: '1' } } });
102
+ });
103
+ it('stops the upload when aborted', async () => {
104
+ const controller = new AbortController();
105
+ const sent = uploadFetch('/graphql', { method: 'POST', body: 'form', signal: controller.signal, onUploadProgress: () => { } });
106
+ controller.abort();
107
+ const error = await sent.then(() => null, e => e);
108
+ expect(FakeXHR.made[0].aborted).to.equal(true);
109
+ expect(error === null || error === void 0 ? void 0 : error.name).to.equal('AbortError');
110
+ });
111
+ it('sends a query through fetch and an upload with progress through XHR', async () => {
112
+ const run = (operation) => new Promise((resolve, reject) => execute(getClientLink(), operation).subscribe({ next: resolve, error: reject }));
113
+ await run({ query: gql `query { one }` });
114
+ expect(fetched.length, 'a query left fetch').to.equal(1);
115
+ expect(FakeXHR.made.length, 'a query went through XHR').to.equal(0);
116
+ const fractions = [];
117
+ const file = new File(['abc'], 'a.pdf', { type: 'application/pdf' });
118
+ const result = await run({
119
+ query: gql `mutation ($file: Upload!) { attach(file: $file) { id } }`,
120
+ variables: { file },
121
+ context: uploadContext(fraction => fractions.push(fraction))
122
+ });
123
+ expect(fetched.length, 'the upload went through fetch').to.equal(1);
124
+ expect(FakeXHR.made.length).to.equal(1);
125
+ expect(FakeXHR.made[0].body).to.be.instanceOf(FormData);
126
+ expect(fractions).to.deep.equal([0.5, 1]);
127
+ expect(result.data.attach.id).to.equal('1');
128
+ });
129
+ });
130
+ //# sourceMappingURL=upload-progress.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-progress.test.js","sourceRoot":"","sources":["../../test/upload-progress.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\nimport gql from 'graphql-tag'\n\nimport { execute } from '@apollo/client/core/index.js'\n\nimport { getClientLink } from '../src/graphql-env.js'\nimport { uploadContext, uploadFetch } from '../src/upload-progress.js'\n\n/**\n * Upload progress.\n *\n * ## Why this is tested\n *\n * Only XHR can tell how much of a body went out, so an upload that asks for progress leaves `fetch`.\n * What must hold:\n *\n * ```\n * unchanged a request that does not ask for progress — every query, every plain upload — still\n * goes through fetch with the same arguments; no XHR is made\n * progress an upload that asks gets fractions up to 1, and the same Response fetch would give\n * credentials the session cookie still goes (credentials: include → withCredentials)\n * abort the link's abort stops the XHR and fails like an aborted fetch\n * ```\n */\n\nclass FakeXHR {\n static made: FakeXHR[] = []\n\n method = ''\n url = ''\n withCredentials = false\n headers: Record<string, string> = {}\n body: any\n aborted = false\n status = 200\n statusText = 'OK'\n responseText = '{\"data\":{\"attach\":{\"id\":\"1\"}}}'\n upload: { onprogress?: (e: any) => void } = {}\n onload?: () => void\n onerror?: () => void\n onabort?: () => void\n\n constructor() {\n FakeXHR.made.push(this)\n }\n open(method: string, url: string) {\n this.method = method\n this.url = url\n }\n setRequestHeader(name: string, value: string) {\n this.headers[name] = value\n }\n getAllResponseHeaders() {\n return 'content-type: application/json\\r\\nx-one: 1\\r\\n'\n }\n send(body: any) {\n this.body = body\n setTimeout(() => {\n this.upload.onprogress?.({ lengthComputable: true, loaded: 50, total: 100 })\n this.onload?.()\n })\n }\n abort() {\n this.aborted = true\n this.onabort?.()\n }\n}\n\ndescribe('upload progress', () => {\n const realXHR = window.XMLHttpRequest\n const realFetch = window.fetch\n let fetched: any[]\n\n beforeEach(() => {\n FakeXHR.made = []\n fetched = []\n ;(window as any).XMLHttpRequest = FakeXHR\n window.fetch = (async (...args: any[]) => {\n fetched.push(args)\n return new Response('{\"data\":{\"one\":1}}', { status: 200, headers: { 'content-type': 'application/json' } })\n }) as any\n })\n\n afterEach(() => {\n window.XMLHttpRequest = realXHR\n window.fetch = realFetch\n })\n\n it('leaves a request without progress to fetch, as it was', async () => {\n const init = { method: 'POST', body: 'x', credentials: 'include' as RequestCredentials }\n await uploadFetch('/graphql', init)\n\n expect(FakeXHR.made.length).to.equal(0)\n expect(fetched.length).to.equal(1)\n expect(fetched[0][0]).to.equal('/graphql')\n expect(fetched[0][1]).to.deep.equal(init)\n })\n\n it('reports progress and answers with the response fetch would give', async () => {\n const fractions: number[] = []\n const response = await uploadFetch('/graphql', {\n method: 'POST',\n credentials: 'include',\n headers: { 'Apollo-Require-Preflight': 'true' },\n body: 'form',\n onUploadProgress: (fraction: number) => fractions.push(fraction)\n } as any)\n\n const xhr = FakeXHR.made[0]\n expect(xhr.withCredentials, 'the session cookie stays home').to.equal(true)\n expect(xhr.headers['apollo-require-preflight']).to.equal('true')\n expect(xhr.body).to.equal('form')\n expect(fractions).to.deep.equal([0.5, 1])\n\n expect(response.status).to.equal(200)\n expect(response.headers.get('x-one')).to.equal('1')\n expect(await response.json()).to.deep.equal({ data: { attach: { id: '1' } } })\n })\n\n it('stops the upload when aborted', async () => {\n const controller = new AbortController()\n const sent = uploadFetch('/graphql', { method: 'POST', body: 'form', signal: controller.signal, onUploadProgress: () => {} } as any)\n\n controller.abort()\n\n const error = await sent.then(\n () => null,\n e => e\n )\n expect(FakeXHR.made[0].aborted).to.equal(true)\n expect(error?.name).to.equal('AbortError')\n })\n\n it('sends a query through fetch and an upload with progress through XHR', async () => {\n const run = (operation: any) =>\n new Promise((resolve, reject) => execute(getClientLink(), operation).subscribe({ next: resolve, error: reject }))\n\n await run({ query: gql`query { one }` })\n expect(fetched.length, 'a query left fetch').to.equal(1)\n expect(FakeXHR.made.length, 'a query went through XHR').to.equal(0)\n\n const fractions: number[] = []\n const file = new File(['abc'], 'a.pdf', { type: 'application/pdf' })\n const result: any = await run({\n query: gql`mutation ($file: Upload!) { attach(file: $file) { id } }`,\n variables: { file },\n context: uploadContext(fraction => fractions.push(fraction))\n })\n\n expect(fetched.length, 'the upload went through fetch').to.equal(1)\n expect(FakeXHR.made.length).to.equal(1)\n expect(FakeXHR.made[0].body).to.be.instanceOf(FormData)\n expect(fractions).to.deep.equal([0.5, 1])\n expect(result.data.attach.id).to.equal('1')\n })\n})\n"]}