@znight/graphql 0.0.0 → 0.2.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.md +1346 -0
- package/dist/compiler.d.mts +114 -0
- package/dist/compiler.mjs +4599 -0
- package/dist/compiler.mjs.map +1 -0
- package/dist/index.d.mts +583 -0
- package/dist/index.mjs +134 -0
- package/dist/index.mjs.map +1 -0
- package/dist/names-BlzDM5cF.mjs +111 -0
- package/dist/names-BlzDM5cF.mjs.map +1 -0
- package/dist/plugin-CR_wVfRU.mjs +3445 -0
- package/dist/plugin-CR_wVfRU.mjs.map +1 -0
- package/dist/testing.d.mts +101 -0
- package/dist/testing.mjs +306 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-DQ713UgI.d.mts +266 -0
- package/package.json +97 -1
- package/src/compiler/analyze.ts +727 -0
- package/src/compiler/builder.ts +1561 -0
- package/src/compiler/checks.ts +105 -0
- package/src/compiler/conformance.ts +1068 -0
- package/src/compiler/constants.ts +80 -0
- package/src/compiler/data.ts +83 -0
- package/src/compiler/defaults.ts +92 -0
- package/src/compiler/discriminators.ts +53 -0
- package/src/compiler/docs.ts +55 -0
- package/src/compiler/generate.ts +419 -0
- package/src/compiler/graphql-js.ts +42 -0
- package/src/compiler/ids.ts +128 -0
- package/src/compiler/index.ts +98 -0
- package/src/compiler/locate.ts +82 -0
- package/src/compiler/model.ts +137 -0
- package/src/compiler/options.ts +85 -0
- package/src/compiler/scalars.ts +138 -0
- package/src/compiler/schema-first.ts +595 -0
- package/src/compiler/sdl-source.ts +529 -0
- package/src/compiler/sdl-view.ts +61 -0
- package/src/compiler/suggest.ts +39 -0
- package/src/context.ts +181 -0
- package/src/decisions.ts +169 -0
- package/src/decorators.ts +202 -0
- package/src/errors.ts +89 -0
- package/src/execution.ts +1064 -0
- package/src/format.ts +285 -0
- package/src/index.ts +89 -0
- package/src/limits.ts +177 -0
- package/src/loaders.ts +149 -0
- package/src/memory-pubsub.ts +133 -0
- package/src/metadata.ts +222 -0
- package/src/names.ts +47 -0
- package/src/options.ts +331 -0
- package/src/pipeline.ts +338 -0
- package/src/plugin.ts +579 -0
- package/src/pubsub.ts +36 -0
- package/src/results.ts +206 -0
- package/src/scalars.ts +138 -0
- package/src/schema-hash.ts +18 -0
- package/src/schema.ts +635 -0
- package/src/sdl.ts +530 -0
- package/src/testing.ts +454 -0
- package/src/tokens.ts +22 -0
- package/src/transport/abort.ts +80 -0
- package/src/transport/accept.ts +155 -0
- package/src/transport/graphiql.ts +106 -0
- package/src/transport/http.ts +373 -0
- package/src/transport/sse.ts +45 -0
- package/src/type-ref.ts +89 -0
- package/src/types.ts +225 -0
- package/src/validate-args.ts +48 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks shared by the code-first schema builder (`builder.ts`) and the schema-first one
|
|
3
|
+
* (`schema-first.ts`): loader providers (ZNIGHT065) and the route decisions of root fields
|
|
4
|
+
* (ZNIGHT023, RFC 0001).
|
|
5
|
+
*/
|
|
6
|
+
import type { ClassInfo, Diagnostic, Location, ValidateContext } from '@znight/compiler'
|
|
7
|
+
import type { MetaEntry } from '@znight/http/compiler'
|
|
8
|
+
import type { GraphQLData, ResolverData } from './data.ts'
|
|
9
|
+
|
|
10
|
+
import { requiredRouteGroups } from '@znight/http/compiler'
|
|
11
|
+
|
|
12
|
+
import { ROOT_TYPE_NAMES } from '../names.ts'
|
|
13
|
+
import { Codes } from './constants.ts'
|
|
14
|
+
import { GraphQLIds } from './ids.ts'
|
|
15
|
+
|
|
16
|
+
type Context = ValidateContext<GraphQLData>
|
|
17
|
+
type Report = (diagnostic: Diagnostic) => void
|
|
18
|
+
|
|
19
|
+
/** `Loader<L>`: `L` must be a loader provider, implementing `BatchLoad<K, V>` (ZNIGHT065). */
|
|
20
|
+
export function checkLoaders(ctx: Context, resolvers: readonly ResolverData[], report: Report): void {
|
|
21
|
+
const seen = new Set<number>()
|
|
22
|
+
for (const resolver of resolvers) {
|
|
23
|
+
for (const field of resolver.fields) {
|
|
24
|
+
for (const param of field.params) {
|
|
25
|
+
if (param?.kind !== 'loader' || seen.has(param.binding.id)) continue
|
|
26
|
+
seen.add(param.binding.id)
|
|
27
|
+
const ref = ctx.binding(param.binding)
|
|
28
|
+
const token = tokenOfRef(ref)
|
|
29
|
+
const provider = token === undefined ? undefined : ctx.provider(token)
|
|
30
|
+
// an unbound loader is ZNIGHT001 (reported by the binding)
|
|
31
|
+
if (provider === undefined || provider.heritage.some(type => type.identity === GraphQLIds.BatchLoad)) continue
|
|
32
|
+
if (provider.kind !== 'class' || hasLoadMethod(ctx, provider.classIdentity)) continue
|
|
33
|
+
report({
|
|
34
|
+
code: Codes.InvalidLoader,
|
|
35
|
+
severity: 'error',
|
|
36
|
+
message: `${provider.displayName} is not a loader: Loader<${provider.displayName}> needs a provider implementing BatchLoad<K, V> (${resolver.host.displayName}.${field.method})`,
|
|
37
|
+
location: param.location,
|
|
38
|
+
hints: [
|
|
39
|
+
`export class ${provider.displayName} implements BatchLoad<ID, Value> { async load(keys: readonly ID[]): Promise<readonly (Value | Error)[]> { ... } }`,
|
|
40
|
+
],
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* RFC 0001: every root field decides every required route decorator group (ZNIGHT023). `roots`
|
|
49
|
+
* names the root types (`schema { query: RootQuery }` in schema-first).
|
|
50
|
+
*/
|
|
51
|
+
export function checkDecisions(
|
|
52
|
+
ctx: Context,
|
|
53
|
+
resolvers: readonly ResolverData[],
|
|
54
|
+
report: Report,
|
|
55
|
+
roots: { readonly query: string; readonly mutation?: string; readonly subscription?: string } = ROOT_TYPE_NAMES,
|
|
56
|
+
): void {
|
|
57
|
+
const applied: MetaEntry[] = []
|
|
58
|
+
for (const resolver of resolvers) for (const field of resolver.fields) applied.push(...field.meta)
|
|
59
|
+
const required = requiredRouteGroups(ctx, applied)
|
|
60
|
+
if (required.length === 0) return
|
|
61
|
+
for (const resolver of resolvers) {
|
|
62
|
+
for (const field of resolver.fields) {
|
|
63
|
+
if (field.operation === 'field') continue
|
|
64
|
+
const decided = new Set(field.meta.map(entry => entry.group))
|
|
65
|
+
for (const group of required) {
|
|
66
|
+
if (decided.has(group.group)) continue
|
|
67
|
+
const root = roots[field.operation] ?? ROOT_TYPE_NAMES[field.operation]
|
|
68
|
+
report({
|
|
69
|
+
code: Codes.MissingRouteDecision,
|
|
70
|
+
severity: 'error',
|
|
71
|
+
message: `Root field ${root}.${field.name} (${resolver.host.displayName}.${field.method}) has no "${group.group}" decision`,
|
|
72
|
+
location: field.methodLocation,
|
|
73
|
+
hints: [`Add ${group.suggestion} to the method or to ${resolver.host.displayName}`],
|
|
74
|
+
related: [
|
|
75
|
+
{
|
|
76
|
+
message: `The "${group.group}" group is required`,
|
|
77
|
+
location: (group.decorators[0] as { definition: Location }).definition,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The class (or one of its base classes) declares an instance `load` method. */
|
|
87
|
+
function hasLoadMethod(ctx: Context, identity: string): boolean {
|
|
88
|
+
const seen = new Set<string>()
|
|
89
|
+
let current: string | undefined = identity
|
|
90
|
+
while (current !== undefined && !seen.has(current)) {
|
|
91
|
+
seen.add(current)
|
|
92
|
+
const cls: ClassInfo | undefined = ctx.classInfo(current)
|
|
93
|
+
if (cls === undefined) return true // not analysable (a compiled class): trust the type checker
|
|
94
|
+
if (cls.methods.some(method => method.name === 'load' && method.kind === 'method' && !method.static)) return true
|
|
95
|
+
if (cls.properties.some(property => property.name === 'load' && !property.static)) return true
|
|
96
|
+
current = cls.superClass?.identity
|
|
97
|
+
}
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The token of a binding result (`undefined` when binding failed). */
|
|
102
|
+
function tokenOfRef(ref: ReturnType<Context['binding']>): string | undefined {
|
|
103
|
+
if (typeof ref === 'string') return ref
|
|
104
|
+
return ref !== undefined && 'token' in ref ? ref.token : undefined
|
|
105
|
+
}
|