@fluojs/graphql 1.0.0-beta.4 → 1.0.0-beta.6

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.
package/README.ko.md CHANGED
@@ -33,6 +33,8 @@ pnpm add @fluojs/graphql graphql graphql-yoga
33
33
 
34
34
  `GraphqlModule.forRoot(...)`를 등록하고 표준 데코레이터를 사용하여 resolver를 정의합니다. 현재 `@fluojs/graphql`는 동기 모듈 엔트리포인트만 제공하며 `GraphqlModule.forRootAsync(...)` 계약은 없습니다.
35
35
 
36
+ Code-first resolver discovery 대신 schema-first 통합을 원하면 executable `GraphQLSchema`를 `schema`로 전달할 수도 있습니다.
37
+
36
38
  ```typescript
37
39
  import { Module } from '@fluojs/core';
38
40
  import { bootstrapNodeApplication } from '@fluojs/runtime/node';
@@ -133,7 +135,7 @@ class RequestResolver {
133
135
  ### 프로토콜 지원
134
136
  - **HTTP**: 표준 GET/POST 쿼리 및 뮤테이션.
135
137
  - **SSE**: Server-Sent Events를 통한 구독(기본값).
136
- - **WebSockets**: 실시간 구독을 위한 선택적 `graphql-ws` 지원.
138
+ - **WebSockets**: 활성 adapter가 upgrade listener를 지원하는 Node HTTP/S 서버를 노출할 때(예: Node HTTP adapter) 사용할 수 있는 선택적 `graphql-ws` 실시간 구독 지원.
137
139
 
138
140
  ```typescript
