@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
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { metadataSymbol } from '@fluojs/core/internal';
|
|
2
|
+
import { argMetadataSymbol, handlerMetadataSymbol, resolverMetadataSymbol } from './metadata.js';
|
|
3
|
+
function getStandardMetadataBag(metadata) {
|
|
4
|
+
void metadataSymbol;
|
|
5
|
+
return metadata;
|
|
6
|
+
}
|
|
7
|
+
function normalizeResolverTypeName(typeName, fallbackName) {
|
|
8
|
+
const trimmed = typeName?.trim();
|
|
9
|
+
if (trimmed) {
|
|
10
|
+
return trimmed;
|
|
11
|
+
}
|
|
12
|
+
return fallbackName;
|
|
13
|
+
}
|
|
14
|
+
function normalizeMethodMetadata(type, fieldNameOrOptions) {
|
|
15
|
+
if (typeof fieldNameOrOptions === 'string') {
|
|
16
|
+
return {
|
|
17
|
+
fieldName: fieldNameOrOptions.trim() || undefined,
|
|
18
|
+
type
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (!fieldNameOrOptions) {
|
|
22
|
+
return {
|
|
23
|
+
type
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
argTypes: fieldNameOrOptions.argTypes,
|
|
28
|
+
fieldName: fieldNameOrOptions.fieldName?.trim() || undefined,
|
|
29
|
+
inputClass: fieldNameOrOptions.input,
|
|
30
|
+
outputType: fieldNameOrOptions.outputType,
|
|
31
|
+
topics: fieldNameOrOptions.topics,
|
|
32
|
+
type
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function defineStandardResolverMetadata(metadata, resolverMetadata) {
|
|
36
|
+
const bag = getStandardMetadataBag(metadata);
|
|
37
|
+
bag[resolverMetadataSymbol] = {
|
|
38
|
+
typeName: resolverMetadata.typeName
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function defineStandardHandlerMetadata(metadata, propertyKey, handlerMetadata) {
|
|
42
|
+
const bag = getStandardMetadataBag(metadata);
|
|
43
|
+
const current = bag[handlerMetadataSymbol];
|
|
44
|
+
const map = current ?? new Map();
|
|
45
|
+
map.set(propertyKey, {
|
|
46
|
+
argTypes: handlerMetadata.argTypes,
|
|
47
|
+
fieldName: handlerMetadata.fieldName,
|
|
48
|
+
inputClass: handlerMetadata.inputClass,
|
|
49
|
+
outputType: handlerMetadata.outputType,
|
|
50
|
+
topics: handlerMetadata.topics,
|
|
51
|
+
type: handlerMetadata.type
|
|
52
|
+
});
|
|
53
|
+
bag[handlerMetadataSymbol] = map;
|
|
54
|
+
}
|
|
55
|
+
function defineStandardArgFieldMetadata(metadata, propertyKey, argFieldMetadata) {
|
|
56
|
+
const bag = getStandardMetadataBag(metadata);
|
|
57
|
+
const current = bag[argMetadataSymbol];
|
|
58
|
+
const map = current ?? new Map();
|
|
59
|
+
map.set(propertyKey, {
|
|
60
|
+
argName: argFieldMetadata.argName,
|
|
61
|
+
fieldName: argFieldMetadata.fieldName
|
|
62
|
+
});
|
|
63
|
+
bag[argMetadataSymbol] = map;
|
|
64
|
+
}
|
|
65
|
+
function createMethodDecorator(type, fieldNameOrOptions) {
|
|
66
|
+
const metadata = normalizeMethodMetadata(type, fieldNameOrOptions);
|
|
67
|
+
const decorator = (_value, context) => {
|
|
68
|
+
const name = type === 'query' ? 'Query' : type === 'mutation' ? 'Mutation' : 'Subscription';
|
|
69
|
+
if (context.private) {
|
|
70
|
+
throw new Error(`@${name}() cannot be used on private methods.`);
|
|
71
|
+
}
|
|
72
|
+
if (context.static) {
|
|
73
|
+
throw new Error(`@${name}() cannot be used on static methods.`);
|
|
74
|
+
}
|
|
75
|
+
defineStandardHandlerMetadata(context.metadata, context.name, metadata);
|
|
76
|
+
};
|
|
77
|
+
return decorator;
|
|
78
|
+
}
|
|
79
|
+
export function Resolver(typeName) {
|
|
80
|
+
const decorator = (value, context) => {
|
|
81
|
+
defineStandardResolverMetadata(context.metadata, {
|
|
82
|
+
typeName: normalizeResolverTypeName(typeName, value.name || 'Resolver')
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
return decorator;
|
|
86
|
+
}
|
|
87
|
+
export function Query(fieldNameOrOptions) {
|
|
88
|
+
return createMethodDecorator('query', fieldNameOrOptions);
|
|
89
|
+
}
|
|
90
|
+
export function Mutation(fieldNameOrOptions) {
|
|
91
|
+
return createMethodDecorator('mutation', fieldNameOrOptions);
|
|
92
|
+
}
|
|
93
|
+
export function Subscription(fieldNameOrOptions) {
|
|
94
|
+
return createMethodDecorator('subscription', fieldNameOrOptions);
|
|
95
|
+
}
|
|
96
|
+
export function Arg(argName) {
|
|
97
|
+
const decorator = (_value, context) => {
|
|
98
|
+
if (context.private) {
|
|
99
|
+
throw new Error('@Arg() cannot be used on private fields.');
|
|
100
|
+
}
|
|
101
|
+
if (context.static) {
|
|
102
|
+
throw new Error('@Arg() cannot be used on static fields.');
|
|
103
|
+
}
|
|
104
|
+
const fieldName = typeof context.name === 'symbol' ? context.name.toString() : context.name;
|
|
105
|
+
defineStandardArgFieldMetadata(context.metadata, context.name, {
|
|
106
|
+
argName: argName?.trim() || fieldName,
|
|
107
|
+
fieldName
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
return decorator;
|
|
111
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CompiledModule } from '@fluojs/runtime';
|
|
2
|
+
import type { GraphqlModuleOptions, ResolverDescriptor } from './types.js';
|
|
3
|
+
export declare function discoverResolverDescriptors(compiledModules: readonly CompiledModule[], options: GraphqlModuleOptions): ResolverDescriptor[];
|
|
4
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkI3E,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,EAAE,CA+CtB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { getClassDiMetadata } from '@fluojs/core/internal';
|
|
2
|
+
import { getArgFieldMetadataEntries, getResolverHandlerMetadataEntries, getResolverMetadata } from './metadata.js';
|
|
3
|
+
function scopeFromProvider(provider, factoryResolverClass) {
|
|
4
|
+
if (typeof provider === 'function') {
|
|
5
|
+
return getClassDiMetadata(provider)?.scope ?? 'singleton';
|
|
6
|
+
}
|
|
7
|
+
if ('useClass' in provider) {
|
|
8
|
+
return provider.scope ?? getClassDiMetadata(provider.useClass)?.scope ?? 'singleton';
|
|
9
|
+
}
|
|
10
|
+
if ('useFactory' in provider) {
|
|
11
|
+
return provider.scope ?? (factoryResolverClass ? getClassDiMetadata(factoryResolverClass)?.scope : undefined) ?? 'singleton';
|
|
12
|
+
}
|
|
13
|
+
return 'singleton';
|
|
14
|
+
}
|
|
15
|
+
function isClassProvider(provider) {
|
|
16
|
+
return typeof provider === 'object' && provider !== null && 'useClass' in provider;
|
|
17
|
+
}
|
|
18
|
+
function isValueProvider(provider) {
|
|
19
|
+
return typeof provider === 'object' && provider !== null && 'useValue' in provider;
|
|
20
|
+
}
|
|
21
|
+
function isFactoryProvider(provider) {
|
|
22
|
+
return typeof provider === 'object' && provider !== null && 'useFactory' in provider;
|
|
23
|
+
}
|
|
24
|
+
function methodKeyToName(methodKey) {
|
|
25
|
+
return typeof methodKey === 'symbol' ? methodKey.toString() : methodKey;
|
|
26
|
+
}
|
|
27
|
+
function normalizeAllowedResolverSet(resolvers) {
|
|
28
|
+
if (!resolvers || resolvers.length === 0) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return new Set(resolvers);
|
|
32
|
+
}
|
|
33
|
+
function discoveryCandidates(compiledModules) {
|
|
34
|
+
const candidates = [];
|
|
35
|
+
for (const compiledModule of compiledModules) {
|
|
36
|
+
for (const provider of compiledModule.definition.providers ?? []) {
|
|
37
|
+
if (typeof provider === 'function') {
|
|
38
|
+
candidates.push({
|
|
39
|
+
moduleName: compiledModule.type.name,
|
|
40
|
+
scope: scopeFromProvider(provider),
|
|
41
|
+
targetType: provider,
|
|
42
|
+
token: provider
|
|
43
|
+
});
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isClassProvider(provider)) {
|
|
47
|
+
candidates.push({
|
|
48
|
+
moduleName: compiledModule.type.name,
|
|
49
|
+
scope: scopeFromProvider(provider),
|
|
50
|
+
targetType: provider.useClass,
|
|
51
|
+
token: provider.provide
|
|
52
|
+
});
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (isValueProvider(provider)) {
|
|
56
|
+
const value = provider.useValue;
|
|
57
|
+
if (typeof value === 'function') {
|
|
58
|
+
candidates.push({
|
|
59
|
+
moduleName: compiledModule.type.name,
|
|
60
|
+
scope: 'singleton',
|
|
61
|
+
targetType: value,
|
|
62
|
+
token: provider.provide
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'object' && value !== null) {
|
|
67
|
+
const constructor = value.constructor;
|
|
68
|
+
if (constructor && constructor !== Object) {
|
|
69
|
+
candidates.push({
|
|
70
|
+
moduleName: compiledModule.type.name,
|
|
71
|
+
scope: 'singleton',
|
|
72
|
+
targetType: constructor,
|
|
73
|
+
token: provider.provide
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (isFactoryProvider(provider)) {
|
|
80
|
+
const resolverClass = provider.resolverClass;
|
|
81
|
+
if (resolverClass) {
|
|
82
|
+
candidates.push({
|
|
83
|
+
moduleName: compiledModule.type.name,
|
|
84
|
+
scope: scopeFromProvider(provider, resolverClass),
|
|
85
|
+
targetType: resolverClass,
|
|
86
|
+
token: provider.provide
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const controller of compiledModule.definition.controllers ?? []) {
|
|
92
|
+
candidates.push({
|
|
93
|
+
moduleName: compiledModule.type.name,
|
|
94
|
+
scope: scopeFromProvider(controller),
|
|
95
|
+
targetType: controller,
|
|
96
|
+
token: controller
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return candidates;
|
|
101
|
+
}
|
|
102
|
+
export function discoverResolverDescriptors(compiledModules, options) {
|
|
103
|
+
const allowedResolvers = normalizeAllowedResolverSet(options.resolvers);
|
|
104
|
+
const seenTargets = new Set();
|
|
105
|
+
const descriptors = [];
|
|
106
|
+
for (const candidate of discoveryCandidates(compiledModules)) {
|
|
107
|
+
if (allowedResolvers && !allowedResolvers.has(candidate.targetType)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const resolverMetadata = getResolverMetadata(candidate.targetType);
|
|
111
|
+
if (!resolverMetadata) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (seenTargets.has(candidate.targetType)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
seenTargets.add(candidate.targetType);
|
|
118
|
+
descriptors.push({
|
|
119
|
+
handlers: getResolverHandlerMetadataEntries(candidate.targetType.prototype).map(entry => {
|
|
120
|
+
const inputClass = entry.metadata.inputClass;
|
|
121
|
+
const argFields = inputClass !== undefined ? getArgFieldMetadataEntries(inputClass.prototype).map(argField => argField.metadata) : [];
|
|
122
|
+
return {
|
|
123
|
+
argFields,
|
|
124
|
+
argTypes: entry.metadata.argTypes,
|
|
125
|
+
fieldName: entry.metadata.fieldName ?? methodKeyToName(entry.propertyKey),
|
|
126
|
+
inputClass,
|
|
127
|
+
methodKey: entry.propertyKey,
|
|
128
|
+
methodName: methodKeyToName(entry.propertyKey),
|
|
129
|
+
outputType: entry.metadata.outputType,
|
|
130
|
+
topics: entry.metadata.topics,
|
|
131
|
+
type: entry.metadata.type
|
|
132
|
+
};
|
|
133
|
+
}),
|
|
134
|
+
moduleName: candidate.moduleName,
|
|
135
|
+
scope: candidate.scope,
|
|
136
|
+
targetName: candidate.targetType.name,
|
|
137
|
+
token: candidate.token,
|
|
138
|
+
typeName: resolverMetadata.typeName
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return descriptors;
|
|
142
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { GraphQLError, type ExecutionArgs, type ValidationRule } from 'graphql';
|
|
2
|
+
import type { GraphqlRequestLimitsOptions } from './types.js';
|
|
3
|
+
interface GraphqlValidationPluginContext {
|
|
4
|
+
addValidationRule(rule: ValidationRule): void;
|
|
5
|
+
}
|
|
6
|
+
interface GraphqlValidationPlugin {
|
|
7
|
+
onExecute?(payload: GraphqlOperationHookPayload): void;
|
|
8
|
+
onSubscribe?(payload: GraphqlOperationHookPayload): void;
|
|
9
|
+
onValidate?(context: GraphqlValidationPluginContext): void;
|
|
10
|
+
}
|
|
11
|
+
interface GraphqlOperationHookPayload {
|
|
12
|
+
args: Pick<ExecutionArgs, 'document' | 'operationName'>;
|
|
13
|
+
setResultAndStopExecution(result: {
|
|
14
|
+
errors: GraphQLError[];
|
|
15
|
+
}): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the effective GraphQL request budget configuration for one module instance.
|
|
19
|
+
*
|
|
20
|
+
* @param limits User-provided request limit overrides, or `false` to disable built-in budgets.
|
|
21
|
+
* @returns The normalized request limits, or `undefined` when guardrails are explicitly disabled.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveGraphqlRequestLimits(limits: GraphqlRequestLimitsOptions | false | undefined): Required<GraphqlRequestLimitsOptions> | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Create the GraphQL plugin that enforces introspection and request-budget guardrails.
|
|
26
|
+
*
|
|
27
|
+
* @param options Effective guardrail settings for introspection and per-request limits.
|
|
28
|
+
* @returns An Envelop-compatible plugin when at least one guardrail is enabled.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createGraphqlValidationPlugin(options: {
|
|
31
|
+
introspection: boolean;
|
|
32
|
+
limits: Required<GraphqlRequestLimitsOptions> | undefined;
|
|
33
|
+
}): GraphqlValidationPlugin | undefined;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../src/guardrails.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAUlB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAQ9D,UAAU,8BAA8B;IACtC,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC/C;AAED,UAAU,uBAAuB;IAC/B,SAAS,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACvD,WAAW,CAAC,CAAC,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACzD,UAAU,CAAC,CAAC,OAAO,EAAE,8BAA8B,GAAG,IAAI,CAAC;CAC5D;AAED,UAAU,2BAA2B;IACnC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,GAAG,eAAe,CAAC,CAAC;IACxD,yBAAyB,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,YAAY,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CACrE;AAcD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,GAAG,KAAK,GAAG,SAAS,GACtD,QAAQ,CAAC,2BAA2B,CAAC,GAAG,SAAS,CAUnD;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE;IACrD,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC,2BAA2B,CAAC,GAAG,SAAS,CAAC;CAC3D,GAAG,uBAAuB,GAAG,SAAS,CAkCtC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { GraphQLError, Kind } from 'graphql';
|
|
2
|
+
const DEFAULT_GRAPHQL_REQUEST_LIMITS = {
|
|
3
|
+
maxComplexity: 250,
|
|
4
|
+
maxCost: 500,
|
|
5
|
+
maxDepth: 12
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the effective GraphQL request budget configuration for one module instance.
|
|
9
|
+
*
|
|
10
|
+
* @param limits User-provided request limit overrides, or `false` to disable built-in budgets.
|
|
11
|
+
* @returns The normalized request limits, or `undefined` when guardrails are explicitly disabled.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveGraphqlRequestLimits(limits) {
|
|
14
|
+
if (limits === false) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
maxComplexity: limits?.maxComplexity ?? DEFAULT_GRAPHQL_REQUEST_LIMITS.maxComplexity,
|
|
19
|
+
maxCost: limits?.maxCost ?? DEFAULT_GRAPHQL_REQUEST_LIMITS.maxCost,
|
|
20
|
+
maxDepth: limits?.maxDepth ?? DEFAULT_GRAPHQL_REQUEST_LIMITS.maxDepth
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create the GraphQL plugin that enforces introspection and request-budget guardrails.
|
|
26
|
+
*
|
|
27
|
+
* @param options Effective guardrail settings for introspection and per-request limits.
|
|
28
|
+
* @returns An Envelop-compatible plugin when at least one guardrail is enabled.
|
|
29
|
+
*/
|
|
30
|
+
export function createGraphqlValidationPlugin(options) {
|
|
31
|
+
const validationRules = [];
|
|
32
|
+
if (!options.introspection) {
|
|
33
|
+
validationRules.push(createDisableIntrospectionRule());
|
|
34
|
+
}
|
|
35
|
+
if (validationRules.length === 0 && !options.limits) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const enforceRequestLimits = options.limits ? payload => {
|
|
39
|
+
const errors = evaluateGraphqlRequestLimits(payload.args.document, payload.args.operationName, options.limits);
|
|
40
|
+
if (errors.length > 0) {
|
|
41
|
+
payload.setResultAndStopExecution({
|
|
42
|
+
errors
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
} : undefined;
|
|
46
|
+
return {
|
|
47
|
+
...(enforceRequestLimits ? {
|
|
48
|
+
onExecute: enforceRequestLimits,
|
|
49
|
+
onSubscribe: enforceRequestLimits
|
|
50
|
+
} : {}),
|
|
51
|
+
onValidate({
|
|
52
|
+
addValidationRule
|
|
53
|
+
}) {
|
|
54
|
+
for (const rule of validationRules) {
|
|
55
|
+
addValidationRule(rule);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createDisableIntrospectionRule() {
|
|
61
|
+
return context => ({
|
|
62
|
+
Field(node) {
|
|
63
|
+
const fieldName = node.name.value;
|
|
64
|
+
if (fieldName === '__schema' || fieldName === '__type') {
|
|
65
|
+
context.reportError(new GraphQLError(`GraphQL introspection is disabled, but the query requested "${fieldName}".`));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function evaluateGraphqlRequestLimits(document, operationName, limits) {
|
|
71
|
+
const metrics = analyzeGraphqlOperation(document, operationName);
|
|
72
|
+
if (!metrics) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const checks = [{
|
|
76
|
+
actual: metrics.maxDepth,
|
|
77
|
+
label: 'depth',
|
|
78
|
+
limit: limits.maxDepth
|
|
79
|
+
}, {
|
|
80
|
+
actual: metrics.complexity,
|
|
81
|
+
label: 'complexity',
|
|
82
|
+
limit: limits.maxComplexity
|
|
83
|
+
}, {
|
|
84
|
+
actual: metrics.cost,
|
|
85
|
+
label: 'cost',
|
|
86
|
+
limit: limits.maxCost
|
|
87
|
+
}];
|
|
88
|
+
return checks.flatMap(check => {
|
|
89
|
+
if (check.actual <= check.limit) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return [new GraphQLError(`GraphQL query ${check.label} ${String(check.actual)} exceeds the configured limit of ${String(check.limit)}.`)];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function analyzeGraphqlOperation(document, operationName) {
|
|
96
|
+
const fragments = new Map();
|
|
97
|
+
const operations = [];
|
|
98
|
+
for (const definition of document.definitions) {
|
|
99
|
+
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
100
|
+
fragments.set(definition.name.value, definition);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (definition.kind === Kind.OPERATION_DEFINITION) {
|
|
104
|
+
operations.push(definition);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const operation = resolveOperationDefinition(operations, operationName);
|
|
108
|
+
if (!operation) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const metrics = {
|
|
112
|
+
complexity: 0,
|
|
113
|
+
cost: 0,
|
|
114
|
+
maxDepth: 0
|
|
115
|
+
};
|
|
116
|
+
const walkSelectionSet = (selectionSet, depth, activeFragments) => {
|
|
117
|
+
for (const selection of selectionSet.selections) {
|
|
118
|
+
if (selection.kind === Kind.FIELD) {
|
|
119
|
+
const nextDepth = depth + 1;
|
|
120
|
+
metrics.maxDepth = Math.max(metrics.maxDepth, nextDepth);
|
|
121
|
+
metrics.complexity += 1;
|
|
122
|
+
metrics.cost += nextDepth;
|
|
123
|
+
if (selection.selectionSet) {
|
|
124
|
+
walkSelectionSet(selection.selectionSet, nextDepth, activeFragments);
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
129
|
+
walkInlineFragment(selection, depth, activeFragments);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (selection.kind !== Kind.FRAGMENT_SPREAD) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
walkFragmentSpread(selection, depth, activeFragments);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const walkInlineFragment = (fragment, depth, activeFragments) => {
|
|
139
|
+
walkSelectionSet(fragment.selectionSet, depth, activeFragments);
|
|
140
|
+
};
|
|
141
|
+
const walkFragmentSpread = (fragmentSpread, depth, activeFragments) => {
|
|
142
|
+
const fragmentName = fragmentSpread.name.value;
|
|
143
|
+
if (activeFragments.has(fragmentName)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const fragment = fragments.get(fragmentName);
|
|
147
|
+
if (!fragment) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
activeFragments.add(fragmentName);
|
|
151
|
+
walkSelectionSet(fragment.selectionSet, depth, activeFragments);
|
|
152
|
+
activeFragments.delete(fragmentName);
|
|
153
|
+
};
|
|
154
|
+
walkSelectionSet(operation.selectionSet, 0, new Set());
|
|
155
|
+
return metrics;
|
|
156
|
+
}
|
|
157
|
+
function resolveOperationDefinition(operations, operationName) {
|
|
158
|
+
if (operationName) {
|
|
159
|
+
return operations.find(operation => operation.name?.value === operationName);
|
|
160
|
+
}
|
|
161
|
+
return operations.length === 1 ? operations[0] : undefined;
|
|
162
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './dataloader.js';
|
|
2
|
+
export * from './decorators.js';
|
|
3
|
+
export { GraphqlModule } from './module.js';
|
|
4
|
+
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
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,OAAO,EACL,oBAAoB,EACpB,MAAM,GACP,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,EACrB,qBAAqB,EACrB,2BAA2B,EAC3B,oCAAoC,EACpC,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Token } from '@fluojs/core';
|
|
2
|
+
import type { GraphqlModuleOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* DI token for providing GraphQL module configuration options.
|
|
5
|
+
*/
|
|
6
|
+
export declare const GRAPHQL_INTERNAL_MODULE_OPTIONS_TOKEN: Token<GraphqlModuleOptions>;
|
|
7
|
+
//# sourceMappingURL=internal-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-tokens.d.ts","sourceRoot":"","sources":["../src/internal-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,qCAAqC,EAAE,KAAK,CAAC,oBAAoB,CAE7E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MetadataPropertyKey } from '@fluojs/core';
|
|
2
|
+
import type { ArgFieldMetadata, ResolverHandlerMetadata, ResolverMetadata } from './types.js';
|
|
3
|
+
export declare function defineResolverMetadata(target: object, metadata: ResolverMetadata): void;
|
|
4
|
+
export declare function getResolverMetadata(target: object): ResolverMetadata | undefined;
|
|
5
|
+
export declare function defineResolverHandlerMetadata(target: object, propertyKey: MetadataPropertyKey, metadata: ResolverHandlerMetadata): void;
|
|
6
|
+
export declare function getResolverHandlerMetadata(target: object, propertyKey: MetadataPropertyKey): ResolverHandlerMetadata | undefined;
|
|
7
|
+
export declare function getResolverHandlerMetadataEntries(target: object): Array<{
|
|
8
|
+
metadata: ResolverHandlerMetadata;
|
|
9
|
+
propertyKey: MetadataPropertyKey;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function defineArgFieldMetadata(target: object, propertyKey: MetadataPropertyKey, metadata: ArgFieldMetadata): void;
|
|
12
|
+
export declare function getArgFieldMetadata(target: object, propertyKey: MetadataPropertyKey): ArgFieldMetadata | undefined;
|
|
13
|
+
export declare function getArgFieldMetadataEntries(target: object): Array<{
|
|
14
|
+
metadata: ArgFieldMetadata;
|
|
15
|
+
propertyKey: MetadataPropertyKey;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const resolverMetadataSymbol: symbol;
|
|
18
|
+
export declare const handlerMetadataSymbol: symbol;
|
|
19
|
+
export declare const argMetadataSymbol: symbol;
|
|
20
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAyH9F,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEvF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAShF;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,uBAAuB,GAChC,IAAI,CAEN;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,uBAAuB,GAAG,SAAS,CASrC;AAED,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,uBAAuB,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAOhF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAEzH;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,SAAS,CASlH;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAEzE;AAED,eAAO,MAAM,sBAAsB,QAA8B,CAAC;AAClE,eAAO,MAAM,qBAAqB,QAA6B,CAAC;AAChE,eAAO,MAAM,iBAAiB,QAA8B,CAAC"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const symbolWithMetadata = Symbol;
|
|
2
|
+
const metadataSymbol = symbolWithMetadata.metadata ?? Symbol.for('fluo.symbol.metadata');
|
|
3
|
+
if (!symbolWithMetadata.metadata) {
|
|
4
|
+
Object.defineProperty(Symbol, 'metadata', {
|
|
5
|
+
configurable: true,
|
|
6
|
+
value: metadataSymbol
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
const standardResolverMetadataKey = Symbol.for('fluo.graphql.standard.resolver');
|
|
10
|
+
const standardHandlerMetadataKey = Symbol.for('fluo.graphql.standard.handler');
|
|
11
|
+
const standardArgFieldMetadataKey = Symbol.for('fluo.graphql.standard.arg-field');
|
|
12
|
+
const resolverMetadataStore = new WeakMap();
|
|
13
|
+
const handlerMetadataStore = new WeakMap();
|
|
14
|
+
const argFieldMetadataStore = new WeakMap();
|
|
15
|
+
function cloneResolverMetadata(metadata) {
|
|
16
|
+
return {
|
|
17
|
+
typeName: metadata.typeName
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function cloneHandlerMetadata(metadata) {
|
|
21
|
+
return {
|
|
22
|
+
argTypes: metadata.argTypes,
|
|
23
|
+
fieldName: metadata.fieldName,
|
|
24
|
+
inputClass: metadata.inputClass,
|
|
25
|
+
outputType: metadata.outputType,
|
|
26
|
+
topics: metadata.topics,
|
|
27
|
+
type: metadata.type
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function cloneArgFieldMetadata(metadata) {
|
|
31
|
+
return {
|
|
32
|
+
argName: metadata.argName,
|
|
33
|
+
fieldName: metadata.fieldName
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function getStandardMetadataBag(target) {
|
|
37
|
+
return target[metadataSymbol];
|
|
38
|
+
}
|
|
39
|
+
function getStandardResolverMetadata(target) {
|
|
40
|
+
const metadata = getStandardMetadataBag(target)?.[standardResolverMetadataKey];
|
|
41
|
+
if (!metadata) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return cloneResolverMetadata(metadata);
|
|
45
|
+
}
|
|
46
|
+
function getStandardHandlerMap(target) {
|
|
47
|
+
const constructor = target.constructor;
|
|
48
|
+
return constructor ? getStandardMetadataBag(constructor)?.[standardHandlerMetadataKey] : undefined;
|
|
49
|
+
}
|
|
50
|
+
function getStandardArgFieldMap(target) {
|
|
51
|
+
const constructor = target.constructor;
|
|
52
|
+
return constructor ? getStandardMetadataBag(constructor)?.[standardArgFieldMetadataKey] : undefined;
|
|
53
|
+
}
|
|
54
|
+
function getOrCreateHandlerMetadataMap(target) {
|
|
55
|
+
let map = handlerMetadataStore.get(target);
|
|
56
|
+
if (!map) {
|
|
57
|
+
map = new Map();
|
|
58
|
+
handlerMetadataStore.set(target, map);
|
|
59
|
+
}
|
|
60
|
+
return map;
|
|
61
|
+
}
|
|
62
|
+
function getOrCreateArgFieldMetadataMap(target) {
|
|
63
|
+
let map = argFieldMetadataStore.get(target);
|
|
64
|
+
if (!map) {
|
|
65
|
+
map = new Map();
|
|
66
|
+
argFieldMetadataStore.set(target, map);
|
|
67
|
+
}
|
|
68
|
+
return map;
|
|
69
|
+
}
|
|
70
|
+
function getMergedMetadataEntries(target, stored, standard, resolve) {
|
|
71
|
+
const storedMap = stored ?? new Map();
|
|
72
|
+
const standardMap = standard ?? new Map();
|
|
73
|
+
const keys = new Set([...storedMap.keys(), ...standardMap.keys()]);
|
|
74
|
+
return Array.from(keys).map(propertyKey => ({
|
|
75
|
+
metadata: resolve(target, propertyKey),
|
|
76
|
+
propertyKey
|
|
77
|
+
})).filter(entry => entry.metadata !== undefined);
|
|
78
|
+
}
|
|
79
|
+
export function defineResolverMetadata(target, metadata) {
|
|
80
|
+
resolverMetadataStore.set(target, cloneResolverMetadata(metadata));
|
|
81
|
+
}
|
|
82
|
+
export function getResolverMetadata(target) {
|
|
83
|
+
const stored = resolverMetadataStore.get(target);
|
|
84
|
+
const standard = getStandardResolverMetadata(target);
|
|
85
|
+
if (!stored && !standard) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return cloneResolverMetadata(stored ?? standard);
|
|
89
|
+
}
|
|
90
|
+
export function defineResolverHandlerMetadata(target, propertyKey, metadata) {
|
|
91
|
+
getOrCreateHandlerMetadataMap(target).set(propertyKey, cloneHandlerMetadata(metadata));
|
|
92
|
+
}
|
|
93
|
+
export function getResolverHandlerMetadata(target, propertyKey) {
|
|
94
|
+
const stored = handlerMetadataStore.get(target)?.get(propertyKey);
|
|
95
|
+
const standard = getStandardHandlerMap(target)?.get(propertyKey);
|
|
96
|
+
if (!stored && !standard) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return cloneHandlerMetadata(stored ?? standard);
|
|
100
|
+
}
|
|
101
|
+
export function getResolverHandlerMetadataEntries(target) {
|
|
102
|
+
return getMergedMetadataEntries(target, handlerMetadataStore.get(target), getStandardHandlerMap(target), getResolverHandlerMetadata);
|
|
103
|
+
}
|
|
104
|
+
export function defineArgFieldMetadata(target, propertyKey, metadata) {
|
|
105
|
+
getOrCreateArgFieldMetadataMap(target).set(propertyKey, cloneArgFieldMetadata(metadata));
|
|
106
|
+
}
|
|
107
|
+
export function getArgFieldMetadata(target, propertyKey) {
|
|
108
|
+
const stored = argFieldMetadataStore.get(target)?.get(propertyKey);
|
|
109
|
+
const standard = getStandardArgFieldMap(target)?.get(propertyKey);
|
|
110
|
+
if (!stored && !standard) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return cloneArgFieldMetadata(stored ?? standard);
|
|
114
|
+
}
|
|
115
|
+
export function getArgFieldMetadataEntries(target) {
|
|
116
|
+
return getMergedMetadataEntries(target, argFieldMetadataStore.get(target), getStandardArgFieldMap(target), getArgFieldMetadata);
|
|
117
|
+
}
|
|
118
|
+
export const resolverMetadataSymbol = standardResolverMetadataKey;
|
|
119
|
+
export const handlerMetadataSymbol = standardHandlerMetadataKey;
|
|
120
|
+
export const argMetadataSymbol = standardArgFieldMetadataKey;
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Provider } from '@fluojs/di';
|
|
2
|
+
import { type ModuleType } from '@fluojs/runtime';
|
|
3
|
+
import type { GraphqlModuleOptions } from './types.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 declare function createGraphqlProviders(options: GraphqlModuleOptions): Provider[];
|
|
12
|
+
export declare class GraphqlModule {
|
|
13
|
+
/**
|
|
14
|
+
* Registers the GraphQL endpoint controller together with lifecycle providers.
|
|
15
|
+
*
|
|
16
|
+
* @param options Optional GraphQL module options for schema, resolver discovery, context, and plugins.
|
|
17
|
+
* @returns A module definition that wires GraphQL runtime behavior and mounts the GraphQL endpoint controller; use this
|
|
18
|
+
* module path (not `createGraphqlProviders(...)` alone) when the application should expose `/graphql`.
|
|
19
|
+
*/
|
|
20
|
+
static forRoot(options?: GraphqlModuleOptions): ModuleType;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,QAAQ,EAAE,CAQhF;AAED,qBAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,oBAAyB,GAAG,UAAU;CAS/D"}
|