@fluojs/graphql 1.0.3 → 1.1.0
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 +95 -7
- package/README.md +95 -7
- package/dist/decorators.d.ts +39 -0
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +102 -1
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/metadata.d.ts +21 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +68 -7
- package/dist/node/graphql-websocket-transport.d.ts +36 -0
- package/dist/node/graphql-websocket-transport.d.ts.map +1 -0
- package/dist/node/graphql-websocket-transport.js +156 -0
- package/dist/schema/object-field-resolvers.d.ts +17 -0
- package/dist/schema/object-field-resolvers.d.ts.map +1 -0
- package/dist/schema/object-field-resolvers.js +78 -0
- package/dist/schema/schema.d.ts +5 -1
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +75 -39
- package/dist/service.d.ts +3 -7
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +56 -156
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/transport/transport.js +8 -3
- package/dist/types.d.ts +16 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -1
- package/package.json +7 -7
package/README.ko.md
CHANGED
|
@@ -22,6 +22,8 @@ fluo를 위한 데코레이터 기반 GraphQL 통합 패키지입니다. **Graph
|
|
|
22
22
|
pnpm add @fluojs/graphql graphql graphql-yoga
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
`@fluojs/graphql`은 Node.js `>=20.16.0`을 지원하며, 이 유효 하한을 `engines.node`로 선언합니다. 필수 dependency graph는 `@fluojs/runtime`을 통해 `@fluojs/config`에 도달하며, `@fluojs/config`도 Node.js `>=20.16.0`을 요구합니다. 다른 필수 first-party dependency가 선언한 더 낮은 하한은 이 범위와 호환됩니다. HTTP query/mutation과 기본 SSE subscription 경로는 내부적으로 Web-standard request/response primitive를 사용하지만, 이 구현 세부 사항이 Bun, Deno, Cloudflare Workers에 대한 package 지원을 의미하지는 않습니다. 전체 dependency metadata와 native runtime suite가 완전한 GraphQL 계약을 입증하기 전까지 해당 runtime은 지원하지 않습니다. 선택적 WebSocket subscription에는 server-backed Node HTTP/S upgrade 표면을 노출하는 adapter도 필요합니다.
|
|
26
|
+
|
|
25
27
|
## 사용 시점
|
|
26
28
|
|
|
27
29
|
- TypeScript 데코레이터를 사용하여 타입 안전한 GraphQL API를 구축할 때 (**Code-first**).
|
|
@@ -73,15 +75,97 @@ await app.listen(3000);
|
|
|
73
75
|
## 핵심 기능
|
|
74
76
|
|
|
75
77
|
### Code-first Resolvers
|
|
76
|
-
fluo는 표준 데코레이터를 사용하여 GraphQL 스키마를 정의합니다. `@Resolver`, `@Query`, `@Mutation`, `@Subscription`을 사용하여 클래스 메서드를 GraphQL 작업에 매핑합니다. GraphQL 인자는 input DTO 필드에 `@Arg(...)`로 선언하고, resolver 메서드는 작업의 `input` 옵션을 통해 해당 DTO를 받습니다.
|
|
78
|
+
fluo는 표준 데코레이터를 사용하여 GraphQL 스키마를 정의합니다. `@Resolver`, `@Query`, `@Mutation`, `@Subscription`을 사용하여 클래스 메서드를 GraphQL 작업에 매핑합니다. GraphQL 인자는 input DTO 필드에 `@Arg(...)`로 선언하고, resolver 메서드는 작업의 `input` 옵션을 통해 해당 DTO를 받습니다. Object field resolver는 `@Resolver('TypeName')`, `@FieldResolver(...)`, 명시적 `@Parent()` / `@Context()` method binding을 사용합니다.
|
|
79
|
+
|
|
80
|
+
Resolver 반환 타입은 TypeScript metadata에서 추론되지 않습니다. `outputType`이 없는 operation은 GraphQL `String`을 사용합니다. Object 결과에는 GraphQL output type을 전달해야 하고, array 결과에는 item type을 `listOf(...)`로 감싸야 합니다.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
84
|
+
import { listOf, Query, Resolver } from '@fluojs/graphql';
|
|
85
|
+
|
|
86
|
+
const UserType = new GraphQLObjectType({
|
|
87
|
+
name: 'User',
|
|
88
|
+
fields: {
|
|
89
|
+
id: { type: GraphQLString },
|
|
90
|
+
name: { type: GraphQLString },
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
@Resolver()
|
|
95
|
+
class UserResolver {
|
|
96
|
+
@Query({ outputType: UserType })
|
|
97
|
+
async user() {
|
|
98
|
+
return userService.findCurrent();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Query({ outputType: listOf(UserType) })
|
|
102
|
+
async users() {
|
|
103
|
+
return userService.findAll();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Object Field Resolver
|
|
109
|
+
|
|
110
|
+
`@FieldResolver(...)`는 provider method를 `@Resolver('TypeName')`이 소유하는 named object type의 field에 연결합니다. 대상 object type은 code-first root operation output에서 도달 가능해야 합니다. 해당 field가 `GraphQLObjectType`에 이미 존재하거나, schema builder가 field를 추가할 수 있도록 field resolver가 `type`을 선언해야 합니다.
|
|
111
|
+
|
|
112
|
+
TC39 표준 데코레이터는 parameter decorator를 지원하지 않습니다. fluo의 standard-decorator 계약을 지키기 위해 `@Parent()`와 `@Context()`는 zero-based parameter index를 바인딩하는 method decorator입니다. 기본값은 parent/source object를 parameter `0`에, `GraphQLContext`를 parameter `1`에 매핑합니다. Method 순서가 다르면 index를 명시적으로 전달하세요.
|
|
77
113
|
|
|
78
|
-
|
|
114
|
+
```typescript
|
|
115
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
116
|
+
import { Context, FieldResolver, Parent, Query, Resolver, type GraphQLContext } from '@fluojs/graphql';
|
|
117
|
+
|
|
118
|
+
const AuthorType = new GraphQLObjectType({
|
|
119
|
+
name: 'Author',
|
|
120
|
+
fields: {
|
|
121
|
+
id: { type: GraphQLString },
|
|
122
|
+
name: { type: GraphQLString },
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const BookType = new GraphQLObjectType({
|
|
127
|
+
name: 'Book',
|
|
128
|
+
fields: {
|
|
129
|
+
id: { type: GraphQLString },
|
|
130
|
+
title: { type: GraphQLString },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
@Resolver()
|
|
135
|
+
class BookQueryResolver {
|
|
136
|
+
@Query({ outputType: BookType })
|
|
137
|
+
book() {
|
|
138
|
+
return { id: 'book-1', title: 'Standard GraphQL', authorId: 'author-1' };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Resolver('Book')
|
|
143
|
+
class BookFieldResolver {
|
|
144
|
+
@FieldResolver({ fieldName: 'author', type: AuthorType })
|
|
145
|
+
@Parent()
|
|
146
|
+
@Context()
|
|
147
|
+
author(book: { authorId: string }, context: GraphQLContext) {
|
|
148
|
+
return authorLoader(context).load(book.authorId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
두 resolver class를 module provider 또는 controller로 등록하고, `GraphqlModule.forRoot({ resolvers })`를 allowlist로 사용할 때는 둘 다 포함하세요. 중복 `TypeName.fieldName` 등록, code-first root output에서 도달할 수 없는 field target, root operation method에 배치한 `@Parent()` / `@Context()` binding은 bootstrap 중 실패합니다. Field argument DTO binding과 schema-first field-resolver attachment는 첫 runtime 계약 범위 밖입니다. `nullable` option은 예약되어 있습니다. 기존 field nullability는 유지되며, `type`으로 추가한 field는 GraphQL의 nullable 기본값을 사용합니다.
|
|
79
154
|
|
|
80
155
|
### Request-Scoped DataLoaders
|
|
81
156
|
내장된 DataLoader 통합을 통해 N+1 문제를 효율적으로 해결합니다. Loader는 각 GraphQL 작업마다 자동으로 격리됩니다.
|
|
82
157
|
|
|
83
158
|
```typescript
|
|
84
|
-
import {
|
|
159
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
160
|
+
import { createDataLoader, type GraphQLContext, Query, Resolver } from '@fluojs/graphql';
|
|
161
|
+
|
|
162
|
+
const UserType = new GraphQLObjectType({
|
|
163
|
+
name: 'User',
|
|
164
|
+
fields: {
|
|
165
|
+
id: { type: GraphQLString },
|
|
166
|
+
name: { type: GraphQLString },
|
|
167
|
+
},
|
|
168
|
+
});
|
|
85
169
|
|
|
86
170
|
const userLoader = createDataLoader(async (ids: string[]) => {
|
|
87
171
|
const users = await userService.findByIds(ids);
|
|
@@ -95,7 +179,7 @@ class UserInput {
|
|
|
95
179
|
|
|
96
180
|
@Resolver()
|
|
97
181
|
class UserResolver {
|
|
98
|
-
@Query({ input: UserInput })
|
|
182
|
+
@Query({ input: UserInput, outputType: UserType })
|
|
99
183
|
async user(input: UserInput, context: GraphQLContext) {
|
|
100
184
|
return userLoader(context).load(input.id);
|
|
101
185
|
}
|
|
@@ -108,6 +192,7 @@ class UserResolver {
|
|
|
108
192
|
- Request-scoped provider를 주입하는 resolver는 resolver 자체에도 `@Scope('request')`를 지정해야 합니다. 이렇게 해야 DI lifetime 규칙이 명시적으로 유지되고 singleton-to-request dependency mismatch를 피할 수 있습니다.
|
|
109
193
|
- `@fluojs/graphql`은 HTTP GraphQL 요청 또는 WebSocket subscription operation마다 operation-scoped DI 컨테이너를 하나 만들고, 해당 operation 안의 resolver 호출들이 이를 공유하며, operation 완료 또는 WebSocket operation 종료 시 dispose합니다.
|
|
110
194
|
- Resolver 메서드는 `GraphQLContext`를 받으며, 내장 필드에는 fluo `request`, middleware 또는 guard가 설정한 인증된 HTTP `principal`, WebSocket subscription의 `connectionParams`와 `socket`, 그리고 `GraphqlModule.forRoot({ context })`가 반환한 사용자 정의 필드가 포함됩니다.
|
|
195
|
+
- Object field resolver는 root resolver와 같은 provider scope 및 operation container를 사용합니다. `@Parent()`와 `@Context()`는 positional method argument만 제어합니다.
|
|
111
196
|
- Request-scoped DataLoader helper는 같은 `GraphQLContext` operation 경계를 사용하므로 loader cache는 하나의 GraphQL operation 안에서만 공유됩니다.
|
|
112
197
|
- 애플리케이션 shutdown은 WebSocket transport를 등록 해제하고, 살아 있는 WebSocket client를 닫으며, 아직 활성 상태인 WebSocket operation container를 정상 operation 완료 때와 같은 request-scoped provider teardown 경로로 dispose합니다.
|
|
113
198
|
|
|
@@ -139,7 +224,7 @@ class RequestResolver {
|
|
|
139
224
|
- **SSE**: Server-Sent Events를 통한 구독(기본값).
|
|
140
225
|
- **WebSockets**: 활성 adapter가 upgrade listener를 지원하는 Node HTTP/S 서버를 노출할 때(예: Node HTTP adapter) 사용할 수 있는 선택적 `graphql-ws` 실시간 구독 지원.
|
|
141
226
|
|
|
142
|
-
HTTP query/mutation과 기본 SSE subscription 경로는 fluo의
|
|
227
|
+
지원되는 Node.js `>=20.16.0` runtime에서 HTTP query/mutation과 기본 SSE subscription 경로는 fluo의 Web-standard HTTP 추상화를 통해 실행됩니다. 이 내부 transport seam은 Bun, Deno, Cloudflare Workers 지원 보장이 아닙니다. 선택적 WebSocket transport는 server-backed Node HTTP/S adapter 표면도 필요하므로 지원 범위가 더 좁습니다.
|
|
143
228
|
|
|
144
229
|
```typescript
|
|
145
230
|
GraphqlModule.forRoot({
|
|
@@ -197,7 +282,8 @@ GraphqlModule.forRoot({
|
|
|
197
282
|
## 공개 API
|
|
198
283
|
|
|
199
284
|
- `GraphqlModule.forRoot(options)`: GraphQL 통합을 위한 메인 엔트리 포인트.
|
|
200
|
-
- `Resolver`, `Query`, `Mutation`, `Subscription`:
|
|
285
|
+
- `Resolver`, `Query`, `Mutation`, `Subscription`: Resolver 및 root operation 데코레이터.
|
|
286
|
+
- `FieldResolver`, `Parent`, `Context`: Code-first object field resolution과 명시적 parent/context parameter-index binding.
|
|
201
287
|
- `Arg`: Input DTO 필드를 GraphQL 인자로 매핑하는 데코레이터.
|
|
202
288
|
- `createDataLoader`, `createDataLoaderMap`, `getRequestScopedDataLoader`, `createRequestScopedDataLoaderFactory`, `DataLoader`: DataLoader factory helper와 type.
|
|
203
289
|
- `listOf`, `isGraphqlListTypeRef`: list output type reference helper.
|
|
@@ -214,4 +300,6 @@ GraphqlModule.forRoot({
|
|
|
214
300
|
## 예제 소스
|
|
215
301
|
|
|
216
302
|
- `packages/graphql/src/module.test.ts`: 모듈 등록, resolver 실행, request-scoped container, subscription, guardrail 기본값을 다루는 통합 테스트 및 사용 예제.
|
|
217
|
-
- `packages/graphql/field-resolver
|
|
303
|
+
- `packages/graphql/src/field-resolver.test.ts`: Object field resolver의 discovery, schema attachment, parent/context binding, invalid placement를 실행 가능한 형태로 검증하는 테스트.
|
|
304
|
+
- `packages/graphql/src/runtime-support.test.ts`: Package의 Node.js engine 하한이 필수 first-party dependency graph에서 가장 높은 하한 이상인지 검증하는 회귀 테스트.
|
|
305
|
+
- `packages/graphql/field-resolver-rfc.md`: Object field resolver의 구현된 계약과 후속 범위.
|
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ Decorator-based GraphQL integration for fluo. Built on **GraphQL Yoga**, it prov
|
|
|
22
22
|
pnpm add @fluojs/graphql graphql graphql-yoga
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
`@fluojs/graphql` supports Node.js `>=20.16.0` and declares that effective floor through `engines.node`. Its mandatory dependency graph reaches `@fluojs/config` through `@fluojs/runtime`; `@fluojs/config` also requires Node.js `>=20.16.0`, while the lower floors declared by other mandatory first-party dependencies remain compatible. HTTP queries/mutations and the default SSE subscription path use Web-standard request/response primitives internally, but that implementation detail does not establish package support for Bun, Deno, or Cloudflare Workers. Those runtimes remain unsupported until the complete dependency metadata and native runtime suites prove the full GraphQL contract. Optional WebSocket subscriptions additionally require an adapter that exposes a server-backed Node HTTP/S upgrade surface.
|
|
26
|
+
|
|
25
27
|
## When to Use
|
|
26
28
|
|
|
27
29
|
- When building type-safe GraphQL APIs using TypeScript decorators (**Code-first**).
|
|
@@ -73,15 +75,97 @@ await app.listen(3000);
|
|
|
73
75
|
## Core Capabilities
|
|
74
76
|
|
|
75
77
|
### Code-first Resolvers
|
|
76
|
-
fluo uses standard decorators to define your GraphQL schema. Use `@Resolver`, `@Query`, `@Mutation`, and `@Subscription` to map class methods to GraphQL operations. GraphQL arguments are declared on input DTO fields with `@Arg(...)`, then passed to the resolver method through the operation `input` option.
|
|
78
|
+
fluo uses standard decorators to define your GraphQL schema. Use `@Resolver`, `@Query`, `@Mutation`, and `@Subscription` to map class methods to GraphQL operations. GraphQL arguments are declared on input DTO fields with `@Arg(...)`, then passed to the resolver method through the operation `input` option. Object field resolvers use `@Resolver('TypeName')` plus `@FieldResolver(...)` and explicit `@Parent()` / `@Context()` method bindings.
|
|
79
|
+
|
|
80
|
+
Resolver return types are not inferred from TypeScript metadata. An operation without `outputType` uses GraphQL `String`; object results must provide a GraphQL output type, and array results must wrap their item type with `listOf(...)`.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
84
|
+
import { listOf, Query, Resolver } from '@fluojs/graphql';
|
|
85
|
+
|
|
86
|
+
const UserType = new GraphQLObjectType({
|
|
87
|
+
name: 'User',
|
|
88
|
+
fields: {
|
|
89
|
+
id: { type: GraphQLString },
|
|
90
|
+
name: { type: GraphQLString },
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
@Resolver()
|
|
95
|
+
class UserResolver {
|
|
96
|
+
@Query({ outputType: UserType })
|
|
97
|
+
async user() {
|
|
98
|
+
return userService.findCurrent();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Query({ outputType: listOf(UserType) })
|
|
102
|
+
async users() {
|
|
103
|
+
return userService.findAll();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Object Field Resolvers
|
|
109
|
+
|
|
110
|
+
`@FieldResolver(...)` attaches a provider method to a field on the named object type owned by `@Resolver('TypeName')`. The target object type must be reachable from a code-first root operation output. The field must already exist on that `GraphQLObjectType`, or the field resolver must declare `type` so the schema builder can add it.
|
|
111
|
+
|
|
112
|
+
TC39 standard decorators do not support parameter decorators. To preserve fluo's standard-decorator contract, `@Parent()` and `@Context()` are method decorators that bind zero-based parameter indexes. Their defaults map the parent/source object to parameter `0` and `GraphQLContext` to parameter `1`; pass an explicit index when your method uses a different order.
|
|
77
113
|
|
|
78
|
-
|
|
114
|
+
```typescript
|
|
115
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
116
|
+
import { Context, FieldResolver, Parent, Query, Resolver, type GraphQLContext } from '@fluojs/graphql';
|
|
117
|
+
|
|
118
|
+
const AuthorType = new GraphQLObjectType({
|
|
119
|
+
name: 'Author',
|
|
120
|
+
fields: {
|
|
121
|
+
id: { type: GraphQLString },
|
|
122
|
+
name: { type: GraphQLString },
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const BookType = new GraphQLObjectType({
|
|
127
|
+
name: 'Book',
|
|
128
|
+
fields: {
|
|
129
|
+
id: { type: GraphQLString },
|
|
130
|
+
title: { type: GraphQLString },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
@Resolver()
|
|
135
|
+
class BookQueryResolver {
|
|
136
|
+
@Query({ outputType: BookType })
|
|
137
|
+
book() {
|
|
138
|
+
return { id: 'book-1', title: 'Standard GraphQL', authorId: 'author-1' };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Resolver('Book')
|
|
143
|
+
class BookFieldResolver {
|
|
144
|
+
@FieldResolver({ fieldName: 'author', type: AuthorType })
|
|
145
|
+
@Parent()
|
|
146
|
+
@Context()
|
|
147
|
+
author(book: { authorId: string }, context: GraphQLContext) {
|
|
148
|
+
return authorLoader(context).load(book.authorId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Register both resolver classes as module providers or controllers and include both when `GraphqlModule.forRoot({ resolvers })` is used as an allowlist. Duplicate `TypeName.fieldName` registrations, field targets that are not reachable from a code-first root output, and `@Parent()` / `@Context()` bindings placed on root operation methods fail during bootstrap. Field argument DTO binding and schema-first field-resolver attachment remain outside this first runtime contract. The `nullable` option is reserved; existing field nullability is preserved, while fields added with `type` use GraphQL's nullable default.
|
|
79
154
|
|
|
80
155
|
### Request-Scoped DataLoaders
|
|
81
156
|
Efficiently solve the N+1 problem with built-in DataLoader integration. Loaders are automatically isolated per GraphQL operation.
|
|
82
157
|
|
|
83
158
|
```typescript
|
|
84
|
-
import {
|
|
159
|
+
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
|
160
|
+
import { createDataLoader, type GraphQLContext, Query, Resolver } from '@fluojs/graphql';
|
|
161
|
+
|
|
162
|
+
const UserType = new GraphQLObjectType({
|
|
163
|
+
name: 'User',
|
|
164
|
+
fields: {
|
|
165
|
+
id: { type: GraphQLString },
|
|
166
|
+
name: { type: GraphQLString },
|
|
167
|
+
},
|
|
168
|
+
});
|
|
85
169
|
|
|
86
170
|
const userLoader = createDataLoader(async (ids: string[]) => {
|
|
87
171
|
const users = await userService.findByIds(ids);
|
|
@@ -95,7 +179,7 @@ class UserInput {
|
|
|
95
179
|
|
|
96
180
|
@Resolver()
|
|
97
181
|
class UserResolver {
|
|
98
|
-
@Query({ input: UserInput })
|
|
182
|
+
@Query({ input: UserInput, outputType: UserType })
|
|
99
183
|
async user(input: UserInput, context: GraphQLContext) {
|
|
100
184
|
return userLoader(context).load(input.id);
|
|
101
185
|
}
|
|
@@ -108,6 +192,7 @@ class UserResolver {
|
|
|
108
192
|
- Resolvers that inject request-scoped providers must also be marked with `@Scope('request')`; this keeps DI lifetime rules explicit and avoids singleton-to-request dependency mismatches.
|
|
109
193
|
- `@fluojs/graphql` creates one operation-scoped DI container for each HTTP GraphQL request or websocket subscription operation, shares it across resolver calls in that operation, and disposes it when the operation completes or the websocket operation disconnects.
|
|
110
194
|
- Resolver methods receive a `GraphQLContext` whose built-in fields expose the underlying fluo `request`, the authenticated HTTP `principal` when middleware or guards set one, websocket `connectionParams` and `socket` for websocket subscriptions, and any custom fields returned from `GraphqlModule.forRoot({ context })`.
|
|
195
|
+
- Object field resolvers use the same provider scope and operation container as root resolvers; `@Parent()` and `@Context()` only control positional method arguments.
|
|
111
196
|
- Request-scoped DataLoader helpers use the same `GraphQLContext` operation boundary, so loader caches are shared only within one GraphQL operation.
|
|
112
197
|
- Application shutdown unregisters the websocket transport, closes live websocket clients, and disposes any still-active websocket operation containers through the same request-scoped provider teardown path used when an operation completes normally.
|
|
113
198
|
|
|
@@ -139,7 +224,7 @@ class RequestResolver {
|
|
|
139
224
|
- **SSE**: Subscriptions over Server-Sent Events (default).
|
|
140
225
|
- **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).
|
|
141
226
|
|
|
142
|
-
HTTP queries/mutations and the default SSE subscription path run through fluo's
|
|
227
|
+
On the supported Node.js `>=20.16.0` runtime, HTTP queries/mutations and the default SSE subscription path run through fluo's Web-standard HTTP abstraction. This internal transport seam is not a Bun, Deno, or Cloudflare Workers support guarantee. The optional websocket transport is narrower still because it requires a server-backed Node HTTP/S adapter surface.
|
|
143
228
|
|
|
144
229
|
```typescript
|
|
145
230
|
GraphqlModule.forRoot({
|
|
@@ -197,7 +282,8 @@ GraphqlModule.forRoot({
|
|
|
197
282
|
## Public API
|
|
198
283
|
|
|
199
284
|
- `GraphqlModule.forRoot(options)`: Main entry point for GraphQL integration.
|
|
200
|
-
- `Resolver`, `Query`, `Mutation`, `Subscription`:
|
|
285
|
+
- `Resolver`, `Query`, `Mutation`, `Subscription`: Resolver and root operation decorators.
|
|
286
|
+
- `FieldResolver`, `Parent`, `Context`: Code-first object field resolution and explicit parent/context parameter-index bindings.
|
|
201
287
|
- `Arg`: Input DTO field-to-GraphQL-argument mapping decorator.
|
|
202
288
|
- `createDataLoader`, `createDataLoaderMap`, `getRequestScopedDataLoader`, `createRequestScopedDataLoaderFactory`, `DataLoader`: DataLoader factory helpers and types.
|
|
203
289
|
- `listOf`, `isGraphqlListTypeRef`: Helpers for list output type references.
|
|
@@ -214,4 +300,6 @@ Supported module options include `schema`, `context`, `plugins`, `graphiql`, `in
|
|
|
214
300
|
## Example Sources
|
|
215
301
|
|
|
216
302
|
- `packages/graphql/src/module.test.ts`: Integration tests and usage examples for module registration, resolver execution, request-scoped containers, subscriptions, and guardrail defaults.
|
|
217
|
-
- `packages/graphql/field-resolver
|
|
303
|
+
- `packages/graphql/src/field-resolver.test.ts`: Executable discovery, schema attachment, parent/context binding, and invalid-placement coverage for object field resolvers.
|
|
304
|
+
- `packages/graphql/src/runtime-support.test.ts`: Regression coverage that keeps the package's Node.js engine floor at or above the highest floor in its mandatory first-party dependency graph.
|
|
305
|
+
- `packages/graphql/field-resolver-rfc.md`: Implemented contract and follow-up boundaries for object field resolvers.
|
package/dist/decorators.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export interface ResolverMethodOptions {
|
|
|
11
11
|
argTypes?: Record<string, GraphqlArgType>;
|
|
12
12
|
outputType?: GraphqlRootOutputType;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Describes an object field resolver's field name and optional output type override.
|
|
16
|
+
*/
|
|
17
|
+
export interface FieldResolverOptions {
|
|
18
|
+
fieldName?: string;
|
|
19
|
+
type?: GraphqlRootOutputType;
|
|
20
|
+
nullable?: boolean;
|
|
21
|
+
}
|
|
14
22
|
type ClassDecoratorLike = StandardClassDecoratorFn;
|
|
15
23
|
type MethodDecoratorLike = StandardMethodDecoratorFn;
|
|
16
24
|
type FieldDecoratorLike = StandardFieldDecoratorFn;
|
|
@@ -42,6 +50,37 @@ export declare function Mutation(fieldNameOrOptions?: string | ResolverMethodOpt
|
|
|
42
50
|
* @returns The subscription result.
|
|
43
51
|
*/
|
|
44
52
|
export declare function Subscription(fieldNameOrOptions?: string | ResolverMethodOptions): MethodDecoratorLike;
|
|
53
|
+
/**
|
|
54
|
+
* Marks a public instance method as the resolver for one field on the object type owned by `@Resolver(typeName)`.
|
|
55
|
+
*
|
|
56
|
+
* @param fieldNameOrOptions Field name or object field resolver options.
|
|
57
|
+
* @returns A TC39 standard method decorator.
|
|
58
|
+
*/
|
|
59
|
+
export declare function FieldResolver(fieldNameOrOptions?: string | FieldResolverOptions): MethodDecoratorLike;
|
|
60
|
+
/**
|
|
61
|
+
* Binds a field resolver method parameter to GraphQL's parent/source object.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* TC39 standard decorators do not support parameter-decorator syntax, so this
|
|
65
|
+
* standard method decorator records the parameter index explicitly. The default
|
|
66
|
+
* index is `0`.
|
|
67
|
+
*
|
|
68
|
+
* @param parameterIndex Zero-based method parameter index to receive the parent value.
|
|
69
|
+
* @returns A TC39 standard method decorator.
|
|
70
|
+
*/
|
|
71
|
+
export declare function Parent(parameterIndex?: number): MethodDecoratorLike;
|
|
72
|
+
/**
|
|
73
|
+
* Binds a field resolver method parameter to the active `GraphQLContext`.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* TC39 standard decorators do not support parameter-decorator syntax, so this
|
|
77
|
+
* standard method decorator records the parameter index explicitly. The default
|
|
78
|
+
* index is `1`.
|
|
79
|
+
*
|
|
80
|
+
* @param parameterIndex Zero-based method parameter index to receive the context value.
|
|
81
|
+
* @returns A TC39 standard method decorator.
|
|
82
|
+
*/
|
|
83
|
+
export declare function Context(parameterIndex?: number): MethodDecoratorLike;
|
|
45
84
|
/**
|
|
46
85
|
* Arg.
|
|
47
86
|
*
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAIV,cAAc,EACd,qBAAqB,EAGtB,MAAM,YAAY,CAAC;AAGpB,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;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;AACrD,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AA8KnD;;;;;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,aAAa,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,oBAAoB,GAAG,mBAAmB,CAerG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,cAAc,SAAI,GAAG,mBAAmB,CAE9D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,cAAc,SAAI,GAAG,mBAAmB,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAmBxD"}
|
package/dist/decorators.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { ensureMetadataSymbol } from '@fluojs/core/internal';
|
|
2
|
-
import { argMetadataSymbol, handlerMetadataSymbol, resolverMetadataSymbol } from './metadata.js';
|
|
2
|
+
import { argMetadataSymbol, fieldResolverParameterMetadataSymbol, handlerMetadataSymbol, resolverMetadataSymbol } from './metadata.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Describes the resolver method options contract.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Describes an object field resolver's field name and optional output type override.
|
|
10
|
+
*/
|
|
11
|
+
|
|
8
12
|
ensureMetadataSymbol();
|
|
9
13
|
function getStandardMetadataBag(metadata) {
|
|
10
14
|
return metadata;
|
|
@@ -53,11 +57,28 @@ function defineStandardHandlerMetadata(metadata, propertyKey, handlerMetadata) {
|
|
|
53
57
|
argTypes: handlerMetadata.argTypes,
|
|
54
58
|
fieldName: handlerMetadata.fieldName,
|
|
55
59
|
inputClass: handlerMetadata.inputClass,
|
|
60
|
+
nullable: handlerMetadata.nullable,
|
|
56
61
|
outputType: handlerMetadata.outputType,
|
|
57
62
|
type: handlerMetadata.type
|
|
58
63
|
});
|
|
59
64
|
bag[handlerMetadataSymbol] = map;
|
|
60
65
|
}
|
|
66
|
+
function defineStandardFieldResolverParameterMetadata(metadata, propertyKey, parameterIndex, kind) {
|
|
67
|
+
const bag = getStandardMetadataBag(metadata);
|
|
68
|
+
const current = bag[fieldResolverParameterMetadataSymbol];
|
|
69
|
+
const methods = current ?? new Map();
|
|
70
|
+
const bindings = methods.get(propertyKey) ?? new Map();
|
|
71
|
+
const existing = bindings.get(parameterIndex);
|
|
72
|
+
if (existing) {
|
|
73
|
+
throw new Error(`GraphQL field resolver parameter ${String(parameterIndex)} on ${String(propertyKey)} is already bound to ${existing.kind}.`);
|
|
74
|
+
}
|
|
75
|
+
bindings.set(parameterIndex, {
|
|
76
|
+
index: parameterIndex,
|
|
77
|
+
kind
|
|
78
|
+
});
|
|
79
|
+
methods.set(propertyKey, bindings);
|
|
80
|
+
bag[fieldResolverParameterMetadataSymbol] = methods;
|
|
81
|
+
}
|
|
61
82
|
function defineStandardArgFieldMetadata(metadata, propertyKey, argFieldMetadata) {
|
|
62
83
|
const bag = getStandardMetadataBag(metadata);
|
|
63
84
|
const current = bag[argMetadataSymbol];
|
|
@@ -82,6 +103,36 @@ function createMethodDecorator(type, fieldNameOrOptions) {
|
|
|
82
103
|
};
|
|
83
104
|
return decorator;
|
|
84
105
|
}
|
|
106
|
+
function normalizeFieldResolverMetadata(fieldNameOrOptions) {
|
|
107
|
+
if (typeof fieldNameOrOptions === 'string') {
|
|
108
|
+
return {
|
|
109
|
+
fieldName: fieldNameOrOptions.trim() || undefined,
|
|
110
|
+
type: 'field'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
fieldName: fieldNameOrOptions?.fieldName?.trim() || undefined,
|
|
115
|
+
nullable: fieldNameOrOptions?.nullable,
|
|
116
|
+
outputType: fieldNameOrOptions?.type,
|
|
117
|
+
type: 'field'
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function createFieldResolverParameterDecorator(kind, parameterIndex) {
|
|
121
|
+
if (!Number.isSafeInteger(parameterIndex) || parameterIndex < 0) {
|
|
122
|
+
throw new Error(`@${kind === 'parent' ? 'Parent' : 'Context'}() parameter index must be a non-negative integer.`);
|
|
123
|
+
}
|
|
124
|
+
const decorator = (_value, context) => {
|
|
125
|
+
const name = kind === 'parent' ? 'Parent' : 'Context';
|
|
126
|
+
if (context.private) {
|
|
127
|
+
throw new Error(`@${name}() cannot be used on private methods.`);
|
|
128
|
+
}
|
|
129
|
+
if (context.static) {
|
|
130
|
+
throw new Error(`@${name}() cannot be used on static methods.`);
|
|
131
|
+
}
|
|
132
|
+
defineStandardFieldResolverParameterMetadata(context.metadata, context.name, parameterIndex, kind);
|
|
133
|
+
};
|
|
134
|
+
return decorator;
|
|
135
|
+
}
|
|
85
136
|
|
|
86
137
|
/**
|
|
87
138
|
* Resolver.
|
|
@@ -128,6 +179,56 @@ export function Subscription(fieldNameOrOptions) {
|
|
|
128
179
|
return createMethodDecorator('subscription', fieldNameOrOptions);
|
|
129
180
|
}
|
|
130
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Marks a public instance method as the resolver for one field on the object type owned by `@Resolver(typeName)`.
|
|
184
|
+
*
|
|
185
|
+
* @param fieldNameOrOptions Field name or object field resolver options.
|
|
186
|
+
* @returns A TC39 standard method decorator.
|
|
187
|
+
*/
|
|
188
|
+
export function FieldResolver(fieldNameOrOptions) {
|
|
189
|
+
const metadata = normalizeFieldResolverMetadata(fieldNameOrOptions);
|
|
190
|
+
const decorator = (_value, context) => {
|
|
191
|
+
if (context.private) {
|
|
192
|
+
throw new Error('@FieldResolver() cannot be used on private methods.');
|
|
193
|
+
}
|
|
194
|
+
if (context.static) {
|
|
195
|
+
throw new Error('@FieldResolver() cannot be used on static methods.');
|
|
196
|
+
}
|
|
197
|
+
defineStandardHandlerMetadata(context.metadata, context.name, metadata);
|
|
198
|
+
};
|
|
199
|
+
return decorator;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Binds a field resolver method parameter to GraphQL's parent/source object.
|
|
204
|
+
*
|
|
205
|
+
* @remarks
|
|
206
|
+
* TC39 standard decorators do not support parameter-decorator syntax, so this
|
|
207
|
+
* standard method decorator records the parameter index explicitly. The default
|
|
208
|
+
* index is `0`.
|
|
209
|
+
*
|
|
210
|
+
* @param parameterIndex Zero-based method parameter index to receive the parent value.
|
|
211
|
+
* @returns A TC39 standard method decorator.
|
|
212
|
+
*/
|
|
213
|
+
export function Parent(parameterIndex = 0) {
|
|
214
|
+
return createFieldResolverParameterDecorator('parent', parameterIndex);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Binds a field resolver method parameter to the active `GraphQLContext`.
|
|
219
|
+
*
|
|
220
|
+
* @remarks
|
|
221
|
+
* TC39 standard decorators do not support parameter-decorator syntax, so this
|
|
222
|
+
* standard method decorator records the parameter index explicitly. The default
|
|
223
|
+
* index is `1`.
|
|
224
|
+
*
|
|
225
|
+
* @param parameterIndex Zero-based method parameter index to receive the context value.
|
|
226
|
+
* @returns A TC39 standard method decorator.
|
|
227
|
+
*/
|
|
228
|
+
export function Context(parameterIndex = 1) {
|
|
229
|
+
return createFieldResolverParameterDecorator('context', parameterIndex);
|
|
230
|
+
}
|
|
231
|
+
|
|
131
232
|
/**
|
|
132
233
|
* Arg.
|
|
133
234
|
*
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkI3E;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,EAAE,CAwDtB"}
|
package/dist/discovery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getClassDiMetadata } from '@fluojs/core/internal';
|
|
2
|
-
import { getArgFieldMetadataEntries, getResolverHandlerMetadataEntries, getResolverMetadata } from './metadata.js';
|
|
2
|
+
import { getArgFieldMetadataEntries, getFieldResolverParameterMetadataEntries, getResolverHandlerMetadataEntries, getResolverMetadata } from './metadata.js';
|
|
3
3
|
function scopeFromProvider(provider, factoryResolverClass) {
|
|
4
4
|
if (typeof provider === 'function') {
|
|
5
5
|
return getClassDiMetadata(provider)?.scope ?? 'singleton';
|
|
@@ -127,6 +127,10 @@ export function discoverResolverDescriptors(compiledModules, options) {
|
|
|
127
127
|
handlers: getResolverHandlerMetadataEntries(candidate.targetType.prototype).map(entry => {
|
|
128
128
|
const inputClass = entry.metadata.inputClass;
|
|
129
129
|
const argFields = inputClass !== undefined ? getArgFieldMetadataEntries(inputClass.prototype).map(argField => argField.metadata) : [];
|
|
130
|
+
const parameterBindings = getFieldResolverParameterMetadataEntries(candidate.targetType.prototype, entry.propertyKey);
|
|
131
|
+
if (entry.metadata.type !== 'field' && parameterBindings.length > 0) {
|
|
132
|
+
throw new Error(`@Parent() and @Context() can only bind parameters on @FieldResolver() methods. ` + `Invalid placement: ${candidate.targetType.name}.${methodKeyToName(entry.propertyKey)}.`);
|
|
133
|
+
}
|
|
130
134
|
return {
|
|
131
135
|
argFields,
|
|
132
136
|
argTypes: entry.metadata.argTypes,
|
|
@@ -134,7 +138,9 @@ export function discoverResolverDescriptors(compiledModules, options) {
|
|
|
134
138
|
inputClass,
|
|
135
139
|
methodKey: entry.propertyKey,
|
|
136
140
|
methodName: methodKeyToName(entry.propertyKey),
|
|
141
|
+
nullable: entry.metadata.nullable,
|
|
137
142
|
outputType: entry.metadata.outputType,
|
|
143
|
+
parameterBindings,
|
|
138
144
|
type: entry.metadata.type
|
|
139
145
|
};
|
|
140
146
|
}),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './dataloader.js';
|
|
2
2
|
export * from './decorators.js';
|
|
3
3
|
export { GraphqlModule } from './module.js';
|
|
4
|
+
export type { ArgFieldMetadata, FieldResolverParameterBindingMetadata, FieldResolverParameterKind, GraphQLContext, GraphqlArgType, GraphqlListTypeRef, GraphqlModuleOptions, GraphqlRequestContext, GraphqlRequestLimitsOptions, GraphqlRootOutputNamedType, GraphqlRootOutputType, GraphqlScalarTypeName, GraphqlSubscriptionsOptions, GraphqlWebSocketSubscriptionsOptions, ResolverHandlerMetadata, ResolverHandlerType, ResolverMetadata, } from './types.js';
|
|
4
5
|
export { isGraphqlListTypeRef, listOf, } from './types.js';
|
|
5
|
-
export type { ArgFieldMetadata, GraphQLContext, GraphqlArgType, GraphqlListTypeRef, GraphqlModuleOptions, GraphqlRequestLimitsOptions, GraphqlRequestContext, GraphqlRootOutputNamedType, GraphqlRootOutputType, GraphqlScalarTypeName, GraphqlSubscriptionsOptions, GraphqlWebSocketSubscriptionsOptions, ResolverHandlerMetadata, ResolverHandlerType, ResolverMetadata, } from './types.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EACV,gBAAgB,EAChB,qCAAqC,EACrC,0BAA0B,EAC1B,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,oCAAoC,EACpC,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,MAAM,GACP,MAAM,YAAY,CAAC"}
|
package/dist/metadata.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MetadataPropertyKey } from '@fluojs/core';
|
|
2
|
-
import type { ArgFieldMetadata, ResolverHandlerMetadata, ResolverMetadata } from './types.js';
|
|
2
|
+
import type { ArgFieldMetadata, FieldResolverParameterBindingMetadata, ResolverHandlerMetadata, ResolverMetadata } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Define resolver metadata.
|
|
5
5
|
*
|
|
@@ -66,6 +66,22 @@ export declare function getArgFieldMetadataEntries(target: object): Array<{
|
|
|
66
66
|
metadata: ArgFieldMetadata;
|
|
67
67
|
propertyKey: MetadataPropertyKey;
|
|
68
68
|
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Define one positional parameter binding for an object field resolver.
|
|
71
|
+
*
|
|
72
|
+
* @param target Resolver prototype that owns the decorated method.
|
|
73
|
+
* @param propertyKey Decorated method key.
|
|
74
|
+
* @param metadata Positional binding metadata to store.
|
|
75
|
+
*/
|
|
76
|
+
export declare function defineFieldResolverParameterMetadata(target: object, propertyKey: MetadataPropertyKey, metadata: FieldResolverParameterBindingMetadata): void;
|
|
77
|
+
/**
|
|
78
|
+
* Get the positional parameter bindings declared for one resolver method.
|
|
79
|
+
*
|
|
80
|
+
* @param target Resolver prototype that owns the decorated method.
|
|
81
|
+
* @param propertyKey Decorated method key.
|
|
82
|
+
* @returns Parameter bindings ordered by their method parameter index.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getFieldResolverParameterMetadataEntries(target: object, propertyKey: MetadataPropertyKey): FieldResolverParameterBindingMetadata[];
|
|
69
85
|
/**
|
|
70
86
|
* Provides the resolver metadata symbol value.
|
|
71
87
|
*/
|
|
@@ -78,4 +94,8 @@ export declare const handlerMetadataSymbol: symbol;
|
|
|
78
94
|
* Provides the arg metadata symbol value.
|
|
79
95
|
*/
|
|
80
96
|
export declare const argMetadataSymbol: symbol;
|
|
97
|
+
/**
|
|
98
|
+
* Provides the object field-resolver parameter metadata symbol value.
|
|
99
|
+
*/
|
|
100
|
+
export declare const fieldResolverParameterMetadataSymbol: symbol;
|
|
81
101
|
//# sourceMappingURL=metadata.d.ts.map
|
package/dist/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,KAAK,EACV,gBAAgB,EAChB,qCAAqC,EACrC,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAwIpB;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEvF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAUhF;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,uBAAuB,GAAG,SAAS,CAUrC;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,uBAAuB,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAOhF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEzH;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,SAAS,CAUlH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAEzE;AAED;;;;;;GAMG;AACH,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,qCAAqC,GAC9C,IAAI,CAKN;AAED;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,qCAAqC,EAAE,CAczC;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,QAA8B,CAAC;AAClE;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAA6B,CAAC;AAChE;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAA8B,CAAC;AAC7D;;GAEG;AACH,eAAO,MAAM,oCAAoC,QAA4C,CAAC"}
|