139
141
  GraphqlModule.forRoot({
@@ -150,10 +152,13 @@ GraphqlModule.forRoot({
150
152
  })
151
153
  ```
152
154
 
155
+ `@Subscription({ topics })`는 지원하지 않습니다. Subscription resolver는 `AsyncIterable`을 반환해야 합니다.
156
+
153
157
  ## 운영 가드레일
154
158
 
155
159
  - `graphiql`을 명시적으로 켜거나 `introspection: true`를 설정하지 않으면 스키마 introspection은 기본적으로 비활성화됩니다.
156
160
  - 문서 depth, field complexity, aggregate query cost에 대한 request validation budget이 기본적으로 보수적인 값으로 활성화됩니다.
161
+ - Streaming GraphQL 응답은 downstream response stream이 닫히거나 오류를 내면 upstream fetch body를 cancel하므로 SSE subscription 리소스를 즉시 해제합니다.
157
162
  - WebSocket 구독 경로에는 별도의 전송 budget이 기본 적용됩니다: 동시 연결 `100`, 최대 payload 크기 `64 KiB`, 연결당 활성 operation `25`개입니다.
158
163
  - 무제한 WebSocket 동작이 정말 필요할 때만 `subscriptions.websocket.limits = false`를 사용하고, 그 경우에도 동일한 수준의 외부 제어 수단을 마련해야 합니다.
159
164
  - 무제한 동작이 꼭 필요할 때만 `limits: false`를 사용하고, 그 경우에는 외부 제어 수단을 함께 두어야 합니다.
@@ -186,8 +191,11 @@ GraphqlModule.forRoot({
186
191
  - `GraphqlModule.forRoot(options)`: GraphQL 통합을 위한 메인 엔트리 포인트.
187
192
  - `Resolver`, `Query`, `Mutation`, `Subscription`: 작업 데코레이터.
188
193
  - `Arg`: Input DTO 필드를 GraphQL 인자로 매핑하는 데코레이터.
189
- - `createDataLoader`, `createDataLoaderMap`: DataLoader 팩토리 헬퍼.
190
- - `GraphQLContext`: GraphQL 실행 컨텍스트를 위한 타입 정의.
194
+ - `createDataLoader`, `createDataLoaderMap`, `getRequestScopedDataLoader`, `createRequestScopedDataLoaderFactory`, `DataLoader`: DataLoader factory helper와 type.
195
+ - `listOf`, `isGraphqlListTypeRef`: list output type reference helper.
196
+ - `GraphQLContext` 및 export되는 option/metadata type: GraphQL 실행과 module 설정을 위한 타입 정의.
197
+
198
+ 지원되는 module option에는 `schema`, `context`, `plugins`, `graphiql`, `introspection`, `limits`, `subscriptions.websocket.enabled`, `subscriptions.websocket.limits`, `connectionInitWaitTimeoutMs`, `keepAliveMs`가 포함됩니다.
191
199
 
192
200
  ## 관련 패키지
193
201
 
package/README.md CHANGED
@@ -33,6 +33,8 @@ pnpm add @fluojs/graphql graphql graphql-yoga
33
33
 
34
34
  Register `GraphqlModule.forRoot(...)` and define a resolver using standard decorators. `@fluojs/graphql` currently exposes a synchronous module entrypoint only; there is no `GraphqlModule.forRootAsync(...)` contract.
35
35
 
36
+ You can also pass an executable `GraphQLSchema` via `schema` when you want schema-first integration instead of code-first resolver discovery.
37
+
36
38
  ```typescript
37
39
  import { Module } from '@fluojs/core';
38
40
  import { bootstrapNodeApplication } from '@fluojs/runtime/node';
@@ -133,7 +135,7 @@ class RequestResolver {
133
135
  ### Protocol Support
134
136
  - **HTTP**: Standard GET/POST queries and mutations.
135
137
  - **SSE**: Subscriptions over Server-Sent Events (default).
136
- - **WebSockets**: Optional `graphql-ws` support for real-time subscriptions.
138
+ - **WebSockets**: Optional `graphql-ws` support for real-time subscriptions when the active adapter exposes a Node HTTP/S server with upgrade listeners (for example, the Node HTTP adapter).
137
139
 
138
140
  ```typescript
139
141
  GraphqlModule.forRoot({
@@ -150,10 +152,13 @@ GraphqlModule.forRoot({
150
152
  })
151
153
  ```
152
154
 
155
+ `@Subscription({ topics })` is not supported. Subscription resolvers must return an `AsyncIterable`.
156
+
153
157
  ## Operational Guardrails
154
158
 
155
159
  - Schema introspection is disabled by default unless you explicitly enable `graphiql` or set `introspection: true`.
156
160
  - Request validation budgets are enabled by default with conservative limits for document depth, field complexity, and aggregate query cost.
161
+ - Streaming GraphQL responses cancel the upstream fetch body when the downstream response stream closes or errors, so SSE subscription resources are released promptly.
157
162
  - WebSocket subscriptions use separate transport budgets by default: `100` concurrent connections, `64 KiB` maximum payload size, and `25` active operations per connection.
158
163
  - Set `subscriptions.websocket.limits = false` only when you intentionally need unbounded websocket behavior and can enforce equivalent controls elsewhere.
159
164
  - Pass `limits: false` only when you intentionally need unbounded behavior and can compensate with external controls.
@@ -186,8 +191,11 @@ GraphqlModule.forRoot({
186
191
  - `GraphqlModule.forRoot(options)`: Main entry point for GraphQL integration.
187
192
  - `Resolver`, `Query`, `Mutation`, `Subscription`: Operation decorators.
188
193
  - `Arg`: Input DTO field-to-GraphQL-argument mapping decorator.
189
- - `createDataLoader`, `createDataLoaderMap`: DataLoader factory helpers.
190
- - `GraphQLContext`: Type definition for the GraphQL execution context.
194
+ - `createDataLoader`, `createDataLoaderMap`, `getRequestScopedDataLoader`, `createRequestScopedDataLoaderFactory`, `DataLoader`: DataLoader factory helpers and types.
195
+ - `listOf`, `isGraphqlListTypeRef`: Helpers for list output type references.
196
+ - `GraphQLContext` and exported option/metadata types: Type definitions for GraphQL execution and module configuration.
197
+
198
+ Supported module options include `schema`, `context`, `plugins`, `graphiql`, `introspection`, `limits`, `subscriptions.websocket.enabled`, `subscriptions.websocket.limits`, `connectionInitWaitTimeoutMs`, and `keepAliveMs`.
191
199
 
192
200
  ## Related Packages
193
201
 
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAoB,cAAc,EAAE,qBAAqB,EAA6C,MAAM,YAAY,CAAC;AAGrI,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,wBAAwB,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;AAE1H;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,qBAAqB,CAAC;CACpC;AAED,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;AACrD,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AAwGnD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAQ9D;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAErG;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAmBxD"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAoB,cAAc,EAAE,qBAAqB,EAA6C,MAAM,YAAY,CAAC;AAGrI,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,wBAAwB,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;AAE1H;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,qBAAqB,CAAC;CACpC;AAED,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;AACrD,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AAyGnD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAQ9D;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,mBAAmB,CAErG;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAmBxD"}
@@ -1,12 +1,12 @@
1
- import { metadataSymbol } from '@fluojs/core/internal';
1
+ import { ensureMetadataSymbol } from '@fluojs/core/internal';
2
2
  import { argMetadataSymbol, handlerMetadataSymbol, resolverMetadataSymbol } from './metadata.js';
3
3
 
4
4
  /**
5
5
  * Describes the resolver method options contract.
6
6
  */
7
7
 
8
+ ensureMetadataSymbol();
8
9
  function getStandardMetadataBag(metadata) {
9
- void metadataSymbol;
10
10
  return metadata;
11
11
  }
12
12
  function normalizeResolverTypeName(typeName, fallbackName) {
package/dist/service.d.ts CHANGED
@@ -27,6 +27,7 @@ export declare class GraphqlLifecycleService implements OnApplicationBootstrap,
27
27
  private websocketUpgradeListener;
28
28
  private websocketUpgradeServer;
29
29
  private executeGraphqlOperation;
30
+ private releaseGraphqlInstanceOfPatch;
30
31
  private subscribeGraphqlOperation;
31
32
  private yoga;
32
33
  private readonly middleware;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgD,KAAK,sBAAsB,EAAsD,MAAM,cAAc,CAAC;AAE7J,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AA2BzB,OAAO,KAAK,EAEV,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAkIpB;;GAEG;AACH,qBACa,yBAAyB;IAEpC,SAAS,IAAI,SAAS;IAKtB,UAAU,IAAI,SAAS;CAGxB;AAoHD;;GAEG;AACH,qBACa,uBAAwB,YAAW,sBAAsB,EAAE,qBAAqB;IA4DzF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IA/DxB,OAAO,CAAC,uBAAuB,CAAsC;IACrE,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqC;IACzE,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAA6C;IAC1F,OAAO,CAAC,mBAAmB,CAAkD;IAC7E,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,wBAAwB,CAAkC;IAClE,OAAO,CAAC,sBAAsB,CAAgC;IAC9D,OAAO,CAAC,uBAAuB,CAAyD;IACxF,OAAO,CAAC,yBAAyB,CAAyD;IAC1F,OAAO,CAAC,IAAI,CAAuB;IAEnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CA4CzB;gBAGiB,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,sBAAsB,EACjC,OAAO,EAAE,oBAAoB;IAGxC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5C,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,0BAA0B;YAuEpB,4BAA4B;IAuC1C,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,oBAAoB;YAkBd,wBAAwB;IAwDtC,OAAO,CAAC,kCAAkC;IAwB1C,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,sCAAsC;YAchC,kCAAkC;YAqBlC,sCAAsC;IAkBpD,OAAO,CAAC,6BAA6B;YAYvB,yBAAyB;CAexC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgD,KAAK,sBAAsB,EAAsD,MAAM,cAAc,CAAC;AAE7J,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AA2BzB,OAAO,KAAK,EAEV,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAmIpB;;GAEG;AACH,qBACa,yBAAyB;IAEpC,SAAS,IAAI,SAAS;IAKtB,UAAU,IAAI,SAAS;CAGxB;AA+ID;;GAEG;AACH,qBACa,uBAAwB,YAAW,sBAAsB,EAAE,qBAAqB;IA6DzF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhExB,OAAO,CAAC,uBAAuB,CAAsC;IACrE,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqC;IACzE,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAA6C;IAC1F,OAAO,CAAC,mBAAmB,CAAkD;IAC7E,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,wBAAwB,CAAkC;IAClE,OAAO,CAAC,sBAAsB,CAAgC;IAC9D,OAAO,CAAC,uBAAuB,CAAyD;IACxF,OAAO,CAAC,6BAA6B,CAA2B;IAChE,OAAO,CAAC,yBAAyB,CAAyD;IAC1F,OAAO,CAAC,IAAI,CAAuB;IAEnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CA4CzB;gBAGiB,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,sBAAsB,EACjC,OAAO,EAAE,oBAAoB;IAGxC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5C,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,0BAA0B;YAuEpB,4BAA4B;IAuC1C,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,oBAAoB;YAkBd,wBAAwB;IAwDtC,OAAO,CAAC,kCAAkC;IAwB1C,OAAO,CAAC,sBAAsB;IAoB9B,OAAO,CAAC,sCAAsC;YAchC,kCAAkC;YAqBlC,sCAAsC;IAkBpD,OAAO,CAAC,6BAA6B;YAYvB,yBAAyB;CAexC"}
package/dist/service.js CHANGED
@@ -78,7 +78,8 @@ function closeWebSocketServer(server) {
78
78
  }
79
79
  const graphqlRequestContextStorage = new AsyncLocalStorage();
80
80
  const runtimeRequire = createRequire(import.meta.url);
81
- let graphqlInstanceOfPatched = false;
81
+ let graphqlInstanceOfPatchRefCount = 0;
82
+ let restoreGraphqlInstanceOfPatch;
82
83
  const allowedCrossRealmGraphqlObjects = new WeakSet();
83
84
 
84
85
  /**
@@ -149,15 +150,16 @@ function isAllowedCrossRealmGraphqlObject(value, constructor) {
149
150
  }
150
151
  return allowedCrossRealmGraphqlObjects.has(value) && getCrossRealmGraphqlTag(value, constructor) !== undefined;
151
152
  }
152
- function patchGraphqlInstanceOf() {
153
- if (graphqlInstanceOfPatched) {
154
- return;
155
- }
153
+ function installGraphqlInstanceOfPatch() {
156
154
  const instanceOfModule = runtimeRequire('graphql/jsutils/instanceOf.js');
157
- const originalInstanceOf = instanceOfModule.instanceOf;
158
- instanceOfModule.instanceOf = (value, constructor) => {
155
+ if (restoreGraphqlInstanceOfPatch) {
156
+ graphqlInstanceOfPatchRefCount += 1;
157
+ return releaseGraphqlInstanceOfPatch;
158
+ }
159
+ const patchedFrom = instanceOfModule.instanceOf;
160
+ const patchedInstanceOf = (value, constructor) => {
159
161
  try {
160
- if (originalInstanceOf(value, constructor)) {
162
+ if (patchedFrom(value, constructor)) {
161
163
  return true;
162
164
  }
163
165
  } catch (error) {
@@ -168,7 +170,26 @@ function patchGraphqlInstanceOf() {
168
170
  }
169
171
  return isAllowedCrossRealmGraphqlObject(value, constructor);
170
172
  };
171
- graphqlInstanceOfPatched = true;
173
+ instanceOfModule.instanceOf = patchedInstanceOf;
174
+ graphqlInstanceOfPatchRefCount = 1;
175
+ restoreGraphqlInstanceOfPatch = () => {
176
+ if (instanceOfModule.instanceOf !== patchedInstanceOf) {
177
+ return;
178
+ }
179
+ instanceOfModule.instanceOf = patchedFrom;
180
+ };
181
+ return releaseGraphqlInstanceOfPatch;
182
+ }
183
+ function releaseGraphqlInstanceOfPatch() {
184
+ if (graphqlInstanceOfPatchRefCount === 0) {
185
+ return;
186
+ }
187
+ graphqlInstanceOfPatchRefCount -= 1;
188
+ if (graphqlInstanceOfPatchRefCount > 0) {
189
+ return;
190
+ }
191
+ restoreGraphqlInstanceOfPatch?.();
192
+ restoreGraphqlInstanceOfPatch = undefined;
172
193
  }
173
194
  async function loadGraphqlDeps() {
174
195
  const graphqlMod = runtimeRequire('graphql');
@@ -208,6 +229,7 @@ class GraphqlLifecycleService {
208
229
  websocketUpgradeListener;
209
230
  websocketUpgradeServer;
210
231
  executeGraphqlOperation;
232
+ releaseGraphqlInstanceOfPatch;
211
233
  subscribeGraphqlOperation;
212
234
  yoga;
213
235
  middleware = {
@@ -291,6 +313,8 @@ class GraphqlLifecycleService {
291
313
  this.middlewareRegistered = false;
292
314
  this.executeGraphqlOperation = undefined;
293
315
  this.graphQLErrorConstructor = undefined;
316
+ this.releaseGraphqlInstanceOfPatch?.();
317
+ this.releaseGraphqlInstanceOfPatch = undefined;
294
318
  this.subscribeGraphqlOperation = undefined;
295
319
  this.yoga = undefined;
296
320
  }
@@ -312,7 +336,7 @@ class GraphqlLifecycleService {
312
336
  };
313
337
  }
314
338
  resolveSchema(deps) {
315
- patchGraphqlInstanceOf();
339
+ this.releaseGraphqlInstanceOfPatch ??= installGraphqlInstanceOfPatch();
316
340
  return resolveSchema(deps, this.options.schema, () => this.createCodeFirstSchema(deps), markAllowedCrossRealmGraphqlObjects);
317
341
  }
318
342
  createCodeFirstSchema(deps) {
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAA2B,MAAM,cAAc,CAAC;AAEjG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAsED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAUjE;AAsBD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDrH"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAA2B,MAAM,cAAc,CAAC;AAEjG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAsED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAUjE;AAgGD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BrH"}
@@ -89,6 +89,71 @@ function readSetCookieValues(headers) {
89
89
  }
90
90
  return values;
91
91
  }
92
+ async function writeFetchResponseStream(body, stream) {
93
+ const reader = body.getReader();
94
+ let readerDone = false;
95
+ let cancelPromise;
96
+ let notifyCancellation;
97
+ const cancellationStarted = new Promise(resolve => {
98
+ notifyCancellation = resolve;
99
+ });
100
+ const removeCloseListener = stream.onClose?.(() => {
101
+ if (!readerDone) {
102
+ void cancelUnreadBody().catch(() => {});
103
+ }
104
+ });
105
+ const cancelUnreadBody = () => {
106
+ if (!cancelPromise) {
107
+ readerDone = true;
108
+ notifyCancellation?.();
109
+ cancelPromise = reader.cancel();
110
+ }
111
+ return cancelPromise;
112
+ };
113
+ const readNextChunk = async () => {
114
+ const readPromise = reader.read();
115
+ readPromise.catch(() => {});
116
+ return await Promise.race([readPromise, cancellationStarted.then(async () => {
117
+ await cancelPromise;
118
+ return {
119
+ done: true,
120
+ value: undefined
121
+ };
122
+ })]);
123
+ };
124
+ try {
125
+ while (true) {
126
+ if (stream.closed) {
127
+ await cancelUnreadBody();
128
+ break;
129
+ }
130
+ const {
131
+ done,
132
+ value
133
+ } = await readNextChunk();
134
+ if (done) {
135
+ readerDone = true;
136
+ break;
137
+ }
138
+ if (stream.closed) {
139
+ await cancelUnreadBody();
140
+ break;
141
+ }
142
+ const canContinue = stream.write(value);
143
+ if (!canContinue && !stream.closed) {
144
+ await stream.waitForDrain?.();
145
+ }
146
+ }
147
+ } catch (error) {
148
+ await cancelUnreadBody();
149
+ throw error;
150
+ } finally {
151
+ removeCloseListener?.();
152
+ }
153
+ if (!stream.closed) {
154
+ stream.close();
155
+ }
156
+ }
92
157
 
93
158
  /**
94
159
  * Write fetch response.
@@ -113,26 +178,7 @@ export async function writeFetchResponse(fetchResponse, frameworkResponse) {
113
178
  if (fetchResponse.body && stream) {
114
179
  frameworkResponse.committed = true;
115
180
  stream.flush?.();
116
- const reader = fetchResponse.body.getReader();
117
- while (true) {
118
- const {
119
- done,
120
- value
121
- } = await reader.read();
122
- if (done) {
123
- break;
124
- }
125
- if (stream.closed) {
126
- break;
127
- }
128
- const canContinue = stream.write(value);
129
- if (!canContinue && !stream.closed) {
130
- await stream.waitForDrain?.();
131
- }
132
- }
133
- if (!stream.closed) {
134
- stream.close();
135
- }
181
+ await writeFetchResponseStream(fetchResponse.body, stream);
136
182
  return;
137
183
  }
138
184
  const buffer = await fetchResponse.arrayBuffer();
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "yoga",
10
10
  "api"
11
11
  ],
12
- "version": "1.0.0-beta.4",
12
+ "version": "1.0.0-beta.6",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -41,11 +41,11 @@
41
41
  "graphql-ws": "^5.16.2",
42
42
  "graphql-yoga": "^5.18.1",
43
43
  "ws": "^8.18.3",
44
- "@fluojs/core": "^1.0.0-beta.2",
45
- "@fluojs/di": "^1.0.0-beta.5",
46
- "@fluojs/http": "^1.0.0-beta.4",
47
- "@fluojs/runtime": "^1.0.0-beta.5",
48
- "@fluojs/validation": "^1.0.0-beta.1"
44
+ "@fluojs/core": "^1.0.0-beta.4",
45
+ "@fluojs/di": "^1.0.0-beta.6",
46
+ "@fluojs/http": "^1.0.0-beta.10",
47
+ "@fluojs/runtime": "^1.0.0-beta.11",
48
+ "@fluojs/validation": "^1.0.0-beta.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/ws": "^8.18.1",