@gqlkit-ts/cli 0.1.0 → 0.2.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/dist/auto-type-generator/auto-type-generator.d.ts +19 -3
- package/dist/auto-type-generator/auto-type-generator.d.ts.map +1 -1
- package/dist/auto-type-generator/auto-type-generator.js +236 -49
- package/dist/auto-type-generator/auto-type-generator.js.map +1 -1
- package/dist/auto-type-generator/index.d.ts +1 -1
- package/dist/auto-type-generator/index.d.ts.map +1 -1
- package/dist/auto-type-generator/index.js.map +1 -1
- package/dist/auto-type-generator/inline-enum-collector.d.ts +31 -0
- package/dist/auto-type-generator/inline-enum-collector.d.ts.map +1 -0
- package/dist/auto-type-generator/inline-enum-collector.js +157 -0
- package/dist/auto-type-generator/inline-enum-collector.js.map +1 -0
- package/dist/auto-type-generator/naming-convention.d.ts +4 -0
- package/dist/auto-type-generator/naming-convention.d.ts.map +1 -1
- package/dist/auto-type-generator/naming-convention.js +8 -0
- package/dist/auto-type-generator/naming-convention.js.map +1 -1
- package/dist/gen-orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/gen-orchestrator/orchestrator.js +106 -8
- package/dist/gen-orchestrator/orchestrator.js.map +1 -1
- package/dist/resolver-extractor/extract-resolvers.d.ts +10 -1
- package/dist/resolver-extractor/extract-resolvers.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts +11 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.d.ts.map +1 -1
- package/dist/resolver-extractor/extractor/define-api-extractor.js +88 -264
- package/dist/resolver-extractor/extractor/define-api-extractor.js.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.d.ts +10 -2
- package/dist/schema-generator/emitter/code-emitter.d.ts.map +1 -1
- package/dist/schema-generator/emitter/code-emitter.js +19 -4
- package/dist/schema-generator/emitter/code-emitter.js.map +1 -1
- package/dist/schema-generator/generate-schema.d.ts.map +1 -1
- package/dist/schema-generator/generate-schema.js +18 -1
- package/dist/schema-generator/generate-schema.js.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.d.ts +20 -0
- package/dist/schema-generator/integrator/result-integrator.d.ts.map +1 -1
- package/dist/schema-generator/integrator/result-integrator.js +42 -0
- package/dist/schema-generator/integrator/result-integrator.js.map +1 -1
- package/dist/shared/constants.d.ts +0 -16
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +0 -19
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/directive-detector.d.ts.map +1 -1
- package/dist/shared/directive-detector.js +8 -15
- package/dist/shared/directive-detector.js.map +1 -1
- package/dist/shared/file-scanner.d.ts.map +1 -1
- package/dist/shared/file-scanner.js +5 -3
- package/dist/shared/file-scanner.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -3
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/interface-detector.d.ts +3 -2
- package/dist/shared/interface-detector.d.ts.map +1 -1
- package/dist/shared/interface-detector.js +54 -11
- package/dist/shared/interface-detector.js.map +1 -1
- package/dist/shared/path-utils.d.ts +2 -0
- package/dist/shared/path-utils.d.ts.map +1 -0
- package/dist/shared/path-utils.js +4 -0
- package/dist/shared/path-utils.js.map +1 -0
- package/dist/shared/type-converter.d.ts.map +1 -1
- package/dist/shared/type-converter.js +11 -0
- package/dist/shared/type-converter.js.map +1 -1
- package/dist/shared/typescript-utils.d.ts +34 -7
- package/dist/shared/typescript-utils.d.ts.map +1 -1
- package/dist/shared/typescript-utils.js +72 -24
- package/dist/shared/typescript-utils.js.map +1 -1
- package/dist/type-extractor/collector/scalar-collector.d.ts.map +1 -1
- package/dist/type-extractor/collector/scalar-collector.js +4 -14
- package/dist/type-extractor/collector/scalar-collector.js.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.d.ts.map +1 -1
- package/dist/type-extractor/converter/graphql-converter.js +42 -3
- package/dist/type-extractor/converter/graphql-converter.js.map +1 -1
- package/dist/type-extractor/extractor/field-type-resolver.d.ts +28 -0
- package/dist/type-extractor/extractor/field-type-resolver.d.ts.map +1 -0
- package/dist/type-extractor/extractor/field-type-resolver.js +394 -0
- package/dist/type-extractor/extractor/field-type-resolver.js.map +1 -0
- package/dist/type-extractor/extractor/type-extractor.d.ts +12 -3
- package/dist/type-extractor/extractor/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor/extractor/type-extractor.js +176 -210
- package/dist/type-extractor/extractor/type-extractor.js.map +1 -1
- package/dist/type-extractor/extractor/type-name-collector.d.ts +24 -0
- package/dist/type-extractor/extractor/type-name-collector.d.ts.map +1 -0
- package/dist/type-extractor/extractor/type-name-collector.js +102 -0
- package/dist/type-extractor/extractor/type-name-collector.js.map +1 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts +89 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.d.ts.map +1 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.js +158 -0
- package/dist/type-extractor/mapper/scalar-base-type-mapper.js.map +1 -0
- package/dist/type-extractor/types/diagnostics.d.ts +1 -1
- package/dist/type-extractor/types/diagnostics.d.ts.map +1 -1
- package/dist/type-extractor/types/graphql.d.ts +2 -0
- package/dist/type-extractor/types/graphql.d.ts.map +1 -1
- package/dist/type-extractor/types/index.d.ts +2 -1
- package/dist/type-extractor/types/index.d.ts.map +1 -1
- package/dist/type-extractor/types/index.js +1 -1
- package/dist/type-extractor/types/index.js.map +1 -1
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts +39 -0
- package/dist/type-extractor/types/ts-type-reference-factory.d.ts.map +1 -0
- package/dist/type-extractor/types/ts-type-reference-factory.js +69 -0
- package/dist/type-extractor/types/ts-type-reference-factory.js.map +1 -0
- package/dist/type-extractor/types/typescript.d.ts +21 -1
- package/dist/type-extractor/types/typescript.d.ts.map +1 -1
- package/dist/type-extractor/validator/type-validator.js +1 -1
- package/dist/type-extractor/validator/type-validator.js.map +1 -1
- package/docs/index.md +1 -0
- package/docs/integration/drizzle.md +191 -0
- package/docs/schema/enums.md +208 -0
- package/docs/schema/fields.md +2 -0
- package/docs/schema/inputs.md +2 -0
- package/docs/schema/objects.md +2 -0
- package/docs/schema/queries-mutations.md +2 -0
- package/package.json +10 -3
- package/src/auto-type-generator/auto-type-generator.ts +925 -0
- package/src/auto-type-generator/index.ts +21 -0
- package/src/auto-type-generator/inline-enum-collector.ts +290 -0
- package/src/auto-type-generator/name-collision-validator.ts +119 -0
- package/src/auto-type-generator/naming-convention.ts +126 -0
- package/src/cli.ts +11 -0
- package/src/commands/gen.ts +141 -0
- package/src/commands/main.ts +5 -0
- package/src/config/define-config.ts +28 -0
- package/src/config/index.ts +7 -0
- package/src/config/types.ts +144 -0
- package/src/config-loader/index.ts +14 -0
- package/src/config-loader/loader.ts +143 -0
- package/src/config-loader/validator.ts +672 -0
- package/src/gen-orchestrator/hook-executor/hook-executor.ts +117 -0
- package/src/gen-orchestrator/orchestrator.ts +784 -0
- package/src/gen-orchestrator/reporter/diagnostic-reporter.ts +44 -0
- package/src/gen-orchestrator/reporter/progress-reporter.ts +61 -0
- package/src/gen-orchestrator/writer/file-writer.ts +38 -0
- package/src/index.ts +2 -0
- package/src/resolver-extractor/extract-resolvers.ts +63 -0
- package/src/resolver-extractor/extractor/define-api-extractor.ts +806 -0
- package/src/resolver-extractor/index.ts +19 -0
- package/src/resolver-extractor/validator/abstract-resolver-validator.ts +251 -0
- package/src/resolver-extractor/validator/only-validator.ts +158 -0
- package/src/schema-generator/builder/ast-builder.ts +706 -0
- package/src/schema-generator/emitter/code-emitter.ts +351 -0
- package/src/schema-generator/emitter/sdl-emitter.ts +13 -0
- package/src/schema-generator/generate-schema.ts +170 -0
- package/src/schema-generator/index.ts +19 -0
- package/src/schema-generator/integrator/result-integrator.ts +690 -0
- package/src/schema-generator/pruner/schema-pruner.ts +112 -0
- package/src/schema-generator/resolver-collector/resolver-collector.ts +123 -0
- package/src/shared/constants.ts +122 -0
- package/src/shared/default-value-detector.ts +172 -0
- package/src/shared/diagnostics.ts +35 -0
- package/src/shared/directive-definition-extractor.ts +564 -0
- package/src/shared/directive-detector.ts +556 -0
- package/src/shared/file-scanner.ts +170 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/inline-object-extractor.ts +102 -0
- package/src/shared/inline-object-utils.ts +23 -0
- package/src/shared/interface-detector.ts +176 -0
- package/src/shared/interface-validator.ts +211 -0
- package/src/shared/metadata-detector.ts +443 -0
- package/src/shared/path-utils.ts +3 -0
- package/src/shared/program-factory.ts +51 -0
- package/src/shared/source-location.ts +27 -0
- package/src/shared/tsconfig-loader.ts +126 -0
- package/src/shared/tsdoc-parser.ts +155 -0
- package/src/shared/type-converter.ts +99 -0
- package/src/shared/typescript-utils.ts +246 -0
- package/src/type-extractor/collector/result-collector.ts +57 -0
- package/src/type-extractor/collector/scalar-collector.ts +254 -0
- package/src/type-extractor/converter/field-eligibility.ts +112 -0
- package/src/type-extractor/converter/graphql-converter.ts +459 -0
- package/src/type-extractor/extract-types.ts +1 -0
- package/src/type-extractor/extractor/field-type-resolver.ts +569 -0
- package/src/type-extractor/extractor/type-extractor.ts +1567 -0
- package/src/type-extractor/extractor/type-name-collector.ts +130 -0
- package/src/type-extractor/index.ts +20 -0
- package/src/type-extractor/mapper/scalar-base-type-mapper.ts +265 -0
- package/src/type-extractor/types/diagnostics.ts +99 -0
- package/src/type-extractor/types/graphql.ts +55 -0
- package/src/type-extractor/types/index.ts +37 -0
- package/src/type-extractor/types/ts-type-reference-factory.ts +137 -0
- package/src/type-extractor/types/typescript.ts +133 -0
- package/src/type-extractor/validator/type-validator.ts +77 -0
- package/dist/auto-type-generator/auto-type-generator.test.d.ts +0 -2
- package/dist/auto-type-generator/auto-type-generator.test.d.ts.map +0 -1
- package/dist/auto-type-generator/auto-type-generator.test.js +0 -613
- package/dist/auto-type-generator/auto-type-generator.test.js.map +0 -1
- package/dist/auto-type-generator/name-collision-validator.test.d.ts +0 -2
- package/dist/auto-type-generator/name-collision-validator.test.d.ts.map +0 -1
- package/dist/auto-type-generator/name-collision-validator.test.js +0 -358
- package/dist/auto-type-generator/name-collision-validator.test.js.map +0 -1
- package/dist/auto-type-generator/naming-convention.test.d.ts +0 -2
- package/dist/auto-type-generator/naming-convention.test.d.ts.map +0 -1
- package/dist/auto-type-generator/naming-convention.test.js +0 -132
- package/dist/auto-type-generator/naming-convention.test.js.map +0 -1
- package/dist/commands/gen.test.d.ts +0 -2
- package/dist/commands/gen.test.d.ts.map +0 -1
- package/dist/commands/gen.test.js +0 -226
- package/dist/commands/gen.test.js.map +0 -1
- package/dist/config-loader/loader.test.d.ts +0 -2
- package/dist/config-loader/loader.test.d.ts.map +0 -1
- package/dist/config-loader/loader.test.js +0 -123
- package/dist/config-loader/loader.test.js.map +0 -1
- package/dist/config-loader/validator.test.d.ts +0 -2
- package/dist/config-loader/validator.test.d.ts.map +0 -1
- package/dist/config-loader/validator.test.js +0 -846
- package/dist/config-loader/validator.test.js.map +0 -1
- package/dist/gen-orchestrator/golden.test.d.ts +0 -2
- package/dist/gen-orchestrator/golden.test.d.ts.map +0 -1
- package/dist/gen-orchestrator/golden.test.js +0 -102
- package/dist/gen-orchestrator/golden.test.js.map +0 -1
- package/dist/gen-orchestrator/hook-executor/hook-executor.test.d.ts +0 -2
- package/dist/gen-orchestrator/hook-executor/hook-executor.test.d.ts.map +0 -1
- package/dist/gen-orchestrator/hook-executor/hook-executor.test.js +0 -167
- package/dist/gen-orchestrator/hook-executor/hook-executor.test.js.map +0 -1
- package/dist/gen-orchestrator/reporter/progress-reporter.test.d.ts +0 -2
- package/dist/gen-orchestrator/reporter/progress-reporter.test.d.ts.map +0 -1
- package/dist/gen-orchestrator/reporter/progress-reporter.test.js +0 -74
- package/dist/gen-orchestrator/reporter/progress-reporter.test.js.map +0 -1
- package/dist/resolver-extractor/validator/only-validator.test.d.ts +0 -8
- package/dist/resolver-extractor/validator/only-validator.test.d.ts.map +0 -1
- package/dist/resolver-extractor/validator/only-validator.test.js +0 -352
- package/dist/resolver-extractor/validator/only-validator.test.js.map +0 -1
- package/dist/schema-generator/builder/ast-builder.test.d.ts +0 -2
- package/dist/schema-generator/builder/ast-builder.test.d.ts.map +0 -1
- package/dist/schema-generator/builder/ast-builder.test.js +0 -469
- package/dist/schema-generator/builder/ast-builder.test.js.map +0 -1
- package/dist/shared/file-scanner.test.d.ts +0 -2
- package/dist/shared/file-scanner.test.d.ts.map +0 -1
- package/dist/shared/file-scanner.test.js +0 -138
- package/dist/shared/file-scanner.test.js.map +0 -1
- package/dist/shared/interface-validator.test.d.ts +0 -2
- package/dist/shared/interface-validator.test.d.ts.map +0 -1
- package/dist/shared/interface-validator.test.js +0 -145
- package/dist/shared/interface-validator.test.js.map +0 -1
- package/dist/type-extractor/types/typescript.test.d.ts +0 -2
- package/dist/type-extractor/types/typescript.test.d.ts.map +0 -1
- package/dist/type-extractor/types/typescript.test.js +0 -287
- package/dist/type-extractor/types/typescript.test.js.map +0 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type { ScanResult } from "../shared/index.js";
|
|
2
|
+
export type {
|
|
3
|
+
ExtractResolversResult,
|
|
4
|
+
GraphQLFieldDefinition,
|
|
5
|
+
GraphQLInputValue,
|
|
6
|
+
MutationFieldDefinitions,
|
|
7
|
+
QueryFieldDefinitions,
|
|
8
|
+
TypeExtension,
|
|
9
|
+
} from "./extract-resolvers.js";
|
|
10
|
+
export type {
|
|
11
|
+
AbstractResolverInfo,
|
|
12
|
+
AbstractResolverKind,
|
|
13
|
+
} from "./extractor/define-api-extractor.js";
|
|
14
|
+
export type {
|
|
15
|
+
OnlyConstraintViolation,
|
|
16
|
+
OnlyConstraintViolationCode,
|
|
17
|
+
ValidateOnlyConstraintsOptions,
|
|
18
|
+
ValidationContext,
|
|
19
|
+
} from "./validator/only-validator.js";
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbstractResolverValidator validates that abstract type resolvers
|
|
3
|
+
* reference valid types in the schema.
|
|
4
|
+
*
|
|
5
|
+
* - resolveType must reference union or interface types
|
|
6
|
+
* - isTypeOf must reference object types
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { BaseType } from "../../schema-generator/integrator/result-integrator.js";
|
|
10
|
+
import type {
|
|
11
|
+
Diagnostic,
|
|
12
|
+
DiagnosticCode,
|
|
13
|
+
} from "../../type-extractor/types/index.js";
|
|
14
|
+
import type { AbstractResolverInfo } from "../extractor/define-api-extractor.js";
|
|
15
|
+
|
|
16
|
+
export interface ValidateAbstractResolversOptions {
|
|
17
|
+
readonly abstractResolvers: ReadonlyArray<AbstractResolverInfo>;
|
|
18
|
+
readonly baseTypes: ReadonlyArray<BaseType>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ValidateAbstractResolversResult {
|
|
22
|
+
readonly diagnostics: ReadonlyArray<Diagnostic>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function formatTypeKindForDisplay(kind: BaseType["kind"]): string {
|
|
26
|
+
return kind.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Formats a list of resolver definitions for error messages.
|
|
31
|
+
*/
|
|
32
|
+
function formatResolverLocations(
|
|
33
|
+
resolvers: ReadonlyArray<AbstractResolverInfo>,
|
|
34
|
+
): string {
|
|
35
|
+
return resolvers
|
|
36
|
+
.map(
|
|
37
|
+
(r) =>
|
|
38
|
+
` - ${r.exportName} at ${r.sourceLocation.file}:${r.sourceLocation.line}:${r.sourceLocation.column}`,
|
|
39
|
+
)
|
|
40
|
+
.join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function reportDuplicates(
|
|
44
|
+
resolverMap: Map<string, AbstractResolverInfo[]>,
|
|
45
|
+
code: DiagnosticCode,
|
|
46
|
+
kind: string,
|
|
47
|
+
): Diagnostic[] {
|
|
48
|
+
const diagnostics: Diagnostic[] = [];
|
|
49
|
+
for (const [typeName, duplicates] of resolverMap) {
|
|
50
|
+
if (duplicates.length > 1) {
|
|
51
|
+
const firstResolver = duplicates[0];
|
|
52
|
+
if (firstResolver) {
|
|
53
|
+
diagnostics.push({
|
|
54
|
+
code,
|
|
55
|
+
message: `Multiple ${kind} definitions found for type '${typeName}':\n${formatResolverLocations(duplicates)}`,
|
|
56
|
+
severity: "error",
|
|
57
|
+
location: firstResolver.sourceLocation,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return diagnostics;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Detects duplicate abstract type resolver definitions.
|
|
67
|
+
* Groups resolvers by kind and target type, then reports all duplicates.
|
|
68
|
+
*
|
|
69
|
+
* @param resolvers - All abstract resolvers to check
|
|
70
|
+
* @param typeMap - Map of type names to their definitions (for existence checking)
|
|
71
|
+
* @returns Array of diagnostics for duplicate definitions
|
|
72
|
+
*/
|
|
73
|
+
function detectDuplicateResolvers(
|
|
74
|
+
resolvers: ReadonlyArray<AbstractResolverInfo>,
|
|
75
|
+
typeMap: Map<string, BaseType>,
|
|
76
|
+
): Diagnostic[] {
|
|
77
|
+
const resolveTypeMap = new Map<string, AbstractResolverInfo[]>();
|
|
78
|
+
const isTypeOfMap = new Map<string, AbstractResolverInfo[]>();
|
|
79
|
+
|
|
80
|
+
for (const resolver of resolvers) {
|
|
81
|
+
if (!typeMap.has(resolver.targetTypeName)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const targetMap =
|
|
86
|
+
resolver.kind === "resolveType" ? resolveTypeMap : isTypeOfMap;
|
|
87
|
+
const existing = targetMap.get(resolver.targetTypeName) ?? [];
|
|
88
|
+
existing.push(resolver);
|
|
89
|
+
targetMap.set(resolver.targetTypeName, existing);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return [
|
|
93
|
+
...reportDuplicates(
|
|
94
|
+
resolveTypeMap,
|
|
95
|
+
"DUPLICATE_RESOLVE_TYPE",
|
|
96
|
+
"resolveType",
|
|
97
|
+
),
|
|
98
|
+
...reportDuplicates(isTypeOfMap, "DUPLICATE_IS_TYPE_OF", "isTypeOf"),
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function createMissingResolverDiagnostic(
|
|
103
|
+
typeName: string,
|
|
104
|
+
typeKind: "Union" | "Interface",
|
|
105
|
+
sourceFile: string | null,
|
|
106
|
+
): Diagnostic {
|
|
107
|
+
const memberLabel = typeKind === "Union" ? "member" : "implementing";
|
|
108
|
+
return {
|
|
109
|
+
code: "MISSING_ABSTRACT_TYPE_RESOLVER",
|
|
110
|
+
message: `${typeKind} type '${typeName}' has no resolveType defined, and not all ${memberLabel} types have isTypeOf defined. To prevent runtime errors, either define a resolveType for '${typeName}' or define isTypeOf for each ${memberLabel} type.`,
|
|
111
|
+
severity: "error",
|
|
112
|
+
location: sourceFile ? { file: sourceFile, line: 1, column: 1 } : null,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detects abstract types (union/interface) that have no resolveType defined
|
|
118
|
+
* and whose member/implementing types don't all have isTypeOf defined.
|
|
119
|
+
*
|
|
120
|
+
* @param resolvers - All abstract resolvers to check
|
|
121
|
+
* @param typeMap - Map of type names to their definitions
|
|
122
|
+
* @returns Array of warning diagnostics for missing resolvers
|
|
123
|
+
*/
|
|
124
|
+
function detectMissingAbstractTypeResolvers(
|
|
125
|
+
resolvers: ReadonlyArray<AbstractResolverInfo>,
|
|
126
|
+
typeMap: Map<string, BaseType>,
|
|
127
|
+
): Diagnostic[] {
|
|
128
|
+
const diagnostics: Diagnostic[] = [];
|
|
129
|
+
|
|
130
|
+
const resolveTypeSet = new Set<string>();
|
|
131
|
+
const isTypeOfSet = new Set<string>();
|
|
132
|
+
|
|
133
|
+
for (const resolver of resolvers) {
|
|
134
|
+
if (resolver.kind === "resolveType") {
|
|
135
|
+
resolveTypeSet.add(resolver.targetTypeName);
|
|
136
|
+
} else if (resolver.kind === "isTypeOf") {
|
|
137
|
+
isTypeOfSet.add(resolver.targetTypeName);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const implementingTypesMap = new Map<string, string[]>();
|
|
142
|
+
for (const [typeName, baseType] of typeMap) {
|
|
143
|
+
if (
|
|
144
|
+
baseType.kind === "Object" &&
|
|
145
|
+
baseType.implementedInterfaces &&
|
|
146
|
+
baseType.implementedInterfaces.length > 0
|
|
147
|
+
) {
|
|
148
|
+
for (const interfaceName of baseType.implementedInterfaces) {
|
|
149
|
+
const existing = implementingTypesMap.get(interfaceName) ?? [];
|
|
150
|
+
existing.push(typeName);
|
|
151
|
+
implementingTypesMap.set(interfaceName, existing);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const [typeName, baseType] of typeMap) {
|
|
157
|
+
if (baseType.kind !== "Union" && baseType.kind !== "Interface") {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (resolveTypeSet.has(typeName)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const members =
|
|
165
|
+
baseType.kind === "Union"
|
|
166
|
+
? (baseType.unionMembers ?? [])
|
|
167
|
+
: (implementingTypesMap.get(typeName) ?? []);
|
|
168
|
+
|
|
169
|
+
if (members.length === 0) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const allMembersHaveIsTypeOf = members.every((m) => isTypeOfSet.has(m));
|
|
174
|
+
if (!allMembersHaveIsTypeOf) {
|
|
175
|
+
diagnostics.push(
|
|
176
|
+
createMissingResolverDiagnostic(
|
|
177
|
+
typeName,
|
|
178
|
+
baseType.kind,
|
|
179
|
+
baseType.sourceFile ?? null,
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return diagnostics;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Validates abstract type resolvers.
|
|
190
|
+
*
|
|
191
|
+
* @param options - Validation options including abstract resolvers and base types
|
|
192
|
+
* @returns Validation result with diagnostics
|
|
193
|
+
*/
|
|
194
|
+
export function validateAbstractResolvers(
|
|
195
|
+
options: ValidateAbstractResolversOptions,
|
|
196
|
+
): ValidateAbstractResolversResult {
|
|
197
|
+
const diagnostics: Diagnostic[] = [];
|
|
198
|
+
const typeMap = new Map<string, BaseType>();
|
|
199
|
+
|
|
200
|
+
for (const baseType of options.baseTypes) {
|
|
201
|
+
typeMap.set(baseType.name, baseType);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const resolver of options.abstractResolvers) {
|
|
205
|
+
const targetType = typeMap.get(resolver.targetTypeName);
|
|
206
|
+
|
|
207
|
+
if (!targetType) {
|
|
208
|
+
diagnostics.push({
|
|
209
|
+
code: "UNKNOWN_ABSTRACT_TYPE",
|
|
210
|
+
message: `Type '${resolver.targetTypeName}' does not exist in the schema. The ${resolver.kind} resolver '${resolver.exportName}' references a type that has not been defined.`,
|
|
211
|
+
severity: "error",
|
|
212
|
+
location: resolver.sourceLocation,
|
|
213
|
+
});
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (resolver.kind === "resolveType") {
|
|
218
|
+
if (targetType.kind !== "Union" && targetType.kind !== "Interface") {
|
|
219
|
+
diagnostics.push({
|
|
220
|
+
code: "INVALID_ABSTRACT_TYPE_KIND",
|
|
221
|
+
message: `Type '${resolver.targetTypeName}' is ${formatTypeKindForDisplay(targetType.kind)} type, but resolveType can only be used with union or interface types. The resolver '${resolver.exportName}' must reference an abstract type.`,
|
|
222
|
+
severity: "error",
|
|
223
|
+
location: resolver.sourceLocation,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} else if (resolver.kind === "isTypeOf") {
|
|
227
|
+
if (targetType.kind !== "Object") {
|
|
228
|
+
diagnostics.push({
|
|
229
|
+
code: "INVALID_OBJECT_TYPE_KIND",
|
|
230
|
+
message: `Type '${resolver.targetTypeName}' is ${formatTypeKindForDisplay(targetType.kind)} type, but isTypeOf can only be used with object types. The resolver '${resolver.exportName}' must reference an object type.`,
|
|
231
|
+
severity: "error",
|
|
232
|
+
location: resolver.sourceLocation,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const duplicateDiagnostics = detectDuplicateResolvers(
|
|
239
|
+
options.abstractResolvers,
|
|
240
|
+
typeMap,
|
|
241
|
+
);
|
|
242
|
+
diagnostics.push(...duplicateDiagnostics);
|
|
243
|
+
|
|
244
|
+
const missingResolverWarnings = detectMissingAbstractTypeResolvers(
|
|
245
|
+
options.abstractResolvers,
|
|
246
|
+
typeMap,
|
|
247
|
+
);
|
|
248
|
+
diagnostics.push(...missingResolverWarnings);
|
|
249
|
+
|
|
250
|
+
return { diagnostics };
|
|
251
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OnlyValidator validates that scalar types with only constraints
|
|
3
|
+
* are used in the correct position (input or output).
|
|
4
|
+
*
|
|
5
|
+
* - Scalars with only: "output" can only be used in output positions
|
|
6
|
+
* - Scalars with only: "input" can only be used in input positions
|
|
7
|
+
* - Scalars without only constraint can be used in both positions
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { TSTypeReference } from "../../type-extractor/types/index.js";
|
|
11
|
+
|
|
12
|
+
export type OnlyConstraintViolationCode =
|
|
13
|
+
| "OUTPUT_ONLY_IN_INPUT_POSITION"
|
|
14
|
+
| "INPUT_ONLY_IN_OUTPUT_POSITION";
|
|
15
|
+
|
|
16
|
+
export interface OnlyConstraintViolation {
|
|
17
|
+
readonly code: OnlyConstraintViolationCode;
|
|
18
|
+
readonly message: string;
|
|
19
|
+
readonly scalarName: string;
|
|
20
|
+
readonly only: "input" | "output";
|
|
21
|
+
readonly position: "input" | "output";
|
|
22
|
+
readonly context: ValidationContext;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface InputTypeFieldContext {
|
|
26
|
+
readonly kind: "inputTypeField";
|
|
27
|
+
readonly typeName: string;
|
|
28
|
+
readonly fieldName: string;
|
|
29
|
+
readonly sourceFile: string;
|
|
30
|
+
readonly line: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ObjectTypeFieldContext {
|
|
34
|
+
readonly kind: "objectTypeField";
|
|
35
|
+
readonly typeName: string;
|
|
36
|
+
readonly fieldName: string;
|
|
37
|
+
readonly sourceFile: string;
|
|
38
|
+
readonly line: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ResolverArgumentContext {
|
|
42
|
+
readonly kind: "resolverArgument";
|
|
43
|
+
readonly resolverName: string;
|
|
44
|
+
readonly argumentName: string;
|
|
45
|
+
readonly sourceFile: string;
|
|
46
|
+
readonly line: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ResolverReturnTypeContext {
|
|
50
|
+
readonly kind: "resolverReturnType";
|
|
51
|
+
readonly resolverName: string;
|
|
52
|
+
readonly sourceFile: string;
|
|
53
|
+
readonly line: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ValidationContext =
|
|
57
|
+
| InputTypeFieldContext
|
|
58
|
+
| ObjectTypeFieldContext
|
|
59
|
+
| ResolverArgumentContext
|
|
60
|
+
| ResolverReturnTypeContext;
|
|
61
|
+
|
|
62
|
+
export interface ValidateOnlyConstraintsOptions {
|
|
63
|
+
readonly typeRef: TSTypeReference;
|
|
64
|
+
readonly position: "input" | "output";
|
|
65
|
+
readonly context: ValidationContext;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatContextLocation(context: ValidationContext): string {
|
|
69
|
+
switch (context.kind) {
|
|
70
|
+
case "inputTypeField":
|
|
71
|
+
return `field '${context.fieldName}' of input type '${context.typeName}'`;
|
|
72
|
+
case "objectTypeField":
|
|
73
|
+
return `field '${context.fieldName}' of type '${context.typeName}'`;
|
|
74
|
+
case "resolverArgument":
|
|
75
|
+
return `argument '${context.argumentName}' of resolver '${context.resolverName}'`;
|
|
76
|
+
case "resolverReturnType":
|
|
77
|
+
return `return type of resolver '${context.resolverName}'`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createViolationMessage(
|
|
82
|
+
scalarName: string,
|
|
83
|
+
only: "input" | "output",
|
|
84
|
+
position: "input" | "output",
|
|
85
|
+
context: ValidationContext,
|
|
86
|
+
): string {
|
|
87
|
+
const locationDesc = formatContextLocation(context);
|
|
88
|
+
const oppositePosition = only === "output" ? "input" : "output";
|
|
89
|
+
const hint =
|
|
90
|
+
position === "input"
|
|
91
|
+
? "Use a type without 'only' constraint or with 'only: \"input\"' instead."
|
|
92
|
+
: "Use a type without 'only' constraint or with 'only: \"output\"' instead.";
|
|
93
|
+
|
|
94
|
+
return `Scalar type '${scalarName}' is ${only}-only but is used in ${position} position (${locationDesc}). ${oppositePosition.charAt(0).toUpperCase() + oppositePosition.slice(1)}-only scalars cannot be used in ${position} positions. ${hint}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function collectScalarViolations(
|
|
98
|
+
typeRef: TSTypeReference,
|
|
99
|
+
position: "input" | "output",
|
|
100
|
+
context: ValidationContext,
|
|
101
|
+
violations: OnlyConstraintViolation[],
|
|
102
|
+
): void {
|
|
103
|
+
if (typeRef.scalarInfo) {
|
|
104
|
+
const { scalarName, only } = typeRef.scalarInfo;
|
|
105
|
+
|
|
106
|
+
if (only !== null) {
|
|
107
|
+
if (position === "input" && only === "output") {
|
|
108
|
+
violations.push({
|
|
109
|
+
code: "OUTPUT_ONLY_IN_INPUT_POSITION",
|
|
110
|
+
message: createViolationMessage(scalarName, only, position, context),
|
|
111
|
+
scalarName,
|
|
112
|
+
only,
|
|
113
|
+
position,
|
|
114
|
+
context,
|
|
115
|
+
});
|
|
116
|
+
} else if (position === "output" && only === "input") {
|
|
117
|
+
violations.push({
|
|
118
|
+
code: "INPUT_ONLY_IN_OUTPUT_POSITION",
|
|
119
|
+
message: createViolationMessage(scalarName, only, position, context),
|
|
120
|
+
scalarName,
|
|
121
|
+
only,
|
|
122
|
+
position,
|
|
123
|
+
context,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (typeRef.elementType) {
|
|
130
|
+
collectScalarViolations(typeRef.elementType, position, context, violations);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (typeRef.members) {
|
|
134
|
+
for (const member of typeRef.members) {
|
|
135
|
+
collectScalarViolations(member, position, context, violations);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validates that only constraints are not violated.
|
|
142
|
+
* Returns diagnostics for each violation.
|
|
143
|
+
*
|
|
144
|
+
* @param options - Validation options including type reference, position, and context
|
|
145
|
+
* @returns Array of constraint violations (empty if no violations)
|
|
146
|
+
*/
|
|
147
|
+
export function validateOnlyConstraints(
|
|
148
|
+
options: ValidateOnlyConstraintsOptions,
|
|
149
|
+
): ReadonlyArray<OnlyConstraintViolation> {
|
|
150
|
+
const violations: OnlyConstraintViolation[] = [];
|
|
151
|
+
collectScalarViolations(
|
|
152
|
+
options.typeRef,
|
|
153
|
+
options.position,
|
|
154
|
+
options.context,
|
|
155
|
+
violations,
|
|
156
|
+
);
|
|
157
|
+
return violations;
|
|
158
|
+
}
|