@fluojs/graphql 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.ko.md +158 -0
- package/README.md +158 -0
- package/dist/dataloader/dataloader.d.ts +127 -0
- package/dist/dataloader/dataloader.d.ts.map +1 -0
- package/dist/dataloader/dataloader.js +151 -0
- package/dist/dataloader.d.ts +2 -0
- package/dist/dataloader.d.ts.map +1 -0
- package/dist/dataloader.js +1 -0
- package/dist/decorators.d.ts +21 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +111 -0
- package/dist/discovery.d.ts +4 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +142 -0
- package/dist/guardrails.d.ts +35 -0
- package/dist/guardrails.d.ts.map +1 -0
- package/dist/guardrails.js +162 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/internal-tokens.d.ts +7 -0
- package/dist/internal-tokens.d.ts.map +1 -0
- package/dist/internal-tokens.js +4 -0
- package/dist/metadata.d.ts +20 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +120 -0
- package/dist/module.d.ts +22 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +33 -0
- package/dist/pipeline/input-pipeline.d.ts +34 -0
- package/dist/pipeline/input-pipeline.d.ts.map +1 -0
- package/dist/pipeline/input-pipeline.js +129 -0
- package/dist/schema/schema.d.ts +20 -0
- package/dist/schema/schema.d.ts.map +1 -0
- package/dist/schema/schema.js +278 -0
- package/dist/service.d.ts +58 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +602 -0
- package/dist/transport/transport.d.ts +5 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +119 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +132 -0
- package/package.json +61 -0
package/dist/module.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineModule } from '@fluojs/runtime';
|
|
2
|
+
import { GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN } from './internal-tokens.js';
|
|
3
|
+
import { GraphqlEndpointController, GraphqlLifecycleService } from './service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates GraphQL runtime providers for module-level options and lifecycle wiring.
|
|
6
|
+
*
|
|
7
|
+
* @param options GraphQL module options used by the lifecycle service and endpoint controller.
|
|
8
|
+
* @returns Provider definitions that register only the internal options token and GraphQL lifecycle service; this helper
|
|
9
|
+
* does not register `GraphqlEndpointController` or mount the `/graphql` endpoint by itself.
|
|
10
|
+
*/
|
|
11
|
+
export function createGraphqlProviders(options) {
|
|
12
|
+
return [{
|
|
13
|
+
provide: GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN,
|
|
14
|
+
useValue: options
|
|
15
|
+
}, GraphqlLifecycleService];
|
|
16
|
+
}
|
|
17
|
+
export class GraphqlModule {
|
|
18
|
+
/**
|
|
19
|
+
* Registers the GraphQL endpoint controller together with lifecycle providers.
|
|
20
|
+
*
|
|
21
|
+
* @param options Optional GraphQL module options for schema, resolver discovery, context, and plugins.
|
|
22
|
+
* @returns A module definition that wires GraphQL runtime behavior and mounts the GraphQL endpoint controller; use this
|
|
23
|
+
* module path (not `createGraphqlProviders(...)` alone) when the application should expose `/graphql`.
|
|
24
|
+
*/
|
|
25
|
+
static forRoot(options = {}) {
|
|
26
|
+
class GraphqlRootModule extends GraphqlModule {}
|
|
27
|
+
return defineModule(GraphqlRootModule, {
|
|
28
|
+
controllers: [GraphqlEndpointController],
|
|
29
|
+
middleware: [],
|
|
30
|
+
providers: createGraphqlProviders(options)
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { GraphqlArgType, GraphqlRootOutputType, GraphqlScalarTypeName, ResolverHandlerDescriptor } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Infers the scalar GraphQL type for one resolver argument from explicit metadata or DTO validation rules.
|
|
4
|
+
*
|
|
5
|
+
* @param handler Resolver descriptor that carries explicit arg metadata and DTO bindings.
|
|
6
|
+
* @param argName GraphQL argument name to inspect.
|
|
7
|
+
* @returns The inferred scalar type name used by the schema builder.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveArgScalarType(handler: ResolverHandlerDescriptor, argName: string): GraphqlScalarTypeName;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the scalar GraphQL argument type for one resolver argument.
|
|
12
|
+
*
|
|
13
|
+
* @param handler Resolver descriptor that carries explicit arg metadata and DTO bindings.
|
|
14
|
+
* @param argName GraphQL argument name to inspect.
|
|
15
|
+
* @returns The inferred scalar type name used by the schema builder.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveArgType(handler: ResolverHandlerDescriptor, argName: string): GraphqlArgType;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the GraphQL output type for one resolver handler.
|
|
20
|
+
*
|
|
21
|
+
* @param handler Resolver descriptor that may define an explicit output type.
|
|
22
|
+
* @returns The configured output type, or the default `string` scalar fallback.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveOutputType(handler: ResolverHandlerDescriptor): GraphqlRootOutputType;
|
|
25
|
+
/**
|
|
26
|
+
* Materializes and validates one resolver input object from GraphQL arguments.
|
|
27
|
+
*
|
|
28
|
+
* @param inputClass Optional DTO constructor used for instantiation and validation.
|
|
29
|
+
* @param args GraphQL argument payload received for the resolver call.
|
|
30
|
+
* @param argFieldDescriptors Mapping between GraphQL argument names and DTO field names.
|
|
31
|
+
* @returns The validated DTO instance, raw argument record, or `undefined` when no input was provided.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createGraphqlInput(inputClass: Function | undefined, args: Record<string, unknown>, argFieldDescriptors: ResolverHandlerDescriptor['argFields']): Promise<unknown>;
|
|
34
|
+
//# sourceMappingURL=input-pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/input-pipeline.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAqD3H;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAiC/G;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAOlG;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,yBAAyB,GAAG,qBAAqB,CAE3F;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,QAAQ,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,mBAAmB,EAAE,yBAAyB,CAAC,WAAW,CAAC,GAC1D,OAAO,CAAC,OAAO,CAAC,CA0BlB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getDtoValidationSchema } from '@fluojs/core/internal';
|
|
2
|
+
import { DefaultValidator } from '@fluojs/validation';
|
|
3
|
+
import { isGraphqlListTypeRef } from '../types.js';
|
|
4
|
+
const defaultValidator = new DefaultValidator();
|
|
5
|
+
const blockedGraphqlInputKeys = new Set(['__proto__', 'constructor', 'prototype']);
|
|
6
|
+
function isSafeGraphqlInputKey(fieldName) {
|
|
7
|
+
return !blockedGraphqlInputKeys.has(fieldName);
|
|
8
|
+
}
|
|
9
|
+
function hasRule(rules, kind) {
|
|
10
|
+
return rules.some(rule => rule.kind === kind);
|
|
11
|
+
}
|
|
12
|
+
function inferScalarFromValidationRules(rules) {
|
|
13
|
+
if (hasRule(rules, 'int')) {
|
|
14
|
+
return 'int';
|
|
15
|
+
}
|
|
16
|
+
if (hasRule(rules, 'number') || hasRule(rules, 'min') || hasRule(rules, 'max') || hasRule(rules, 'divisibleBy')) {
|
|
17
|
+
return 'float';
|
|
18
|
+
}
|
|
19
|
+
if (hasRule(rules, 'boolean')) {
|
|
20
|
+
return 'boolean';
|
|
21
|
+
}
|
|
22
|
+
if (hasRule(rules, 'string') || hasRule(rules, 'validatorjs')) {
|
|
23
|
+
return 'string';
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
function inferScalarFromDefaultValue(value) {
|
|
28
|
+
if (typeof value === 'boolean') {
|
|
29
|
+
return 'boolean';
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === 'number') {
|
|
32
|
+
return Number.isInteger(value) ? 'int' : 'float';
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
return 'string';
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
function toPropertyKey(fieldName) {
|
|
40
|
+
return fieldName;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Infers the scalar GraphQL type for one resolver argument from explicit metadata or DTO validation rules.
|
|
45
|
+
*
|
|
46
|
+
* @param handler Resolver descriptor that carries explicit arg metadata and DTO bindings.
|
|
47
|
+
* @param argName GraphQL argument name to inspect.
|
|
48
|
+
* @returns The inferred scalar type name used by the schema builder.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveArgScalarType(handler, argName) {
|
|
51
|
+
const explicit = handler.argTypes?.[argName];
|
|
52
|
+
if (explicit && !isGraphqlListTypeRef(explicit)) {
|
|
53
|
+
return explicit;
|
|
54
|
+
}
|
|
55
|
+
if (!handler.inputClass) {
|
|
56
|
+
return 'string';
|
|
57
|
+
}
|
|
58
|
+
const mappedFieldName = handler.argFields.find(field => field.argName === argName)?.fieldName;
|
|
59
|
+
if (!mappedFieldName) {
|
|
60
|
+
return 'string';
|
|
61
|
+
}
|
|
62
|
+
const validationEntry = getDtoValidationSchema(handler.inputClass).find(entry => entry.propertyKey === toPropertyKey(mappedFieldName));
|
|
63
|
+
const inferredFromRules = validationEntry ? inferScalarFromValidationRules(validationEntry.rules) : undefined;
|
|
64
|
+
if (inferredFromRules) {
|
|
65
|
+
return inferredFromRules;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const seed = new handler.inputClass();
|
|
69
|
+
return inferScalarFromDefaultValue(seed[mappedFieldName]) ?? 'string';
|
|
70
|
+
} catch {
|
|
71
|
+
return 'string';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolves the scalar GraphQL argument type for one resolver argument.
|
|
77
|
+
*
|
|
78
|
+
* @param handler Resolver descriptor that carries explicit arg metadata and DTO bindings.
|
|
79
|
+
* @param argName GraphQL argument name to inspect.
|
|
80
|
+
* @returns The inferred scalar type name used by the schema builder.
|
|
81
|
+
*/
|
|
82
|
+
export function resolveArgType(handler, argName) {
|
|
83
|
+
const explicit = handler.argTypes?.[argName];
|
|
84
|
+
if (explicit) {
|
|
85
|
+
return explicit;
|
|
86
|
+
}
|
|
87
|
+
return resolveArgScalarType(handler, argName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the GraphQL output type for one resolver handler.
|
|
92
|
+
*
|
|
93
|
+
* @param handler Resolver descriptor that may define an explicit output type.
|
|
94
|
+
* @returns The configured output type, or the default `string` scalar fallback.
|
|
95
|
+
*/
|
|
96
|
+
export function resolveOutputType(handler) {
|
|
97
|
+
return handler.outputType ?? 'string';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Materializes and validates one resolver input object from GraphQL arguments.
|
|
102
|
+
*
|
|
103
|
+
* @param inputClass Optional DTO constructor used for instantiation and validation.
|
|
104
|
+
* @param args GraphQL argument payload received for the resolver call.
|
|
105
|
+
* @param argFieldDescriptors Mapping between GraphQL argument names and DTO field names.
|
|
106
|
+
* @returns The validated DTO instance, raw argument record, or `undefined` when no input was provided.
|
|
107
|
+
*/
|
|
108
|
+
export async function createGraphqlInput(inputClass, args, argFieldDescriptors) {
|
|
109
|
+
if (!inputClass) {
|
|
110
|
+
return Object.keys(args).length === 0 ? undefined : args;
|
|
111
|
+
}
|
|
112
|
+
const instance = new inputClass();
|
|
113
|
+
if (argFieldDescriptors.length === 0) {
|
|
114
|
+
for (const [fieldName, value] of Object.entries(args)) {
|
|
115
|
+
if (!isSafeGraphqlInputKey(fieldName)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
instance[fieldName] = value;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
for (const descriptor of argFieldDescriptors) {
|
|
122
|
+
if (isSafeGraphqlInputKey(descriptor.fieldName) && Object.hasOwn(args, descriptor.argName)) {
|
|
123
|
+
instance[descriptor.fieldName] = args[descriptor.argName];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
await defaultValidator.validate(instance, inputClass);
|
|
128
|
+
return instance;
|
|
129
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Container } from '@fluojs/di';
|
|
2
|
+
import type { GraphQLError as GraphQLErrorType, GraphQLList as GraphQLListType, GraphQLObjectType as GraphQLObjectTypeType, GraphQLSchema as GraphQLSchemaType, GraphQLScalarType, GraphQLUnionType as GraphQLUnionTypeType } from 'graphql';
|
|
3
|
+
import { type ResolverDescriptor } from '../types.js';
|
|
4
|
+
type YogaGraphqlDeps = {
|
|
5
|
+
GraphQLError: typeof GraphQLErrorType;
|
|
6
|
+
GraphQLBoolean: GraphQLScalarType;
|
|
7
|
+
GraphQLFloat: GraphQLScalarType;
|
|
8
|
+
GraphQLID: GraphQLScalarType;
|
|
9
|
+
GraphQLInt: GraphQLScalarType;
|
|
10
|
+
GraphQLList: typeof GraphQLListType;
|
|
11
|
+
GraphQLObjectType: typeof GraphQLObjectTypeType;
|
|
12
|
+
GraphQLSchema: typeof GraphQLSchemaType;
|
|
13
|
+
GraphQLString: GraphQLScalarType;
|
|
14
|
+
GraphQLUnionType: typeof GraphQLUnionTypeType;
|
|
15
|
+
buildSchema: (source: string) => GraphQLSchemaType;
|
|
16
|
+
};
|
|
17
|
+
export declare function resolveSchema(deps: YogaGraphqlDeps, optionsSchema: GraphQLSchemaType | string | undefined, createCodeFirstSchema: () => GraphQLSchemaType, markAllowedCrossRealmGraphqlObjects: (value: unknown) => void): GraphQLSchemaType;
|
|
18
|
+
export declare function createCodeFirstSchema(deps: YogaGraphqlDeps, runtimeContainer: Container, resolverDescriptors: ResolverDescriptor[], markAllowedCrossRealmGraphqlObjects?: (value: unknown) => void): GraphQLSchemaType;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAGV,YAAY,IAAI,gBAAgB,EAGhC,WAAW,IAAI,eAAe,EAC9B,iBAAiB,IAAI,qBAAqB,EAE1C,aAAa,IAAI,iBAAiB,EAClC,iBAAiB,EACjB,gBAAgB,IAAI,oBAAoB,EACzC,MAAM,SAAS,CAAC;AAIjB,OAAO,EAOL,KAAK,kBAAkB,EAGxB,MAAM,aAAa,CAAC;AAErB,KAAK,eAAe,GAAG;IACrB,YAAY,EAAE,OAAO,gBAAgB,CAAC;IACtC,cAAc,EAAE,iBAAiB,CAAC;IAClC,YAAY,EAAE,iBAAiB,CAAC;IAChC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,OAAO,eAAe,CAAC;IACpC,iBAAiB,EAAE,OAAO,qBAAqB,CAAC;IAChD,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACxC,aAAa,EAAE,iBAAiB,CAAC;IACjC,gBAAgB,EAAE,OAAO,oBAAoB,CAAC;IAC9C,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,iBAAiB,CAAC;CACpD,CAAC;AAiaF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,EACrD,qBAAqB,EAAE,MAAM,iBAAiB,EAC9C,mCAAmC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC5D,iBAAiB,CAWnB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,gBAAgB,EAAE,SAAS,EAC3B,mBAAmB,EAAE,kBAAkB,EAAE,EACzC,mCAAmC,GAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAe,GACvE,iBAAiB,CA0CnB"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { DtoValidationError } from '@fluojs/validation';
|
|
2
|
+
import { createGraphqlInput, resolveArgType, resolveOutputType } from '../pipeline/input-pipeline.js';
|
|
3
|
+
import { GRAPHQL_OPERATION_CONTAINER, isGraphqlListTypeRef } from '../types.js';
|
|
4
|
+
function isAsyncIterable(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && Symbol.asyncIterator in value;
|
|
6
|
+
}
|
|
7
|
+
function scalarByName(deps, scalar) {
|
|
8
|
+
switch (scalar) {
|
|
9
|
+
case 'int':
|
|
10
|
+
return deps.GraphQLInt;
|
|
11
|
+
case 'float':
|
|
12
|
+
return deps.GraphQLFloat;
|
|
13
|
+
case 'boolean':
|
|
14
|
+
return deps.GraphQLBoolean;
|
|
15
|
+
case 'id':
|
|
16
|
+
return deps.GraphQLID;
|
|
17
|
+
case 'string':
|
|
18
|
+
default:
|
|
19
|
+
return deps.GraphQLString;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function builtinScalarByGraphqlName(deps, scalarName) {
|
|
23
|
+
switch (scalarName) {
|
|
24
|
+
case 'String':
|
|
25
|
+
return deps.GraphQLString;
|
|
26
|
+
case 'Int':
|
|
27
|
+
return deps.GraphQLInt;
|
|
28
|
+
case 'Float':
|
|
29
|
+
return deps.GraphQLFloat;
|
|
30
|
+
case 'Boolean':
|
|
31
|
+
return deps.GraphQLBoolean;
|
|
32
|
+
case 'ID':
|
|
33
|
+
return deps.GraphQLID;
|
|
34
|
+
default:
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function normalizeFieldOutputType(deps, type) {
|
|
39
|
+
const maybeScalarName = type.name;
|
|
40
|
+
if (typeof maybeScalarName === 'string') {
|
|
41
|
+
return builtinScalarByGraphqlName(deps, maybeScalarName) ?? type;
|
|
42
|
+
}
|
|
43
|
+
return type;
|
|
44
|
+
}
|
|
45
|
+
function normalizeObjectOutputType(deps, outputTypeCache, outputType) {
|
|
46
|
+
const outputTypeName = outputType.name;
|
|
47
|
+
const cached = outputTypeCache.get(outputTypeName);
|
|
48
|
+
if (cached) {
|
|
49
|
+
return cached;
|
|
50
|
+
}
|
|
51
|
+
const config = outputType.toConfig();
|
|
52
|
+
const clonedFields = Object.fromEntries(Object.entries(config.fields).map(([fieldName, fieldConfig]) => {
|
|
53
|
+
const field = fieldConfig;
|
|
54
|
+
return [fieldName, {
|
|
55
|
+
...field,
|
|
56
|
+
type: normalizeFieldOutputType(deps, field.type)
|
|
57
|
+
}];
|
|
58
|
+
}));
|
|
59
|
+
const normalized = new deps.GraphQLObjectType({
|
|
60
|
+
...config,
|
|
61
|
+
fields: clonedFields
|
|
62
|
+
});
|
|
63
|
+
outputTypeCache.set(outputTypeName, normalized);
|
|
64
|
+
return normalized;
|
|
65
|
+
}
|
|
66
|
+
function normalizeUnionOutputType(deps, outputTypeCache, outputType) {
|
|
67
|
+
const outputTypeName = outputType.name;
|
|
68
|
+
const cached = outputTypeCache.get(outputTypeName);
|
|
69
|
+
if (cached) {
|
|
70
|
+
return cached;
|
|
71
|
+
}
|
|
72
|
+
const config = outputType.toConfig();
|
|
73
|
+
const normalizedTypes = config.types.map(itemType => normalizeObjectOutputType(deps, outputTypeCache, itemType));
|
|
74
|
+
const normalizedTypeByName = new Set(normalizedTypes.map(itemType => itemType.name).filter(name => typeof name === 'string'));
|
|
75
|
+
const normalized = new deps.GraphQLUnionType({
|
|
76
|
+
...config,
|
|
77
|
+
resolveType: async (...args) => {
|
|
78
|
+
if (!config.resolveType) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const resolved = await config.resolveType(...args);
|
|
82
|
+
if (typeof resolved === 'string' || resolved === null || resolved === undefined) {
|
|
83
|
+
return typeof resolved === 'string' && normalizedTypeByName.has(resolved) ? resolved : resolved;
|
|
84
|
+
}
|
|
85
|
+
const resolvedName = resolved.name;
|
|
86
|
+
if (typeof resolvedName === 'string') {
|
|
87
|
+
return normalizedTypeByName.has(resolvedName) ? resolvedName : undefined;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
},
|
|
91
|
+
types: normalizedTypes
|
|
92
|
+
});
|
|
93
|
+
outputTypeCache.set(outputTypeName, normalized);
|
|
94
|
+
return normalized;
|
|
95
|
+
}
|
|
96
|
+
function isUnionOutputType(value) {
|
|
97
|
+
return typeof value === 'object' && typeof value.getTypes === 'function';
|
|
98
|
+
}
|
|
99
|
+
function resolveArgGraphqlType(deps, argType) {
|
|
100
|
+
if (isGraphqlListTypeRef(argType)) {
|
|
101
|
+
return new deps.GraphQLList(scalarByName(deps, argType.ofType));
|
|
102
|
+
}
|
|
103
|
+
return scalarByName(deps, argType);
|
|
104
|
+
}
|
|
105
|
+
function resolveNamedRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputRef) {
|
|
106
|
+
if (typeof outputRef === 'string') {
|
|
107
|
+
return scalarByName(deps, outputRef);
|
|
108
|
+
}
|
|
109
|
+
markAllowedCrossRealmGraphqlObjects(outputRef);
|
|
110
|
+
if (isUnionOutputType(outputRef)) {
|
|
111
|
+
return normalizeUnionOutputType(deps, outputTypeCache, outputRef);
|
|
112
|
+
}
|
|
113
|
+
return normalizeObjectOutputType(deps, outputTypeCache, outputRef);
|
|
114
|
+
}
|
|
115
|
+
function resolveRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputRef) {
|
|
116
|
+
if (isGraphqlListTypeRef(outputRef)) {
|
|
117
|
+
const listItemType = resolveNamedRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputRef.ofType);
|
|
118
|
+
return new deps.GraphQLList(listItemType);
|
|
119
|
+
}
|
|
120
|
+
return resolveNamedRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputRef);
|
|
121
|
+
}
|
|
122
|
+
function createFieldArgs(deps, handler) {
|
|
123
|
+
return Object.fromEntries(handler.argFields.map(argField => [argField.argName, {
|
|
124
|
+
type: resolveArgGraphqlType(deps, resolveArgType(handler, argField.argName))
|
|
125
|
+
}]));
|
|
126
|
+
}
|
|
127
|
+
function createSubscriptionField(descriptor, handler, args, outputType, invokeResolver) {
|
|
128
|
+
return {
|
|
129
|
+
args,
|
|
130
|
+
resolve(payload) {
|
|
131
|
+
return payload;
|
|
132
|
+
},
|
|
133
|
+
subscribe: async (_source, rawArgs, contextValue) => {
|
|
134
|
+
const value = await invokeResolver(descriptor, handler, rawArgs, contextValue);
|
|
135
|
+
if (!isAsyncIterable(value)) {
|
|
136
|
+
throw new Error(`Subscription resolver ${descriptor.targetName}.${handler.methodName} must return AsyncIterable.`);
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
},
|
|
140
|
+
type: outputType
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function createOperationField(descriptor, handler, args, outputType, invokeResolver) {
|
|
144
|
+
return {
|
|
145
|
+
args,
|
|
146
|
+
resolve: async (_source, rawArgs, contextValue) => invokeResolver(descriptor, handler, rawArgs, contextValue),
|
|
147
|
+
type: outputType
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function pickFieldsByType(deps, descriptors, handlerType, markAllowedCrossRealmGraphqlObjects, outputTypeCache, invokeResolver) {
|
|
151
|
+
const fields = {};
|
|
152
|
+
for (const descriptor of descriptors) {
|
|
153
|
+
for (const handler of descriptor.handlers) {
|
|
154
|
+
if (handler.type !== handlerType) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const args = createFieldArgs(deps, handler);
|
|
158
|
+
const outputRef = resolveOutputType(handler);
|
|
159
|
+
const outputType = resolveRootOutputType(deps, outputTypeCache, markAllowedCrossRealmGraphqlObjects, outputRef);
|
|
160
|
+
if (Object.hasOwn(fields, handler.fieldName)) {
|
|
161
|
+
throw new Error(`GraphQL schema conflict: field "${handler.fieldName}" on ${handlerType} type is registered more than once. ` + `Found duplicate in resolver "${descriptor.targetName}". Each field name must be unique across all resolvers.`);
|
|
162
|
+
}
|
|
163
|
+
if (handler.type === 'subscription') {
|
|
164
|
+
fields[handler.fieldName] = createSubscriptionField(descriptor, handler, args, outputType, invokeResolver);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
fields[handler.fieldName] = createOperationField(descriptor, handler, args, outputType, invokeResolver);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return fields;
|
|
171
|
+
}
|
|
172
|
+
function isGraphQLSchemaLike(value) {
|
|
173
|
+
if (value === null || typeof value !== 'object') {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return typeof value.getQueryType === 'function' && typeof value.getTypeMap === 'function';
|
|
177
|
+
}
|
|
178
|
+
function toGraphqlValidationError(deps, error) {
|
|
179
|
+
return new deps.GraphQLError('Validation failed.', {
|
|
180
|
+
extensions: {
|
|
181
|
+
code: 'BAD_USER_INPUT',
|
|
182
|
+
issues: error.issues
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function createResolverInput(deps, handler, args) {
|
|
187
|
+
try {
|
|
188
|
+
return await createGraphqlInput(handler.inputClass, args, handler.argFields);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof DtoValidationError) {
|
|
191
|
+
throw toGraphqlValidationError(deps, error);
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function resolveResolverMethod(instance, descriptor, handler) {
|
|
197
|
+
const value = instance[handler.methodKey];
|
|
198
|
+
if (typeof value !== 'function') {
|
|
199
|
+
throw new Error(`Resolver handler ${descriptor.targetName}.${handler.methodName} is not callable.`);
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
function createResolverInvoker(deps, runtimeContainer) {
|
|
204
|
+
return async (descriptor, handler, args, contextValue) => {
|
|
205
|
+
if (descriptor.scope === 'singleton') {
|
|
206
|
+
const instance = await runtimeContainer.resolve(descriptor.token);
|
|
207
|
+
const resolverMethod = resolveResolverMethod(instance, descriptor, handler);
|
|
208
|
+
const input = await createResolverInput(deps, handler, args);
|
|
209
|
+
return resolverMethod.call(instance, input, contextValue);
|
|
210
|
+
}
|
|
211
|
+
const operationContainer = contextValue[GRAPHQL_OPERATION_CONTAINER] ?? runtimeContainer.createRequestScope();
|
|
212
|
+
const disposeOperationContainer = contextValue[GRAPHQL_OPERATION_CONTAINER] === undefined;
|
|
213
|
+
try {
|
|
214
|
+
const instance = await operationContainer.resolve(descriptor.token);
|
|
215
|
+
const resolverMethod = resolveResolverMethod(instance, descriptor, handler);
|
|
216
|
+
const input = await createResolverInput(deps, handler, args);
|
|
217
|
+
return await resolverMethod.call(instance, input, contextValue);
|
|
218
|
+
} finally {
|
|
219
|
+
if (disposeOperationContainer) {
|
|
220
|
+
await operationContainer.dispose();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function createQueryRootType(deps, queryFields) {
|
|
226
|
+
if (Object.keys(queryFields).length === 0) {
|
|
227
|
+
return new deps.GraphQLObjectType({
|
|
228
|
+
fields: {
|
|
229
|
+
_empty: {
|
|
230
|
+
resolve: () => 'ok',
|
|
231
|
+
type: deps.GraphQLString
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
name: 'Query'
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return new deps.GraphQLObjectType({
|
|
238
|
+
fields: queryFields,
|
|
239
|
+
name: 'Query'
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function createOptionalRootType(deps, name, fields) {
|
|
243
|
+
if (Object.keys(fields).length === 0) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
return new deps.GraphQLObjectType({
|
|
247
|
+
fields,
|
|
248
|
+
name
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
export function resolveSchema(deps, optionsSchema, createCodeFirstSchema, markAllowedCrossRealmGraphqlObjects) {
|
|
252
|
+
if (isGraphQLSchemaLike(optionsSchema)) {
|
|
253
|
+
markAllowedCrossRealmGraphqlObjects(optionsSchema);
|
|
254
|
+
return optionsSchema;
|
|
255
|
+
}
|
|
256
|
+
if (typeof optionsSchema === 'string') {
|
|
257
|
+
return deps.buildSchema(optionsSchema);
|
|
258
|
+
}
|
|
259
|
+
return createCodeFirstSchema();
|
|
260
|
+
}
|
|
261
|
+
export function createCodeFirstSchema(deps, runtimeContainer, resolverDescriptors, markAllowedCrossRealmGraphqlObjects = () => {}) {
|
|
262
|
+
if (resolverDescriptors.length === 0) {
|
|
263
|
+
throw new Error('GraphQL module requires either schema or at least one resolver decorated with @Resolver().');
|
|
264
|
+
}
|
|
265
|
+
const invokeResolver = createResolverInvoker(deps, runtimeContainer);
|
|
266
|
+
const outputTypeCache = new Map();
|
|
267
|
+
const queryFields = pickFieldsByType(deps, resolverDescriptors, 'query', markAllowedCrossRealmGraphqlObjects, outputTypeCache, invokeResolver);
|
|
268
|
+
const mutationFields = pickFieldsByType(deps, resolverDescriptors, 'mutation', markAllowedCrossRealmGraphqlObjects, outputTypeCache, invokeResolver);
|
|
269
|
+
const subscriptionFields = pickFieldsByType(deps, resolverDescriptors, 'subscription', markAllowedCrossRealmGraphqlObjects, outputTypeCache, invokeResolver);
|
|
270
|
+
const queryType = createQueryRootType(deps, queryFields);
|
|
271
|
+
const mutationType = createOptionalRootType(deps, 'Mutation', mutationFields);
|
|
272
|
+
const subscriptionType = createOptionalRootType(deps, 'Subscription', subscriptionFields);
|
|
273
|
+
return new deps.GraphQLSchema({
|
|
274
|
+
mutation: mutationType,
|
|
275
|
+
query: queryType,
|
|
276
|
+
subscription: subscriptionType
|
|
277
|
+
});
|
|
278
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type HttpApplicationAdapter } from '@fluojs/http';
|
|
2
|
+
import type { Container } from '@fluojs/di';
|
|
3
|
+
import { type ApplicationLogger, type CompiledModule, type OnApplicationBootstrap, type OnApplicationShutdown } from '@fluojs/runtime';
|
|
4
|
+
import type { GraphqlModuleOptions } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Declares the HTTP endpoints that receive GraphQL GET and POST requests.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GraphqlEndpointController {
|
|
9
|
+
handleGet(): undefined;
|
|
10
|
+
handlePost(): undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Boots the GraphQL runtime, middleware, and subscription transports for the active adapter.
|
|
14
|
+
*/
|
|
15
|
+
export declare class GraphqlLifecycleService implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
16
|
+
private readonly runtimeContainer;
|
|
17
|
+
private readonly compiledModules;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
private readonly adapter;
|
|
20
|
+
private readonly options;
|
|
21
|
+
private graphQLErrorConstructor;
|
|
22
|
+
private middlewareRegistered;
|
|
23
|
+
private readonly operationContainers;
|
|
24
|
+
private readonly websocketOperationContainers;
|
|
25
|
+
private websocketDisposable;
|
|
26
|
+
private websocketServer;
|
|
27
|
+
private websocketUpgradeListener;
|
|
28
|
+
private websocketUpgradeServer;
|
|
29
|
+
private executeGraphqlOperation;
|
|
30
|
+
private subscribeGraphqlOperation;
|
|
31
|
+
private yoga;
|
|
32
|
+
private readonly middleware;
|
|
33
|
+
constructor(runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, adapter: HttpApplicationAdapter, options: GraphqlModuleOptions);
|
|
34
|
+
onApplicationBootstrap(): Promise<void>;
|
|
35
|
+
onApplicationShutdown(): Promise<void>;
|
|
36
|
+
private resolveGraphiqlEnabled;
|
|
37
|
+
private resolveIntrospectionEnabled;
|
|
38
|
+
private resolveWebSocketLimits;
|
|
39
|
+
private resolveSchema;
|
|
40
|
+
private createCodeFirstSchema;
|
|
41
|
+
private discoverResolverDescriptors;
|
|
42
|
+
private registerMiddleware;
|
|
43
|
+
private unregisterMiddleware;
|
|
44
|
+
private buildGraphqlContext;
|
|
45
|
+
private registerWebSocketTransport;
|
|
46
|
+
private unregisterWebSocketTransport;
|
|
47
|
+
private isWebSocketTransportEnabled;
|
|
48
|
+
private resolveUpgradeServer;
|
|
49
|
+
private handleWebSocketSubscribe;
|
|
50
|
+
private createWebSocketOperationLimitError;
|
|
51
|
+
private rejectWebSocketUpgrade;
|
|
52
|
+
private getOrCreateWebSocketOperationContainer;
|
|
53
|
+
private disposeWebSocketOperationContainer;
|
|
54
|
+
private disposeAllWebSocketOperationContainers;
|
|
55
|
+
private getOrCreateOperationContainer;
|
|
56
|
+
private disposeOperationContainer;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +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"}
|