@znight/core 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -3
- package/dist/index.d.mts +68 -3
- package/dist/index.mjs +36 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/errors.ts +1 -1
- package/src/hooks.ts +8 -2
- package/src/index.ts +12 -0
- package/src/params.ts +89 -0
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -186,7 +186,7 @@ export const ERROR_DESCRIPTIONS: Readonly<Record<ErrorCode, string>> = {
|
|
|
186
186
|
ZNIGHT063: 'Invalid GraphQL input: a type cannot be used in an input position',
|
|
187
187
|
ZNIGHT064: 'Unresolvable abstract type: the members of a GraphQL union or interface cannot be told apart at runtime',
|
|
188
188
|
ZNIGHT065: 'Invalid loader: not a Loader of a BatchLoad provider, or a batch returning the wrong number of values',
|
|
189
|
-
ZNIGHT066: 'Invalid GraphQL setup: metadata, schema, GraphQLPlugin options or HttpPlugin missing',
|
|
189
|
+
ZNIGHT066: 'Invalid GraphQL setup: metadata, schema, GraphQLPlugin options, driver or HttpPlugin missing',
|
|
190
190
|
ZNIGHT067: 'Invalid GraphQL SDL: the schema-first .graphql files cannot be read, parsed or validated',
|
|
191
191
|
ZNIGHT068: 'GraphQL schema mismatch: schema-first resolvers do not cover or conform to the SDL',
|
|
192
192
|
}
|
package/src/hooks.ts
CHANGED
|
@@ -173,9 +173,10 @@ export type GraphQLOperationType = 'query' | 'mutation' | 'subscription'
|
|
|
173
173
|
|
|
174
174
|
/**
|
|
175
175
|
* How a GraphQL operation reached the server (`GraphQLOperationEvent.transport`): GraphQL over HTTP,
|
|
176
|
-
* GraphQL over Server-Sent Events,
|
|
176
|
+
* GraphQL over Server-Sent Events, GraphQL over WebSocket (`ws`, reserved for `graphql-ws`), or a
|
|
177
|
+
* direct call (`execute()` / `subscribe()` of the plugin).
|
|
177
178
|
*/
|
|
178
|
-
export type GraphQLOperationTransport = 'http' | 'sse' | 'direct'
|
|
179
|
+
export type GraphQLOperationTransport = 'http' | 'sse' | 'ws' | 'direct'
|
|
179
180
|
|
|
180
181
|
/**
|
|
181
182
|
* Outcome of a GraphQL operation (`GraphQLOperationEvent.outcome`):
|
|
@@ -210,6 +211,11 @@ export interface GraphQLOperationEvent {
|
|
|
210
211
|
/** SHA-256 (hex) of the query text, cached with the parsed document; `undefined` without a query. */
|
|
211
212
|
readonly documentId: string | undefined
|
|
212
213
|
readonly transport: GraphQLOperationTransport
|
|
214
|
+
/**
|
|
215
|
+
* The GraphQL driver that received the operation (RFC 0009): `'builtin'` for the built-in
|
|
216
|
+
* transport and for direct calls, `'yoga'`, `'apollo'` or a third-party driver's name otherwise.
|
|
217
|
+
*/
|
|
218
|
+
readonly driver: string
|
|
213
219
|
readonly requestId: string
|
|
214
220
|
/** `GraphQLContext` from `@znight/graphql`. */
|
|
215
221
|
readonly context: unknown
|
package/src/index.ts
CHANGED
|
@@ -78,6 +78,18 @@ export {
|
|
|
78
78
|
silentLogger,
|
|
79
79
|
} from './logger.ts'
|
|
80
80
|
export * from './metadata.ts'
|
|
81
|
+
export {
|
|
82
|
+
createParamDecorator,
|
|
83
|
+
type ParamContexts,
|
|
84
|
+
type ParamDataArgs,
|
|
85
|
+
type ParamDecorator,
|
|
86
|
+
type ParamFunctionOf,
|
|
87
|
+
type ParamResolver,
|
|
88
|
+
type ParamResolverClass,
|
|
89
|
+
type ParamResolverFunction,
|
|
90
|
+
type ParamResolverOf,
|
|
91
|
+
type ParamResolvers,
|
|
92
|
+
} from './params.ts'
|
|
81
93
|
export {
|
|
82
94
|
type Plugin,
|
|
83
95
|
type PluginContext,
|
package/src/params.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom parameter decorators (RFC 0010). `createParamDecorator` returns a marker decorator, read
|
|
3
|
+
* by `@znight/compiler`, and keeps the resolvers that the generated `invoke` of each transport calls.
|
|
4
|
+
*/
|
|
5
|
+
import type { ParameterMarker } from './decorators.ts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolver contexts by transport. Each transport package adds its own entry through declaration
|
|
9
|
+
* merging (`http: HttpContext` in `@znight/http`, `graphql: ResolverContext`, `messaging:
|
|
10
|
+
* InboundMessage`); a transport that is not installed has no entry.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* declare module '@znight/core' {
|
|
14
|
+
* interface ParamContexts {
|
|
15
|
+
* http: HttpContext
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
// biome-ignore lint/suspicious/noEmptyInterface: augmented by the transport packages (declaration merging)
|
|
21
|
+
export interface ParamContexts {}
|
|
22
|
+
|
|
23
|
+
/** A provider computing the value of a parameter, in the scope of the call (RFC 0010 §4.6). */
|
|
24
|
+
export interface ParamResolver<C, T, D = undefined> {
|
|
25
|
+
resolve(ctx: C, data: D): T | PromiseLike<T>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A resolver function: receives the context of its transport and the data argument of the use. */
|
|
29
|
+
export type ParamResolverFunction<C, T, D> = (ctx: C, data: D) => T | PromiseLike<T>
|
|
30
|
+
|
|
31
|
+
/** The class of a provider implementing {@link ParamResolver} (bound at build time, resolved in the scope of the call). */
|
|
32
|
+
export type ParamResolverClass<C, T, D> = abstract new (...args: never[]) => ParamResolver<C, T, D>
|
|
33
|
+
|
|
34
|
+
/** Computes the value of a parameter: a function, or the class of a provider implementing {@link ParamResolver}. */
|
|
35
|
+
export type ParamResolverOf<C, T, D> = ParamResolverFunction<C, T, D> | ParamResolverClass<C, T, D>
|
|
36
|
+
|
|
37
|
+
/** The resolvers of a definition, by transport (keys of {@link ParamContexts}). */
|
|
38
|
+
export type ParamResolvers<T, D> = {
|
|
39
|
+
readonly [K in keyof ParamContexts]?: ParamResolverOf<ParamContexts[K], T, D>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Arguments of a use: none when `D` is `undefined`, optional when `D` includes `undefined`, required otherwise. */
|
|
43
|
+
export type ParamDataArgs<D> = undefined extends D ? [data?: D] : [data: D]
|
|
44
|
+
|
|
45
|
+
/** What {@link createParamDecorator} returns: the decorator (a no-op marker) and its resolvers, for generated code. */
|
|
46
|
+
export interface ParamDecorator<T, D = undefined> {
|
|
47
|
+
(...data: ParamDataArgs<D>): ParameterMarker
|
|
48
|
+
/** The resolvers of the definition, frozen. */
|
|
49
|
+
readonly resolvers: ParamResolvers<T, D>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The resolver function of a definition for a transport, as generated code calls it:
|
|
54
|
+
* `(CurrentUser.resolvers.http as ParamFunctionOf<typeof CurrentUser, 'http'>)(ctx, undefined)`.
|
|
55
|
+
* The compiler emits it only for function resolvers.
|
|
56
|
+
*/
|
|
57
|
+
export type ParamFunctionOf<P extends { readonly resolvers: object }, K extends keyof P['resolvers']> = Extract<
|
|
58
|
+
NonNullable<P['resolvers'][K]>,
|
|
59
|
+
// biome-ignore lint/suspicious/noExplicitAny: matches every function type, whatever its parameters
|
|
60
|
+
(...args: any[]) => unknown
|
|
61
|
+
>
|
|
62
|
+
|
|
63
|
+
function marker(): void {
|
|
64
|
+
// intentionally empty: decorators are compile-time markers
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parameterMarker: ParameterMarker = marker
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Defines a parameter decorator whose value is computed per call by a resolver of the handler's
|
|
71
|
+
* transport (RFC 0010). `T` (required) is the injected type, checked at build time against every
|
|
72
|
+
* decorated parameter; `D` is the type of the data argument (none by default). The definition must
|
|
73
|
+
* be an exported top-level `const`: the generated module imports it.
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* export const CurrentUser = createParamDecorator<User>({
|
|
77
|
+
* http: ctx => userOf(ctx.state),
|
|
78
|
+
* graphql: ctx => userOf(ctx.operation.state),
|
|
79
|
+
* })
|
|
80
|
+
*
|
|
81
|
+
* @Get('/me')
|
|
82
|
+
* me(@CurrentUser() user: User) { ... }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function createParamDecorator<T, D = undefined>(resolvers: ParamResolvers<T, D>): ParamDecorator<T, D> {
|
|
86
|
+
const decorator = ((..._data: ParamDataArgs<D>): ParameterMarker => parameterMarker) as ParamDecorator<T, D>
|
|
87
|
+
Object.defineProperty(decorator, 'resolvers', { value: Object.freeze({ ...resolvers }), enumerable: true })
|
|
88
|
+
return decorator
|
|
89
|
+
}
|