@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/README.md
CHANGED
|
@@ -93,6 +93,36 @@ export class DatabaseFactory {
|
|
|
93
93
|
}
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
## Custom parameter decorators
|
|
97
|
+
|
|
98
|
+
`createParamDecorator<T, D>(resolvers)` defines a parameter decorator of your own (RFC 0010), usable
|
|
99
|
+
on HTTP routes, GraphQL fields and message handlers. Each key of `resolvers` is a transport
|
|
100
|
+
(`http`, `graphql`, `messaging`: the installed transport packages declare them in `ParamContexts`)
|
|
101
|
+
and its value computes the parameter: a function `(ctx, data) => T | PromiseLike<T>`, or the class
|
|
102
|
+
of a provider implementing `ParamResolver<C, T, D>` (constructor injection, any scope).
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { createParamDecorator } from '@znight/core'
|
|
106
|
+
import type { User } from './user.ts'
|
|
107
|
+
|
|
108
|
+
/** The authenticated user, stored in the request state by the authentication guard. */
|
|
109
|
+
export const CurrentUser = createParamDecorator<User>({
|
|
110
|
+
http: ctx => ctx.state.get('user') as User,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// @Get('/me') me(@CurrentUser() user: User) { ... }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `T` (required) is the injected type; the compiler checks it against every decorated parameter.
|
|
117
|
+
- `D` is the type of the data argument (`@Claim('sub')`): none by default, required when it excludes
|
|
118
|
+
`undefined`. The argument is a `value` argument: statically evaluable or an exported binding.
|
|
119
|
+
- The definition is an exported top-level `const`: the generated module imports it and calls
|
|
120
|
+
`CurrentUser.resolvers.http(ctx, data)` (no lookup, no closure, no promise when the resolver
|
|
121
|
+
returns a value). `resolvers` is frozen.
|
|
122
|
+
- Using the decorator on a handler of a transport it has no resolver for, on a constructor parameter,
|
|
123
|
+
with a data argument that cannot be emitted or on a parameter whose type does not accept `T` is a
|
|
124
|
+
ZNIGHT005 at build time.
|
|
125
|
+
|
|
96
126
|
## Application
|
|
97
127
|
|
|
98
128
|
`Application.create(options)` builds the container and runs the plugins:
|
|
@@ -344,7 +374,7 @@ without depending on each other:
|
|
|
344
374
|
| `messaging:message` | `MessageDeliveryEvent` | `@znight/messaging`, one delivery |
|
|
345
375
|
| `messaging:publish` | `MessagePublishEvent` | `@znight/messaging`, one `MessageBroker` publication |
|
|
346
376
|
| `schedule:run` | `ScheduledRunEvent` | `@znight/schedule`, one occurrence of a job (executed or not, see `outcome`) |
|
|
347
|
-
| `graphql:operation` | `GraphQLOperationEvent` | `@znight/graphql` (
|
|
377
|
+
| `graphql:operation` | `GraphQLOperationEvent` | `@znight/graphql` (RFC 0005), one GraphQL operation |
|
|
348
378
|
|
|
349
379
|
`ScheduledRunEvent` carries `job`, `kind` (`cron` / `interval` / `timeout`), `host`, `method`,
|
|
350
380
|
`runId`, `trigger` (`schedule` / `start` / `manual` / `queued`), `scheduledAt` (epoch ms),
|
|
@@ -361,8 +391,10 @@ threw), so a wrapper that reports it reads `event.outcome ?? (nextRejected ? 'fa
|
|
|
361
391
|
|
|
362
392
|
`GraphQLOperationEvent` carries `operationType` (`query` / `mutation` / `subscription`, `undefined`
|
|
363
393
|
when the request was rejected before an operation was selected), `operationName`, `documentId`
|
|
364
|
-
(SHA-256 of the query text), `transport` (`http` / `sse` / `
|
|
365
|
-
`
|
|
394
|
+
(SHA-256 of the query text), `transport` (`http` / `sse` / `ws` / `direct`; `ws` is reserved for
|
|
395
|
+
`graphql-ws`), `driver` (the GraphQL driver that received the operation: `builtin` for the built-in
|
|
396
|
+
transport and direct calls, `yoga`...), `requestId`, `context` (the `GraphQLContext`), `startTime`,
|
|
397
|
+
and, once settled, `outcome` (`success` / `partial` / `error` /
|
|
366
398
|
`rejected`) and `errors` (`{ message, code, path? }`, as sent to the client). A subscription is one
|
|
367
399
|
operation from its start to its end.
|
|
368
400
|
|
package/dist/index.d.mts
CHANGED
|
@@ -783,9 +783,10 @@ interface ScheduledRunEvent {
|
|
|
783
783
|
type GraphQLOperationType = "query" | "mutation" | "subscription";
|
|
784
784
|
/**
|
|
785
785
|
* How a GraphQL operation reached the server (`GraphQLOperationEvent.transport`): GraphQL over HTTP,
|
|
786
|
-
* GraphQL over Server-Sent Events,
|
|
786
|
+
* GraphQL over Server-Sent Events, GraphQL over WebSocket (`ws`, reserved for `graphql-ws`), or a
|
|
787
|
+
* direct call (`execute()` / `subscribe()` of the plugin).
|
|
787
788
|
*/
|
|
788
|
-
type GraphQLOperationTransport = "http" | "sse" | "direct";
|
|
789
|
+
type GraphQLOperationTransport = "http" | "sse" | "ws" | "direct";
|
|
789
790
|
/**
|
|
790
791
|
* Outcome of a GraphQL operation (`GraphQLOperationEvent.outcome`):
|
|
791
792
|
*
|
|
@@ -817,6 +818,11 @@ interface GraphQLOperationEvent {
|
|
|
817
818
|
/** SHA-256 (hex) of the query text, cached with the parsed document; `undefined` without a query. */
|
|
818
819
|
readonly documentId: string | undefined;
|
|
819
820
|
readonly transport: GraphQLOperationTransport;
|
|
821
|
+
/**
|
|
822
|
+
* The GraphQL driver that received the operation (RFC 0009): `'builtin'` for the built-in
|
|
823
|
+
* transport and for direct calls, `'yoga'`, `'apollo'` or a third-party driver's name otherwise.
|
|
824
|
+
*/
|
|
825
|
+
readonly driver: string;
|
|
820
826
|
readonly requestId: string;
|
|
821
827
|
/** `GraphQLContext` from `@znight/graphql`. */
|
|
822
828
|
readonly context: unknown;
|
|
@@ -1153,6 +1159,65 @@ export declare function isDisposable(instance: unknown, disposeMethod?: string):
|
|
|
1153
1159
|
* @throws ZnightError (ZNIGHT021) when `disposeMethod` is not a method of `instance`.
|
|
1154
1160
|
*/
|
|
1155
1161
|
export declare function disposeInstance(instance: unknown, disposeMethod?: string): unknown;
|
|
1162
|
+
/**
|
|
1163
|
+
* Resolver contexts by transport. Each transport package adds its own entry through declaration
|
|
1164
|
+
* merging (`http: HttpContext` in `@znight/http`, `graphql: ResolverContext`, `messaging:
|
|
1165
|
+
* InboundMessage`); a transport that is not installed has no entry.
|
|
1166
|
+
*
|
|
1167
|
+
* ```ts
|
|
1168
|
+
* declare module '@znight/core' {
|
|
1169
|
+
* interface ParamContexts {
|
|
1170
|
+
* http: HttpContext
|
|
1171
|
+
* }
|
|
1172
|
+
* }
|
|
1173
|
+
* ```
|
|
1174
|
+
*/
|
|
1175
|
+
interface ParamContexts {}
|
|
1176
|
+
/** A provider computing the value of a parameter, in the scope of the call (RFC 0010 §4.6). */
|
|
1177
|
+
interface ParamResolver<C, T, D = undefined> {
|
|
1178
|
+
resolve(ctx: C, data: D): T | PromiseLike<T>;
|
|
1179
|
+
}
|
|
1180
|
+
/** A resolver function: receives the context of its transport and the data argument of the use. */
|
|
1181
|
+
type ParamResolverFunction<C, T, D> = (ctx: C, data: D) => T | PromiseLike<T>;
|
|
1182
|
+
/** The class of a provider implementing {@link ParamResolver} (bound at build time, resolved in the scope of the call). */
|
|
1183
|
+
type ParamResolverClass<C, T, D> = abstract new (...args: never[]) => ParamResolver<C, T, D>;
|
|
1184
|
+
/** Computes the value of a parameter: a function, or the class of a provider implementing {@link ParamResolver}. */
|
|
1185
|
+
type ParamResolverOf<C, T, D> = ParamResolverFunction<C, T, D> | ParamResolverClass<C, T, D>;
|
|
1186
|
+
/** The resolvers of a definition, by transport (keys of {@link ParamContexts}). */
|
|
1187
|
+
type ParamResolvers<T, D> = { readonly [K in keyof ParamContexts]?: ParamResolverOf<ParamContexts[K], T, D>; };
|
|
1188
|
+
/** Arguments of a use: none when `D` is `undefined`, optional when `D` includes `undefined`, required otherwise. */
|
|
1189
|
+
type ParamDataArgs<D> = undefined extends D ? [data?: D] : [data: D];
|
|
1190
|
+
/** What {@link createParamDecorator} returns: the decorator (a no-op marker) and its resolvers, for generated code. */
|
|
1191
|
+
interface ParamDecorator<T, D = undefined> {
|
|
1192
|
+
(...data: ParamDataArgs<D>): ParameterMarker;
|
|
1193
|
+
/** The resolvers of the definition, frozen. */
|
|
1194
|
+
readonly resolvers: ParamResolvers<T, D>;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* The resolver function of a definition for a transport, as generated code calls it:
|
|
1198
|
+
* `(CurrentUser.resolvers.http as ParamFunctionOf<typeof CurrentUser, 'http'>)(ctx, undefined)`.
|
|
1199
|
+
* The compiler emits it only for function resolvers.
|
|
1200
|
+
*/
|
|
1201
|
+
type ParamFunctionOf<P extends {
|
|
1202
|
+
readonly resolvers: object;
|
|
1203
|
+
}, K extends keyof P["resolvers"]> = Extract<NonNullable<P["resolvers"][K]>, (...args: any[]) => unknown>;
|
|
1204
|
+
/**
|
|
1205
|
+
* Defines a parameter decorator whose value is computed per call by a resolver of the handler's
|
|
1206
|
+
* transport (RFC 0010). `T` (required) is the injected type, checked at build time against every
|
|
1207
|
+
* decorated parameter; `D` is the type of the data argument (none by default). The definition must
|
|
1208
|
+
* be an exported top-level `const`: the generated module imports it.
|
|
1209
|
+
*
|
|
1210
|
+
* ```ts
|
|
1211
|
+
* export const CurrentUser = createParamDecorator<User>({
|
|
1212
|
+
* http: ctx => userOf(ctx.state),
|
|
1213
|
+
* graphql: ctx => userOf(ctx.operation.state),
|
|
1214
|
+
* })
|
|
1215
|
+
*
|
|
1216
|
+
* @Get('/me')
|
|
1217
|
+
* me(@CurrentUser() user: User) { ... }
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
export declare function createParamDecorator<T, D = undefined>(resolvers: ParamResolvers<T, D>): ParamDecorator<T, D>;
|
|
1156
1221
|
/**
|
|
1157
1222
|
* ZNIGHT015: a provider factory threw or rejected.
|
|
1158
1223
|
*
|
|
@@ -1184,5 +1249,5 @@ export declare class ZnightAggregateError extends ZnightError {
|
|
|
1184
1249
|
* that were attached to it instead of being thrown. Empty when there are none.
|
|
1185
1250
|
*/
|
|
1186
1251
|
export declare function getSuppressedErrors(error: unknown): readonly unknown[];
|
|
1187
|
-
export type { ApplicationOptions, ApplicationState, ClassKey, ClassMarker, ConsoleLike, ConsoleLoggerOptions, CoreLogger, GraphQLOperationError, GraphQLOperationEvent, GraphQLOperationOutcome, GraphQLOperationTransport, GraphQLOperationType, HookEvent, HookListener, HookName, HookWrapper, HooksOptions, HotContext, HttpRequestEvent, InitOptions, InjectableOptions, InterceptMethodOptions, InterceptOptionsArgs, InterceptorClass, InterceptorLink, LogContext, LogLevel, ManagedInstance, MessageDeliveryEvent, MessagePublishEvent, MethodInterceptor, MethodInvocation, MethodMarker, OnReady, OnStart, OnStop, OpenedScope, ParameterMarker, Plugin, PluginContext, PluginHook, PluginOptions, PluginRootsContext, ProcessLike, ProviderKey, ProviderValues, ProvidesOptions, ScheduledRunAbortReason, ScheduledRunEvent, ScheduledRunOutcome, ScheduledRunTrigger, ScopeContext, ScopeValues, ShutdownOptions, ShutdownSignal, TypedToken, ZnightHooks };
|
|
1252
|
+
export type { ApplicationOptions, ApplicationState, ClassKey, ClassMarker, ConsoleLike, ConsoleLoggerOptions, CoreLogger, GraphQLOperationError, GraphQLOperationEvent, GraphQLOperationOutcome, GraphQLOperationTransport, GraphQLOperationType, HookEvent, HookListener, HookName, HookWrapper, HooksOptions, HotContext, HttpRequestEvent, InitOptions, InjectableOptions, InterceptMethodOptions, InterceptOptionsArgs, InterceptorClass, InterceptorLink, LogContext, LogLevel, ManagedInstance, MessageDeliveryEvent, MessagePublishEvent, MethodInterceptor, MethodInvocation, MethodMarker, OnReady, OnStart, OnStop, OpenedScope, ParamContexts, ParamDataArgs, ParamDecorator, ParamFunctionOf, ParamResolver, ParamResolverClass, ParamResolverFunction, ParamResolverOf, ParamResolvers, ParameterMarker, Plugin, PluginContext, PluginHook, PluginOptions, PluginRootsContext, ProcessLike, ProviderKey, ProviderValues, ProvidesOptions, ScheduledRunAbortReason, ScheduledRunEvent, ScheduledRunOutcome, ScheduledRunTrigger, ScopeContext, ScopeValues, ShutdownOptions, ShutdownSignal, TypedToken, ZnightHooks };
|
|
1188
1253
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -179,7 +179,7 @@ const ERROR_DESCRIPTIONS = {
|
|
|
179
179
|
ZNIGHT063: "Invalid GraphQL input: a type cannot be used in an input position",
|
|
180
180
|
ZNIGHT064: "Unresolvable abstract type: the members of a GraphQL union or interface cannot be told apart at runtime",
|
|
181
181
|
ZNIGHT065: "Invalid loader: not a Loader of a BatchLoad provider, or a batch returning the wrong number of values",
|
|
182
|
-
ZNIGHT066: "Invalid GraphQL setup: metadata, schema, GraphQLPlugin options or HttpPlugin missing",
|
|
182
|
+
ZNIGHT066: "Invalid GraphQL setup: metadata, schema, GraphQLPlugin options, driver or HttpPlugin missing",
|
|
183
183
|
ZNIGHT067: "Invalid GraphQL SDL: the schema-first .graphql files cannot be read, parsed or validated",
|
|
184
184
|
ZNIGHT068: "GraphQL schema mismatch: schema-first resolvers do not cover or conform to the SDL"
|
|
185
185
|
};
|
|
@@ -2236,11 +2236,11 @@ function positive(value, fallback, name) {
|
|
|
2236
2236
|
return value;
|
|
2237
2237
|
}
|
|
2238
2238
|
// src/decorators.ts
|
|
2239
|
-
function marker() {}
|
|
2240
|
-
const classMarker = marker;
|
|
2241
|
-
const methodMarker = marker;
|
|
2242
|
-
const parameterMarker = marker;
|
|
2243
|
-
const classOrMethodMarker = marker;
|
|
2239
|
+
function marker$1() {}
|
|
2240
|
+
const classMarker = marker$1;
|
|
2241
|
+
const methodMarker = marker$1;
|
|
2242
|
+
const parameterMarker$1 = marker$1;
|
|
2243
|
+
const classOrMethodMarker = marker$1;
|
|
2244
2244
|
/**
|
|
2245
2245
|
* Declares a provider class. Its constructor parameters are injected.
|
|
2246
2246
|
*
|
|
@@ -2275,11 +2275,11 @@ function Provides(_options) {
|
|
|
2275
2275
|
}
|
|
2276
2276
|
/** Injects the provider qualified with `name` (see {@link Named}). `name` is static. */
|
|
2277
2277
|
function Inject(_name) {
|
|
2278
|
-
return parameterMarker;
|
|
2278
|
+
return parameterMarker$1;
|
|
2279
2279
|
}
|
|
2280
2280
|
/** Marks a dependency as optional: `undefined` is injected when no provider exists. */
|
|
2281
2281
|
function Optional() {
|
|
2282
|
-
return parameterMarker;
|
|
2282
|
+
return parameterMarker$1;
|
|
2283
2283
|
}
|
|
2284
2284
|
/** Sets the qualifier of a provider class or `@Provides` method. `name` is static. */
|
|
2285
2285
|
function Named(_name) {
|
|
@@ -2301,11 +2301,38 @@ function Primary() {
|
|
|
2301
2301
|
function Intercept(_interceptor, ..._options) {
|
|
2302
2302
|
return methodMarker;
|
|
2303
2303
|
}
|
|
2304
|
+
// src/params.ts
|
|
2305
|
+
function marker() {}
|
|
2306
|
+
const parameterMarker = marker;
|
|
2307
|
+
/**
|
|
2308
|
+
* Defines a parameter decorator whose value is computed per call by a resolver of the handler's
|
|
2309
|
+
* transport (RFC 0010). `T` (required) is the injected type, checked at build time against every
|
|
2310
|
+
* decorated parameter; `D` is the type of the data argument (none by default). The definition must
|
|
2311
|
+
* be an exported top-level `const`: the generated module imports it.
|
|
2312
|
+
*
|
|
2313
|
+
* ```ts
|
|
2314
|
+
* export const CurrentUser = createParamDecorator<User>({
|
|
2315
|
+
* http: ctx => userOf(ctx.state),
|
|
2316
|
+
* graphql: ctx => userOf(ctx.operation.state),
|
|
2317
|
+
* })
|
|
2318
|
+
*
|
|
2319
|
+
* @Get('/me')
|
|
2320
|
+
* me(@CurrentUser() user: User) { ... }
|
|
2321
|
+
* ```
|
|
2322
|
+
*/
|
|
2323
|
+
function createParamDecorator(resolvers) {
|
|
2324
|
+
const decorator = ((..._data) => parameterMarker);
|
|
2325
|
+
Object.defineProperty(decorator, "resolvers", {
|
|
2326
|
+
value: Object.freeze({ ...resolvers }),
|
|
2327
|
+
enumerable: true
|
|
2328
|
+
});
|
|
2329
|
+
return decorator;
|
|
2330
|
+
}
|
|
2304
2331
|
// src/plugin.ts
|
|
2305
2332
|
/** Resolves {@link PluginOptions}: calls the function form with the container, returns the others as is. */
|
|
2306
2333
|
function resolveOptions(options, container) {
|
|
2307
2334
|
return typeof options === "function" ? options(container) : options;
|
|
2308
2335
|
}
|
|
2309
|
-
export { Application, Container, CoreTokens, DelegatingLogger, ERROR_DESCRIPTIONS, ErrorCode, Factory, Hooks, Inject, Injectable, InstantiationError, Intercept, METADATA_VERSION, Named, Optional, Primary, Provides, ZnightAggregateError, ZnightError, applyInterceptors, callLifecycleHook, createConsoleLogger, describeToken, displayName, disposeInstance, formatPath, getSuppressedErrors, hasLifecycleHook, interceptMethod, isDisposable, isProviderDefinition, resolveOptions, silentLogger, token };
|
|
2336
|
+
export { Application, Container, CoreTokens, DelegatingLogger, ERROR_DESCRIPTIONS, ErrorCode, Factory, Hooks, Inject, Injectable, InstantiationError, Intercept, METADATA_VERSION, Named, Optional, Primary, Provides, ZnightAggregateError, ZnightError, applyInterceptors, callLifecycleHook, createConsoleLogger, createParamDecorator, describeToken, displayName, disposeInstance, formatPath, getSuppressedErrors, hasLifecycleHook, interceptMethod, isDisposable, isProviderDefinition, resolveOptions, silentLogger, token };
|
|
2310
2337
|
|
|
2311
2338
|
//# sourceMappingURL=index.mjs.map
|