@makerx/graphql-core 3.0.0-beta.4 → 3.0.0-beta.6
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/User.d.cts +13 -0
- package/User.d.mts +13 -0
- package/context.d.cts +67 -0
- package/context.d.mts +67 -0
- package/index.d.cts +8 -0
- package/index.d.mts +8 -0
- package/logging.d.cts +82 -0
- package/logging.d.mts +82 -0
- package/logging.d.ts +2 -1
- package/logging.js +2 -1
- package/logging.js.map +1 -1
- package/logging.mjs +2 -1
- package/logging.mjs.map +1 -1
- package/package.json +26 -11
- package/request-info.d.cts +38 -0
- package/request-info.d.mts +38 -0
- package/schema-util.d.cts +9 -0
- package/schema-util.d.mts +9 -0
- package/shield.d.cts +122 -0
- package/shield.d.mts +122 -0
- package/subscriptions/context.d.cts +24 -0
- package/subscriptions/context.d.mts +24 -0
- package/subscriptions/index.d.cts +3 -0
- package/subscriptions/index.d.mts +3 -0
- package/subscriptions/server.d.cts +17 -0
- package/subscriptions/server.d.mts +17 -0
- package/subscriptions/server.js +6 -6
- package/subscriptions/server.js.map +1 -1
- package/subscriptions/server.mjs +6 -6
- package/subscriptions/server.mjs.map +1 -1
- package/subscriptions/utils.d.cts +10 -0
- package/subscriptions/utils.d.mts +10 -0
- package/type-utils.d.cts +4 -0
- package/type-utils.d.mts +4 -0
- package/unauthorised.d.cts +30 -0
- package/unauthorised.d.mts +30 -0
- package/utils.d.cts +3 -0
- package/utils.d.mts +3 -0
package/User.d.cts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JwtPayload } from './context';
|
|
2
|
+
export declare class User {
|
|
3
|
+
readonly token: string;
|
|
4
|
+
readonly claims: JwtPayload;
|
|
5
|
+
constructor(claims: JwtPayload, accessToken: string);
|
|
6
|
+
get email(): string | undefined;
|
|
7
|
+
get name(): string | undefined;
|
|
8
|
+
get id(): string;
|
|
9
|
+
get scopes(): string[];
|
|
10
|
+
get roles(): string[];
|
|
11
|
+
private first;
|
|
12
|
+
private firstOrThrow;
|
|
13
|
+
}
|
package/User.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JwtPayload } from './context.js';
|
|
2
|
+
export declare class User {
|
|
3
|
+
readonly token: string;
|
|
4
|
+
readonly claims: JwtPayload;
|
|
5
|
+
constructor(claims: JwtPayload, accessToken: string);
|
|
6
|
+
get email(): string | undefined;
|
|
7
|
+
get name(): string | undefined;
|
|
8
|
+
get id(): string;
|
|
9
|
+
get scopes(): string[];
|
|
10
|
+
get roles(): string[];
|
|
11
|
+
private first;
|
|
12
|
+
private firstOrThrow;
|
|
13
|
+
}
|
package/context.d.cts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { Request } from 'express';
|
|
3
|
+
import { type BaseRequestInfo } from './request-info';
|
|
4
|
+
import { User } from './User';
|
|
5
|
+
export interface GraphQLContext<TLogger extends Logger = Logger, TRequestInfo extends BaseRequestInfo = RequestInfo, TUser = User | undefined> {
|
|
6
|
+
logger: TLogger;
|
|
7
|
+
requestInfo: TRequestInfo;
|
|
8
|
+
user: TUser;
|
|
9
|
+
started: number;
|
|
10
|
+
}
|
|
11
|
+
export type AnyGraphqlContext = GraphQLContext<any, any, any>;
|
|
12
|
+
export interface LambdaContext {
|
|
13
|
+
functionName?: string;
|
|
14
|
+
awsRequestId?: string;
|
|
15
|
+
}
|
|
16
|
+
export type LambdaEvent = never;
|
|
17
|
+
export type LambdaRequestInfo = BaseRequestInfo & LambdaContext;
|
|
18
|
+
export type RequestInfo = BaseRequestInfo | LambdaRequestInfo;
|
|
19
|
+
type KnownKeys<T> = keyof {
|
|
20
|
+
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
21
|
+
};
|
|
22
|
+
export type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {});
|
|
23
|
+
export interface JwtPayload {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
name?: string | undefined;
|
|
26
|
+
email?: string | undefined;
|
|
27
|
+
given_name?: string | undefined;
|
|
28
|
+
family_name?: string | undefined;
|
|
29
|
+
preferred_username?: string | undefined;
|
|
30
|
+
upn?: string | undefined;
|
|
31
|
+
unique_name?: string | undefined;
|
|
32
|
+
oid?: string | undefined;
|
|
33
|
+
iss?: string | undefined;
|
|
34
|
+
sub?: string | undefined;
|
|
35
|
+
aud?: string | string[] | undefined;
|
|
36
|
+
roles?: string[] | undefined;
|
|
37
|
+
scp?: string | undefined;
|
|
38
|
+
exp?: number | undefined;
|
|
39
|
+
nbf?: number | undefined;
|
|
40
|
+
iat?: number | undefined;
|
|
41
|
+
jti?: string | undefined;
|
|
42
|
+
}
|
|
43
|
+
export type InferUserFromContext<TContext extends AnyGraphqlContext> = TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never;
|
|
44
|
+
export type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T;
|
|
45
|
+
export interface ContextInput {
|
|
46
|
+
req: Request;
|
|
47
|
+
claims?: JwtPayload;
|
|
48
|
+
context?: LambdaContext;
|
|
49
|
+
event?: LambdaEvent;
|
|
50
|
+
}
|
|
51
|
+
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
52
|
+
export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
|
|
53
|
+
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
54
|
+
export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
55
|
+
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
56
|
+
augmentRequestInfo?: AugmentRequestInfo;
|
|
57
|
+
claimsToLog?: string[];
|
|
58
|
+
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
59
|
+
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
60
|
+
} & ([User | undefined] extends [TUser] ? {
|
|
61
|
+
createUser?: CreateUser<TUser>;
|
|
62
|
+
} : {
|
|
63
|
+
createUser: CreateUser<TUser>;
|
|
64
|
+
});
|
|
65
|
+
export declare const createContextFactory: <TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger>(config: CreateContextConfig<TUser, TAugment, TLogger>) => CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment>;
|
|
66
|
+
export declare const defaultCreateUser: CreateUser<User | undefined>;
|
|
67
|
+
export {};
|
package/context.d.mts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { Request } from 'express';
|
|
3
|
+
import { type BaseRequestInfo } from './request-info.js';
|
|
4
|
+
import { User } from './User.js';
|
|
5
|
+
export interface GraphQLContext<TLogger extends Logger = Logger, TRequestInfo extends BaseRequestInfo = RequestInfo, TUser = User | undefined> {
|
|
6
|
+
logger: TLogger;
|
|
7
|
+
requestInfo: TRequestInfo;
|
|
8
|
+
user: TUser;
|
|
9
|
+
started: number;
|
|
10
|
+
}
|
|
11
|
+
export type AnyGraphqlContext = GraphQLContext<any, any, any>;
|
|
12
|
+
export interface LambdaContext {
|
|
13
|
+
functionName?: string;
|
|
14
|
+
awsRequestId?: string;
|
|
15
|
+
}
|
|
16
|
+
export type LambdaEvent = never;
|
|
17
|
+
export type LambdaRequestInfo = BaseRequestInfo & LambdaContext;
|
|
18
|
+
export type RequestInfo = BaseRequestInfo | LambdaRequestInfo;
|
|
19
|
+
type KnownKeys<T> = keyof {
|
|
20
|
+
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
21
|
+
};
|
|
22
|
+
export type RequestInfoLogKey = KnownKeys<BaseRequestInfo> | keyof LambdaContext | (string & {});
|
|
23
|
+
export interface JwtPayload {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
name?: string | undefined;
|
|
26
|
+
email?: string | undefined;
|
|
27
|
+
given_name?: string | undefined;
|
|
28
|
+
family_name?: string | undefined;
|
|
29
|
+
preferred_username?: string | undefined;
|
|
30
|
+
upn?: string | undefined;
|
|
31
|
+
unique_name?: string | undefined;
|
|
32
|
+
oid?: string | undefined;
|
|
33
|
+
iss?: string | undefined;
|
|
34
|
+
sub?: string | undefined;
|
|
35
|
+
aud?: string | string[] | undefined;
|
|
36
|
+
roles?: string[] | undefined;
|
|
37
|
+
scp?: string | undefined;
|
|
38
|
+
exp?: number | undefined;
|
|
39
|
+
nbf?: number | undefined;
|
|
40
|
+
iat?: number | undefined;
|
|
41
|
+
jti?: string | undefined;
|
|
42
|
+
}
|
|
43
|
+
export type InferUserFromContext<TContext extends AnyGraphqlContext> = TContext extends GraphQLContext<any, any, infer TUser> ? TUser : never;
|
|
44
|
+
export type CreateUser<T = User | undefined> = (input: Omit<ContextInput, 'createUser'>) => Promise<T> | T;
|
|
45
|
+
export interface ContextInput {
|
|
46
|
+
req: Request;
|
|
47
|
+
claims?: JwtPayload;
|
|
48
|
+
context?: LambdaContext;
|
|
49
|
+
event?: LambdaEvent;
|
|
50
|
+
}
|
|
51
|
+
export type CreateContext<TContext = GraphQLContext> = (input: ContextInput) => Promise<TContext>;
|
|
52
|
+
export type CreateRequestLogger<TUser = User | undefined, TLogger extends Logger = Logger> = (requestMetadata: Record<string, unknown>, user: TUser) => TLogger;
|
|
53
|
+
export type AugmentRequestInfo = (input: ContextInput) => Record<string, unknown>;
|
|
54
|
+
export type CreateContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
55
|
+
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
56
|
+
augmentRequestInfo?: AugmentRequestInfo;
|
|
57
|
+
claimsToLog?: string[];
|
|
58
|
+
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
59
|
+
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
60
|
+
} & ([User | undefined] extends [TUser] ? {
|
|
61
|
+
createUser?: CreateUser<TUser>;
|
|
62
|
+
} : {
|
|
63
|
+
createUser: CreateUser<TUser>;
|
|
64
|
+
});
|
|
65
|
+
export declare const createContextFactory: <TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger>(config: CreateContextConfig<TUser, TAugment, TLogger>) => CreateContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment>;
|
|
66
|
+
export declare const defaultCreateUser: CreateUser<User | undefined>;
|
|
67
|
+
export {};
|
package/index.d.cts
ADDED
package/index.d.mts
ADDED
package/logging.d.cts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { ExecutionArgs, GraphQLFormattedError } from 'graphql';
|
|
3
|
+
import { OperationTypeNode } from 'graphql';
|
|
4
|
+
import type { ExecutionResult } from 'graphql-ws';
|
|
5
|
+
import type { GraphQLContext } from './context';
|
|
6
|
+
type LogFunction = Logger['info'];
|
|
7
|
+
export type LoggerLogFunctions<T extends Logger> = {
|
|
8
|
+
[Property in keyof T]: LogFunction;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Info for logging a GraphQL operation in a consistent format with the option of including any additional data.
|
|
12
|
+
*/
|
|
13
|
+
export interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {
|
|
14
|
+
/**
|
|
15
|
+
* The message to log, defaults to 'GraphQL operation'.
|
|
16
|
+
*/
|
|
17
|
+
message?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The timestamp when the operation started, if supplied, the duration will be logged.
|
|
20
|
+
*/
|
|
21
|
+
started?: number;
|
|
22
|
+
/**
|
|
23
|
+
* The type of GraphQL operation.
|
|
24
|
+
*/
|
|
25
|
+
type?: OperationTypeNode | null;
|
|
26
|
+
/**
|
|
27
|
+
* The name of the GraphQL operation.
|
|
28
|
+
*/
|
|
29
|
+
operationName?: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* The formatted GraphQL query.
|
|
32
|
+
*/
|
|
33
|
+
query?: string | null;
|
|
34
|
+
/**
|
|
35
|
+
* The GraphQL variables.
|
|
36
|
+
*/
|
|
37
|
+
variables?: Record<string, unknown> | null;
|
|
38
|
+
/**
|
|
39
|
+
* The result of the GraphQL operation.
|
|
40
|
+
* Generally, we don't log the data or extensions, just errors.
|
|
41
|
+
*/
|
|
42
|
+
result?: {
|
|
43
|
+
data?: Record<string, unknown> | null;
|
|
44
|
+
errors?: readonly GraphQLFormattedError[] | null;
|
|
45
|
+
extensions?: Record<string, unknown> | null;
|
|
46
|
+
hasNext?: boolean;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Whether the operation is an introspection query.
|
|
50
|
+
*/
|
|
51
|
+
isIntrospectionQuery?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the operation is part of an incremental response.
|
|
54
|
+
*/
|
|
55
|
+
isIncrementalResponse?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the operation is a subsequent payload of an incremental response.
|
|
58
|
+
*/
|
|
59
|
+
isSubsequentPayload?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The logger to use.
|
|
62
|
+
*/
|
|
63
|
+
logger: TLogger;
|
|
64
|
+
/**
|
|
65
|
+
* The logger function to use, defaults to 'info'.
|
|
66
|
+
*/
|
|
67
|
+
logLevel?: keyof LoggerLogFunctions<TLogger>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Logs a GraphQL operation in a consistent format with the option of including any additional data.
|
|
71
|
+
* Top level and result entries with null or undefined values will be omitted.
|
|
72
|
+
*/
|
|
73
|
+
export declare const logGraphQLOperation: <TLogger extends Logger = Logger>({ message, started, type, operationName, query, variables, result, logger, logLevel, ...rest }: GraphQLLogOperationInfo<TLogger>) => void;
|
|
74
|
+
export declare const logSubscriptionOperation: <TLogger extends Logger = Logger>({ id, args, result, message, logLevel, resolveLogger, }: {
|
|
75
|
+
id?: string;
|
|
76
|
+
args: ExecutionArgs;
|
|
77
|
+
result?: ExecutionResult;
|
|
78
|
+
message?: string;
|
|
79
|
+
logLevel?: keyof LoggerLogFunctions<TLogger>;
|
|
80
|
+
resolveLogger?: (context: GraphQLContext) => TLogger;
|
|
81
|
+
}) => void;
|
|
82
|
+
export {};
|
package/logging.d.mts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { ExecutionArgs, GraphQLFormattedError } from 'graphql';
|
|
3
|
+
import { OperationTypeNode } from 'graphql';
|
|
4
|
+
import type { ExecutionResult } from 'graphql-ws';
|
|
5
|
+
import type { GraphQLContext } from './context.js';
|
|
6
|
+
type LogFunction = Logger['info'];
|
|
7
|
+
export type LoggerLogFunctions<T extends Logger> = {
|
|
8
|
+
[Property in keyof T]: LogFunction;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Info for logging a GraphQL operation in a consistent format with the option of including any additional data.
|
|
12
|
+
*/
|
|
13
|
+
export interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {
|
|
14
|
+
/**
|
|
15
|
+
* The message to log, defaults to 'GraphQL operation'.
|
|
16
|
+
*/
|
|
17
|
+
message?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The timestamp when the operation started, if supplied, the duration will be logged.
|
|
20
|
+
*/
|
|
21
|
+
started?: number;
|
|
22
|
+
/**
|
|
23
|
+
* The type of GraphQL operation.
|
|
24
|
+
*/
|
|
25
|
+
type?: OperationTypeNode | null;
|
|
26
|
+
/**
|
|
27
|
+
* The name of the GraphQL operation.
|
|
28
|
+
*/
|
|
29
|
+
operationName?: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* The formatted GraphQL query.
|
|
32
|
+
*/
|
|
33
|
+
query?: string | null;
|
|
34
|
+
/**
|
|
35
|
+
* The GraphQL variables.
|
|
36
|
+
*/
|
|
37
|
+
variables?: Record<string, unknown> | null;
|
|
38
|
+
/**
|
|
39
|
+
* The result of the GraphQL operation.
|
|
40
|
+
* Generally, we don't log the data or extensions, just errors.
|
|
41
|
+
*/
|
|
42
|
+
result?: {
|
|
43
|
+
data?: Record<string, unknown> | null;
|
|
44
|
+
errors?: readonly GraphQLFormattedError[] | null;
|
|
45
|
+
extensions?: Record<string, unknown> | null;
|
|
46
|
+
hasNext?: boolean;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Whether the operation is an introspection query.
|
|
50
|
+
*/
|
|
51
|
+
isIntrospectionQuery?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether the operation is part of an incremental response.
|
|
54
|
+
*/
|
|
55
|
+
isIncrementalResponse?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the operation is a subsequent payload of an incremental response.
|
|
58
|
+
*/
|
|
59
|
+
isSubsequentPayload?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The logger to use.
|
|
62
|
+
*/
|
|
63
|
+
logger: TLogger;
|
|
64
|
+
/**
|
|
65
|
+
* The logger function to use, defaults to 'info'.
|
|
66
|
+
*/
|
|
67
|
+
logLevel?: keyof LoggerLogFunctions<TLogger>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Logs a GraphQL operation in a consistent format with the option of including any additional data.
|
|
71
|
+
* Top level and result entries with null or undefined values will be omitted.
|
|
72
|
+
*/
|
|
73
|
+
export declare const logGraphQLOperation: <TLogger extends Logger = Logger>({ message, started, type, operationName, query, variables, result, logger, logLevel, ...rest }: GraphQLLogOperationInfo<TLogger>) => void;
|
|
74
|
+
export declare const logSubscriptionOperation: <TLogger extends Logger = Logger>({ id, args, result, message, logLevel, resolveLogger, }: {
|
|
75
|
+
id?: string;
|
|
76
|
+
args: ExecutionArgs;
|
|
77
|
+
result?: ExecutionResult;
|
|
78
|
+
message?: string;
|
|
79
|
+
logLevel?: keyof LoggerLogFunctions<TLogger>;
|
|
80
|
+
resolveLogger?: (context: GraphQLContext) => TLogger;
|
|
81
|
+
}) => void;
|
|
82
|
+
export {};
|
package/logging.d.ts
CHANGED
|
@@ -71,7 +71,8 @@ export interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extend
|
|
|
71
71
|
* Top level and result entries with null or undefined values will be omitted.
|
|
72
72
|
*/
|
|
73
73
|
export declare const logGraphQLOperation: <TLogger extends Logger = Logger>({ message, started, type, operationName, query, variables, result, logger, logLevel, ...rest }: GraphQLLogOperationInfo<TLogger>) => void;
|
|
74
|
-
export declare const logSubscriptionOperation: <TLogger extends Logger = Logger>({ args, result, message, logLevel, resolveLogger, }: {
|
|
74
|
+
export declare const logSubscriptionOperation: <TLogger extends Logger = Logger>({ id, args, result, message, logLevel, resolveLogger, }: {
|
|
75
|
+
id?: string;
|
|
75
76
|
args: ExecutionArgs;
|
|
76
77
|
result?: ExecutionResult;
|
|
77
78
|
message?: string;
|
package/logging.js
CHANGED
|
@@ -24,7 +24,7 @@ const logGraphQLOperation = ({ message = 'GraphQL operation', started, type, ope
|
|
|
24
24
|
...compat.omitBy(rest, utils.isNil),
|
|
25
25
|
}, utils.isNil));
|
|
26
26
|
};
|
|
27
|
-
const logSubscriptionOperation = ({ args, result, message, logLevel, resolveLogger, }) => {
|
|
27
|
+
const logSubscriptionOperation = ({ id, args, result, message, logLevel, resolveLogger, }) => {
|
|
28
28
|
const logger = resolveLogger
|
|
29
29
|
? resolveLogger(args.contextValue)
|
|
30
30
|
: args.contextValue.logger;
|
|
@@ -35,6 +35,7 @@ const logSubscriptionOperation = ({ args, result, message, logLevel, resolveLogg
|
|
|
35
35
|
logGraphQLOperation({
|
|
36
36
|
message,
|
|
37
37
|
type: graphql.OperationTypeNode.SUBSCRIPTION,
|
|
38
|
+
subscriptionId: id,
|
|
38
39
|
operationName,
|
|
39
40
|
query: graphql.print(document),
|
|
40
41
|
variables: variableValues,
|
package/logging.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.js","sources":["../src/logging.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { isLocalDev } from '@makerx/node-common'\nimport type { ExecutionArgs, GraphQLFormattedError } from 'graphql'\nimport { OperationTypeNode, print } from 'graphql'\nimport type { ExecutionResult } from 'graphql-ws'\nimport { omitBy } from 'es-toolkit/compat'\nimport type { GraphQLContext } from './context'\nimport { isIntrospectionQuery, isNil } from './utils'\n\ntype LogFunction = Logger['info']\nexport type LoggerLogFunctions<T extends Logger> = {\n [Property in keyof T]: LogFunction\n}\n\n/**\n * Info for logging a GraphQL operation in a consistent format with the option of including any additional data.\n */\nexport interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {\n /**\n * The message to log, defaults to 'GraphQL operation'.\n */\n message?: string\n /**\n * The timestamp when the operation started, if supplied, the duration will be logged.\n */\n started?: number\n /**\n * The type of GraphQL operation.\n */\n type?: OperationTypeNode | null\n /**\n * The name of the GraphQL operation.\n */\n operationName?: string | null\n /**\n * The formatted GraphQL query.\n */\n query?: string | null\n /**\n * The GraphQL variables.\n */\n variables?: Record<string, unknown> | null\n /**\n * The result of the GraphQL operation.\n * Generally, we don't log the data or extensions, just errors.\n */\n result?: {\n data?: Record<string, unknown> | null\n errors?: readonly GraphQLFormattedError[] | null\n extensions?: Record<string, unknown> | null\n hasNext?: boolean\n }\n /**\n * Whether the operation is an introspection query.\n */\n isIntrospectionQuery?: boolean\n /**\n * Whether the operation is part of an incremental response.\n */\n isIncrementalResponse?: boolean\n /**\n * Whether the operation is a subsequent payload of an incremental response.\n */\n isSubsequentPayload?: boolean\n /**\n * The logger to use.\n */\n logger: TLogger\n /**\n * The logger function to use, defaults to 'info'.\n */\n logLevel?: keyof LoggerLogFunctions<TLogger>\n}\n\n/**\n * Logs a GraphQL operation in a consistent format with the option of including any additional data.\n * Top level and result entries with null or undefined values will be omitted.\n */\nexport const logGraphQLOperation = <TLogger extends Logger = Logger>({\n message = 'GraphQL operation',\n started,\n type,\n operationName,\n query,\n variables,\n result,\n logger,\n logLevel = 'info',\n ...rest\n}: GraphQLLogOperationInfo<TLogger>) => {\n const isIntrospection = query && isIntrospectionQuery(query)\n if (isLocalDev && isIntrospection) return\n logger[logLevel as keyof Logger](\n message,\n omitBy(\n {\n type,\n operationName,\n query,\n variables: variables && Object.keys(variables).length > 0 ? variables : undefined,\n duration: started ? Date.now() - started : undefined,\n result: result ? omitBy(result, isNil) : undefined,\n isIntrospectionQuery: isIntrospection || undefined,\n ...omitBy(rest, isNil),\n },\n isNil,\n ),\n )\n}\n\nexport const logSubscriptionOperation = <TLogger extends Logger = Logger>({\n args,\n result,\n message,\n logLevel,\n resolveLogger,\n}: {\n args: ExecutionArgs\n result?: ExecutionResult\n message?: string\n logLevel?: keyof LoggerLogFunctions<TLogger>\n resolveLogger?: (context: GraphQLContext) => TLogger\n}) => {\n const logger = resolveLogger\n ? resolveLogger(args.contextValue as GraphQLContext)\n : ((args.contextValue as GraphQLContext).logger as TLogger)\n if (!logger) return\n\n const { operationName, variableValues, document } = args\n const { data, ...resultWithoutData } = result ?? {}\n\n logGraphQLOperation({\n message,\n type: OperationTypeNode.SUBSCRIPTION,\n operationName,\n query: print(document),\n variables: variableValues,\n result: resultWithoutData,\n logger,\n logLevel,\n })\n}\n"],"names":["isIntrospectionQuery","isLocalDev","omitBy","isNil","OperationTypeNode","print"],"mappings":";;;;;;;AA0EA;;;AAGG;AACI,MAAM,mBAAmB,GAAG,CAAkC,EACnE,OAAO,GAAG,mBAAmB,EAC7B,OAAO,EACP,IAAI,EACJ,aAAa,EACb,KAAK,EACL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,GAAG,MAAM,EACjB,GAAG,IAAI,EAC0B,KAAI;IACrC,MAAM,eAAe,GAAG,KAAK,IAAIA,0BAAoB,CAAC,KAAK,CAAC;IAC5D,IAAIC,qBAAU,IAAI,eAAe;QAAE;AACnC,IAAA,MAAM,CAAC,QAAwB,CAAC,CAC9B,OAAO,EACPC,aAAM,CACJ;QACE,IAAI;QACJ,aAAa;QACb,KAAK;QACL,SAAS,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS;AACjF,QAAA,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS;AACpD,QAAA,MAAM,EAAE,MAAM,GAAGA,aAAM,CAAC,MAAM,EAAEC,WAAK,CAAC,GAAG,SAAS;QAClD,oBAAoB,EAAE,eAAe,IAAI,SAAS;AAClD,QAAA,GAAGD,aAAM,CAAC,IAAI,EAAEC,WAAK,CAAC;KACvB,EACDA,WAAK,CACN,CACF;AACH;AAEO,MAAM,wBAAwB,GAAG,CAAkC,EACxE,IAAI,EACJ,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,
|
|
1
|
+
{"version":3,"file":"logging.js","sources":["../src/logging.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { isLocalDev } from '@makerx/node-common'\nimport type { ExecutionArgs, GraphQLFormattedError } from 'graphql'\nimport { OperationTypeNode, print } from 'graphql'\nimport type { ExecutionResult } from 'graphql-ws'\nimport { omitBy } from 'es-toolkit/compat'\nimport type { GraphQLContext } from './context'\nimport { isIntrospectionQuery, isNil } from './utils'\n\ntype LogFunction = Logger['info']\nexport type LoggerLogFunctions<T extends Logger> = {\n [Property in keyof T]: LogFunction\n}\n\n/**\n * Info for logging a GraphQL operation in a consistent format with the option of including any additional data.\n */\nexport interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {\n /**\n * The message to log, defaults to 'GraphQL operation'.\n */\n message?: string\n /**\n * The timestamp when the operation started, if supplied, the duration will be logged.\n */\n started?: number\n /**\n * The type of GraphQL operation.\n */\n type?: OperationTypeNode | null\n /**\n * The name of the GraphQL operation.\n */\n operationName?: string | null\n /**\n * The formatted GraphQL query.\n */\n query?: string | null\n /**\n * The GraphQL variables.\n */\n variables?: Record<string, unknown> | null\n /**\n * The result of the GraphQL operation.\n * Generally, we don't log the data or extensions, just errors.\n */\n result?: {\n data?: Record<string, unknown> | null\n errors?: readonly GraphQLFormattedError[] | null\n extensions?: Record<string, unknown> | null\n hasNext?: boolean\n }\n /**\n * Whether the operation is an introspection query.\n */\n isIntrospectionQuery?: boolean\n /**\n * Whether the operation is part of an incremental response.\n */\n isIncrementalResponse?: boolean\n /**\n * Whether the operation is a subsequent payload of an incremental response.\n */\n isSubsequentPayload?: boolean\n /**\n * The logger to use.\n */\n logger: TLogger\n /**\n * The logger function to use, defaults to 'info'.\n */\n logLevel?: keyof LoggerLogFunctions<TLogger>\n}\n\n/**\n * Logs a GraphQL operation in a consistent format with the option of including any additional data.\n * Top level and result entries with null or undefined values will be omitted.\n */\nexport const logGraphQLOperation = <TLogger extends Logger = Logger>({\n message = 'GraphQL operation',\n started,\n type,\n operationName,\n query,\n variables,\n result,\n logger,\n logLevel = 'info',\n ...rest\n}: GraphQLLogOperationInfo<TLogger>) => {\n const isIntrospection = query && isIntrospectionQuery(query)\n if (isLocalDev && isIntrospection) return\n logger[logLevel as keyof Logger](\n message,\n omitBy(\n {\n type,\n operationName,\n query,\n variables: variables && Object.keys(variables).length > 0 ? variables : undefined,\n duration: started ? Date.now() - started : undefined,\n result: result ? omitBy(result, isNil) : undefined,\n isIntrospectionQuery: isIntrospection || undefined,\n ...omitBy(rest, isNil),\n },\n isNil,\n ),\n )\n}\n\nexport const logSubscriptionOperation = <TLogger extends Logger = Logger>({\n id,\n args,\n result,\n message,\n logLevel,\n resolveLogger,\n}: {\n id?: string\n args: ExecutionArgs\n result?: ExecutionResult\n message?: string\n logLevel?: keyof LoggerLogFunctions<TLogger>\n resolveLogger?: (context: GraphQLContext) => TLogger\n}) => {\n const logger = resolveLogger\n ? resolveLogger(args.contextValue as GraphQLContext)\n : ((args.contextValue as GraphQLContext).logger as TLogger)\n if (!logger) return\n\n const { operationName, variableValues, document } = args\n const { data, ...resultWithoutData } = result ?? {}\n\n logGraphQLOperation({\n message,\n type: OperationTypeNode.SUBSCRIPTION,\n subscriptionId: id,\n operationName,\n query: print(document),\n variables: variableValues,\n result: resultWithoutData,\n logger,\n logLevel,\n })\n}\n"],"names":["isIntrospectionQuery","isLocalDev","omitBy","isNil","OperationTypeNode","print"],"mappings":";;;;;;;AA0EA;;;AAGG;AACI,MAAM,mBAAmB,GAAG,CAAkC,EACnE,OAAO,GAAG,mBAAmB,EAC7B,OAAO,EACP,IAAI,EACJ,aAAa,EACb,KAAK,EACL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,GAAG,MAAM,EACjB,GAAG,IAAI,EAC0B,KAAI;IACrC,MAAM,eAAe,GAAG,KAAK,IAAIA,0BAAoB,CAAC,KAAK,CAAC;IAC5D,IAAIC,qBAAU,IAAI,eAAe;QAAE;AACnC,IAAA,MAAM,CAAC,QAAwB,CAAC,CAC9B,OAAO,EACPC,aAAM,CACJ;QACE,IAAI;QACJ,aAAa;QACb,KAAK;QACL,SAAS,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS;AACjF,QAAA,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS;AACpD,QAAA,MAAM,EAAE,MAAM,GAAGA,aAAM,CAAC,MAAM,EAAEC,WAAK,CAAC,GAAG,SAAS;QAClD,oBAAoB,EAAE,eAAe,IAAI,SAAS;AAClD,QAAA,GAAGD,aAAM,CAAC,IAAI,EAAEC,WAAK,CAAC;KACvB,EACDA,WAAK,CACN,CACF;AACH;AAEO,MAAM,wBAAwB,GAAG,CAAkC,EACxE,EAAE,EACF,IAAI,EACJ,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,GAQd,KAAI;IACH,MAAM,MAAM,GAAG;AACb,UAAE,aAAa,CAAC,IAAI,CAAC,YAA8B;AACnD,UAAI,IAAI,CAAC,YAA+B,CAAC,MAAkB;AAC7D,IAAA,IAAI,CAAC,MAAM;QAAE;IAEb,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,IAAI;IACxD,MAAM,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,GAAG,MAAM,IAAI,EAAE;AAEnD,IAAA,mBAAmB,CAAC;QAClB,OAAO;QACP,IAAI,EAAEC,yBAAiB,CAAC,YAAY;AACpC,QAAA,cAAc,EAAE,EAAE;QAClB,aAAa;AACb,QAAA,KAAK,EAAEC,aAAK,CAAC,QAAQ,CAAC;AACtB,QAAA,SAAS,EAAE,cAAc;AACzB,QAAA,MAAM,EAAE,iBAAiB;QACzB,MAAM;QACN,QAAQ;AACT,KAAA,CAAC;AACJ;;;;;"}
|
package/logging.mjs
CHANGED
|
@@ -22,7 +22,7 @@ const logGraphQLOperation = ({ message = 'GraphQL operation', started, type, ope
|
|
|
22
22
|
...omitBy(rest, isNil),
|
|
23
23
|
}, isNil));
|
|
24
24
|
};
|
|
25
|
-
const logSubscriptionOperation = ({ args, result, message, logLevel, resolveLogger, }) => {
|
|
25
|
+
const logSubscriptionOperation = ({ id, args, result, message, logLevel, resolveLogger, }) => {
|
|
26
26
|
const logger = resolveLogger
|
|
27
27
|
? resolveLogger(args.contextValue)
|
|
28
28
|
: args.contextValue.logger;
|
|
@@ -33,6 +33,7 @@ const logSubscriptionOperation = ({ args, result, message, logLevel, resolveLogg
|
|
|
33
33
|
logGraphQLOperation({
|
|
34
34
|
message,
|
|
35
35
|
type: OperationTypeNode.SUBSCRIPTION,
|
|
36
|
+
subscriptionId: id,
|
|
36
37
|
operationName,
|
|
37
38
|
query: print(document),
|
|
38
39
|
variables: variableValues,
|
package/logging.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logging.mjs","sources":["../src/logging.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { isLocalDev } from '@makerx/node-common'\nimport type { ExecutionArgs, GraphQLFormattedError } from 'graphql'\nimport { OperationTypeNode, print } from 'graphql'\nimport type { ExecutionResult } from 'graphql-ws'\nimport { omitBy } from 'es-toolkit/compat'\nimport type { GraphQLContext } from './context'\nimport { isIntrospectionQuery, isNil } from './utils'\n\ntype LogFunction = Logger['info']\nexport type LoggerLogFunctions<T extends Logger> = {\n [Property in keyof T]: LogFunction\n}\n\n/**\n * Info for logging a GraphQL operation in a consistent format with the option of including any additional data.\n */\nexport interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {\n /**\n * The message to log, defaults to 'GraphQL operation'.\n */\n message?: string\n /**\n * The timestamp when the operation started, if supplied, the duration will be logged.\n */\n started?: number\n /**\n * The type of GraphQL operation.\n */\n type?: OperationTypeNode | null\n /**\n * The name of the GraphQL operation.\n */\n operationName?: string | null\n /**\n * The formatted GraphQL query.\n */\n query?: string | null\n /**\n * The GraphQL variables.\n */\n variables?: Record<string, unknown> | null\n /**\n * The result of the GraphQL operation.\n * Generally, we don't log the data or extensions, just errors.\n */\n result?: {\n data?: Record<string, unknown> | null\n errors?: readonly GraphQLFormattedError[] | null\n extensions?: Record<string, unknown> | null\n hasNext?: boolean\n }\n /**\n * Whether the operation is an introspection query.\n */\n isIntrospectionQuery?: boolean\n /**\n * Whether the operation is part of an incremental response.\n */\n isIncrementalResponse?: boolean\n /**\n * Whether the operation is a subsequent payload of an incremental response.\n */\n isSubsequentPayload?: boolean\n /**\n * The logger to use.\n */\n logger: TLogger\n /**\n * The logger function to use, defaults to 'info'.\n */\n logLevel?: keyof LoggerLogFunctions<TLogger>\n}\n\n/**\n * Logs a GraphQL operation in a consistent format with the option of including any additional data.\n * Top level and result entries with null or undefined values will be omitted.\n */\nexport const logGraphQLOperation = <TLogger extends Logger = Logger>({\n message = 'GraphQL operation',\n started,\n type,\n operationName,\n query,\n variables,\n result,\n logger,\n logLevel = 'info',\n ...rest\n}: GraphQLLogOperationInfo<TLogger>) => {\n const isIntrospection = query && isIntrospectionQuery(query)\n if (isLocalDev && isIntrospection) return\n logger[logLevel as keyof Logger](\n message,\n omitBy(\n {\n type,\n operationName,\n query,\n variables: variables && Object.keys(variables).length > 0 ? variables : undefined,\n duration: started ? Date.now() - started : undefined,\n result: result ? omitBy(result, isNil) : undefined,\n isIntrospectionQuery: isIntrospection || undefined,\n ...omitBy(rest, isNil),\n },\n isNil,\n ),\n )\n}\n\nexport const logSubscriptionOperation = <TLogger extends Logger = Logger>({\n args,\n result,\n message,\n logLevel,\n resolveLogger,\n}: {\n args: ExecutionArgs\n result?: ExecutionResult\n message?: string\n logLevel?: keyof LoggerLogFunctions<TLogger>\n resolveLogger?: (context: GraphQLContext) => TLogger\n}) => {\n const logger = resolveLogger\n ? resolveLogger(args.contextValue as GraphQLContext)\n : ((args.contextValue as GraphQLContext).logger as TLogger)\n if (!logger) return\n\n const { operationName, variableValues, document } = args\n const { data, ...resultWithoutData } = result ?? {}\n\n logGraphQLOperation({\n message,\n type: OperationTypeNode.SUBSCRIPTION,\n operationName,\n query: print(document),\n variables: variableValues,\n result: resultWithoutData,\n logger,\n logLevel,\n })\n}\n"],"names":[],"mappings":";;;;;AA0EA;;;AAGG;AACI,MAAM,mBAAmB,GAAG,CAAkC,EACnE,OAAO,GAAG,mBAAmB,EAC7B,OAAO,EACP,IAAI,EACJ,aAAa,EACb,KAAK,EACL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,GAAG,MAAM,EACjB,GAAG,IAAI,EAC0B,KAAI;IACrC,MAAM,eAAe,GAAG,KAAK,IAAI,oBAAoB,CAAC,KAAK,CAAC;IAC5D,IAAI,UAAU,IAAI,eAAe;QAAE;AACnC,IAAA,MAAM,CAAC,QAAwB,CAAC,CAC9B,OAAO,EACP,MAAM,CACJ;QACE,IAAI;QACJ,aAAa;QACb,KAAK;QACL,SAAS,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS;AACjF,QAAA,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS;AACpD,QAAA,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS;QAClD,oBAAoB,EAAE,eAAe,IAAI,SAAS;AAClD,QAAA,GAAG,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;KACvB,EACD,KAAK,CACN,CACF;AACH;AAEO,MAAM,wBAAwB,GAAG,CAAkC,EACxE,IAAI,EACJ,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,
|
|
1
|
+
{"version":3,"file":"logging.mjs","sources":["../src/logging.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { isLocalDev } from '@makerx/node-common'\nimport type { ExecutionArgs, GraphQLFormattedError } from 'graphql'\nimport { OperationTypeNode, print } from 'graphql'\nimport type { ExecutionResult } from 'graphql-ws'\nimport { omitBy } from 'es-toolkit/compat'\nimport type { GraphQLContext } from './context'\nimport { isIntrospectionQuery, isNil } from './utils'\n\ntype LogFunction = Logger['info']\nexport type LoggerLogFunctions<T extends Logger> = {\n [Property in keyof T]: LogFunction\n}\n\n/**\n * Info for logging a GraphQL operation in a consistent format with the option of including any additional data.\n */\nexport interface GraphQLLogOperationInfo<TLogger extends Logger = Logger> extends Record<string, unknown> {\n /**\n * The message to log, defaults to 'GraphQL operation'.\n */\n message?: string\n /**\n * The timestamp when the operation started, if supplied, the duration will be logged.\n */\n started?: number\n /**\n * The type of GraphQL operation.\n */\n type?: OperationTypeNode | null\n /**\n * The name of the GraphQL operation.\n */\n operationName?: string | null\n /**\n * The formatted GraphQL query.\n */\n query?: string | null\n /**\n * The GraphQL variables.\n */\n variables?: Record<string, unknown> | null\n /**\n * The result of the GraphQL operation.\n * Generally, we don't log the data or extensions, just errors.\n */\n result?: {\n data?: Record<string, unknown> | null\n errors?: readonly GraphQLFormattedError[] | null\n extensions?: Record<string, unknown> | null\n hasNext?: boolean\n }\n /**\n * Whether the operation is an introspection query.\n */\n isIntrospectionQuery?: boolean\n /**\n * Whether the operation is part of an incremental response.\n */\n isIncrementalResponse?: boolean\n /**\n * Whether the operation is a subsequent payload of an incremental response.\n */\n isSubsequentPayload?: boolean\n /**\n * The logger to use.\n */\n logger: TLogger\n /**\n * The logger function to use, defaults to 'info'.\n */\n logLevel?: keyof LoggerLogFunctions<TLogger>\n}\n\n/**\n * Logs a GraphQL operation in a consistent format with the option of including any additional data.\n * Top level and result entries with null or undefined values will be omitted.\n */\nexport const logGraphQLOperation = <TLogger extends Logger = Logger>({\n message = 'GraphQL operation',\n started,\n type,\n operationName,\n query,\n variables,\n result,\n logger,\n logLevel = 'info',\n ...rest\n}: GraphQLLogOperationInfo<TLogger>) => {\n const isIntrospection = query && isIntrospectionQuery(query)\n if (isLocalDev && isIntrospection) return\n logger[logLevel as keyof Logger](\n message,\n omitBy(\n {\n type,\n operationName,\n query,\n variables: variables && Object.keys(variables).length > 0 ? variables : undefined,\n duration: started ? Date.now() - started : undefined,\n result: result ? omitBy(result, isNil) : undefined,\n isIntrospectionQuery: isIntrospection || undefined,\n ...omitBy(rest, isNil),\n },\n isNil,\n ),\n )\n}\n\nexport const logSubscriptionOperation = <TLogger extends Logger = Logger>({\n id,\n args,\n result,\n message,\n logLevel,\n resolveLogger,\n}: {\n id?: string\n args: ExecutionArgs\n result?: ExecutionResult\n message?: string\n logLevel?: keyof LoggerLogFunctions<TLogger>\n resolveLogger?: (context: GraphQLContext) => TLogger\n}) => {\n const logger = resolveLogger\n ? resolveLogger(args.contextValue as GraphQLContext)\n : ((args.contextValue as GraphQLContext).logger as TLogger)\n if (!logger) return\n\n const { operationName, variableValues, document } = args\n const { data, ...resultWithoutData } = result ?? {}\n\n logGraphQLOperation({\n message,\n type: OperationTypeNode.SUBSCRIPTION,\n subscriptionId: id,\n operationName,\n query: print(document),\n variables: variableValues,\n result: resultWithoutData,\n logger,\n logLevel,\n })\n}\n"],"names":[],"mappings":";;;;;AA0EA;;;AAGG;AACI,MAAM,mBAAmB,GAAG,CAAkC,EACnE,OAAO,GAAG,mBAAmB,EAC7B,OAAO,EACP,IAAI,EACJ,aAAa,EACb,KAAK,EACL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,GAAG,MAAM,EACjB,GAAG,IAAI,EAC0B,KAAI;IACrC,MAAM,eAAe,GAAG,KAAK,IAAI,oBAAoB,CAAC,KAAK,CAAC;IAC5D,IAAI,UAAU,IAAI,eAAe;QAAE;AACnC,IAAA,MAAM,CAAC,QAAwB,CAAC,CAC9B,OAAO,EACP,MAAM,CACJ;QACE,IAAI;QACJ,aAAa;QACb,KAAK;QACL,SAAS,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,SAAS;AACjF,QAAA,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,SAAS;AACpD,QAAA,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,SAAS;QAClD,oBAAoB,EAAE,eAAe,IAAI,SAAS;AAClD,QAAA,GAAG,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;KACvB,EACD,KAAK,CACN,CACF;AACH;AAEO,MAAM,wBAAwB,GAAG,CAAkC,EACxE,EAAE,EACF,IAAI,EACJ,MAAM,EACN,OAAO,EACP,QAAQ,EACR,aAAa,GAQd,KAAI;IACH,MAAM,MAAM,GAAG;AACb,UAAE,aAAa,CAAC,IAAI,CAAC,YAA8B;AACnD,UAAI,IAAI,CAAC,YAA+B,CAAC,MAAkB;AAC7D,IAAA,IAAI,CAAC,MAAM;QAAE;IAEb,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,IAAI;IACxD,MAAM,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,GAAG,MAAM,IAAI,EAAE;AAEnD,IAAA,mBAAmB,CAAC;QAClB,OAAO;QACP,IAAI,EAAE,iBAAiB,CAAC,YAAY;AACpC,QAAA,cAAc,EAAE,EAAE;QAClB,aAAa;AACb,QAAA,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC;AACtB,QAAA,SAAS,EAAE,cAAc;AACzB,QAAA,MAAM,EAAE,iBAAiB;QACzB,MAAM;QACN,QAAQ;AACT,KAAA,CAAC;AACJ;;;;"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"**"
|
|
5
5
|
],
|
|
6
6
|
"name": "@makerx/graphql-core",
|
|
7
|
-
"version": "3.0.0-beta.
|
|
7
|
+
"version": "3.0.0-beta.6",
|
|
8
8
|
"private": false,
|
|
9
9
|
"description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
|
|
10
10
|
"author": "MakerX",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"express": ">=4",
|
|
27
27
|
"graphql": ">=16",
|
|
28
28
|
"graphql-shield": ">=7",
|
|
29
|
-
"graphql-ws": ">=
|
|
29
|
+
"graphql-ws": ">=6",
|
|
30
30
|
"ws": ">=8"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
@@ -46,19 +46,34 @@
|
|
|
46
46
|
"type": "commonjs",
|
|
47
47
|
"exports": {
|
|
48
48
|
".": {
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./index.d.mts",
|
|
51
|
+
"default": "./index.mjs"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./index.d.cts",
|
|
55
|
+
"default": "./index.js"
|
|
56
|
+
}
|
|
52
57
|
},
|
|
53
58
|
"./shield": {
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
"import": {
|
|
60
|
+
"types": "./shield.d.mts",
|
|
61
|
+
"default": "./shield.mjs"
|
|
62
|
+
},
|
|
63
|
+
"require": {
|
|
64
|
+
"types": "./shield.d.cts",
|
|
65
|
+
"default": "./shield.js"
|
|
66
|
+
}
|
|
57
67
|
},
|
|
58
68
|
"./subscriptions": {
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
"import": {
|
|
70
|
+
"types": "./subscriptions/index.d.mts",
|
|
71
|
+
"default": "./subscriptions/index.mjs"
|
|
72
|
+
},
|
|
73
|
+
"require": {
|
|
74
|
+
"types": "./subscriptions/index.d.cts",
|
|
75
|
+
"default": "./subscriptions/index.js"
|
|
76
|
+
}
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
79
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Request } from 'express';
|
|
2
|
+
import type { IncomingMessage } from 'http';
|
|
3
|
+
export interface BaseRequestInfo extends Record<string, unknown> {
|
|
4
|
+
requestId: string;
|
|
5
|
+
source: 'http' | 'subscription';
|
|
6
|
+
protocol: 'http' | 'https' | 'ws' | 'wss';
|
|
7
|
+
host: string;
|
|
8
|
+
port?: number;
|
|
9
|
+
method: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
url: string;
|
|
12
|
+
origin: string;
|
|
13
|
+
referer?: string;
|
|
14
|
+
correlationId?: string;
|
|
15
|
+
arrLogId?: string;
|
|
16
|
+
clientIp?: string;
|
|
17
|
+
userAgent?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds the base URL (`protocol://host[:port]`) for an Express HTTP request, using the request's
|
|
21
|
+
* protocol and resolving the host/port from forwarding headers with the Express hostname as fallback.
|
|
22
|
+
* The port is omitted when it matches the default for the protocol (80 for http, 443 for https).
|
|
23
|
+
* @param req The Express request.
|
|
24
|
+
* @returns The base URL string.
|
|
25
|
+
*/
|
|
26
|
+
export declare const requestBaseUrl: (req: Request) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the base URL (`http(s)://host[:port]`) for a raw WebSocket upgrade (connect) request.
|
|
29
|
+
* Returns an `https` URL when the connection is TLS-encrypted or `x-forwarded-proto` indicates a
|
|
30
|
+
* secure scheme, otherwise `http`. Throws if no host can be determined from the request headers,
|
|
31
|
+
* since upgrade requests have no Express `hostname` fallback.
|
|
32
|
+
* @param req The incoming upgrade request.
|
|
33
|
+
* @returns The base URL string.
|
|
34
|
+
* @throws Error when the host cannot be resolved from the request.
|
|
35
|
+
*/
|
|
36
|
+
export declare const connectRequestBaseUrl: (req: IncomingMessage) => string;
|
|
37
|
+
export declare const buildBaseRequestInfo: (req: Request) => BaseRequestInfo;
|
|
38
|
+
export declare const buildConnectRequestInfo: (req: IncomingMessage) => BaseRequestInfo;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Request } from 'express';
|
|
2
|
+
import type { IncomingMessage } from 'http';
|
|
3
|
+
export interface BaseRequestInfo extends Record<string, unknown> {
|
|
4
|
+
requestId: string;
|
|
5
|
+
source: 'http' | 'subscription';
|
|
6
|
+
protocol: 'http' | 'https' | 'ws' | 'wss';
|
|
7
|
+
host: string;
|
|
8
|
+
port?: number;
|
|
9
|
+
method: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
url: string;
|
|
12
|
+
origin: string;
|
|
13
|
+
referer?: string;
|
|
14
|
+
correlationId?: string;
|
|
15
|
+
arrLogId?: string;
|
|
16
|
+
clientIp?: string;
|
|
17
|
+
userAgent?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds the base URL (`protocol://host[:port]`) for an Express HTTP request, using the request's
|
|
21
|
+
* protocol and resolving the host/port from forwarding headers with the Express hostname as fallback.
|
|
22
|
+
* The port is omitted when it matches the default for the protocol (80 for http, 443 for https).
|
|
23
|
+
* @param req The Express request.
|
|
24
|
+
* @returns The base URL string.
|
|
25
|
+
*/
|
|
26
|
+
export declare const requestBaseUrl: (req: Request) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the base URL (`http(s)://host[:port]`) for a raw WebSocket upgrade (connect) request.
|
|
29
|
+
* Returns an `https` URL when the connection is TLS-encrypted or `x-forwarded-proto` indicates a
|
|
30
|
+
* secure scheme, otherwise `http`. Throws if no host can be determined from the request headers,
|
|
31
|
+
* since upgrade requests have no Express `hostname` fallback.
|
|
32
|
+
* @param req The incoming upgrade request.
|
|
33
|
+
* @returns The base URL string.
|
|
34
|
+
* @throws Error when the host cannot be resolved from the request.
|
|
35
|
+
*/
|
|
36
|
+
export declare const connectRequestBaseUrl: (req: IncomingMessage) => string;
|
|
37
|
+
export declare const buildBaseRequestInfo: (req: Request) => BaseRequestInfo;
|
|
38
|
+
export declare const buildConnectRequestInfo: (req: IncomingMessage) => BaseRequestInfo;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GraphQLSchema } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* It is possible to register a custom scalar with apollo server without defining an explicit resolver (a default resolver is used in
|
|
4
|
+
* this case), but it is rarely desirable. This function, when provided with your built schema, will ensure any registered scalar types
|
|
5
|
+
* provide at least a base level of validation by failing to parse a Symbol instance. The default resolver does not pass this test
|
|
6
|
+
* meaning any scalars that are falling back to the default resolver will fail.
|
|
7
|
+
* @param schema A schema object, the result of calling `makeExecutableSchema` from `@graphql-tools/schema`
|
|
8
|
+
*/
|
|
9
|
+
export declare function requireExplicitResolversForScalars(schema: GraphQLSchema, ...scalarsToIgnore: string[]): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GraphQLSchema } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* It is possible to register a custom scalar with apollo server without defining an explicit resolver (a default resolver is used in
|
|
4
|
+
* this case), but it is rarely desirable. This function, when provided with your built schema, will ensure any registered scalar types
|
|
5
|
+
* provide at least a base level of validation by failing to parse a Symbol instance. The default resolver does not pass this test
|
|
6
|
+
* meaning any scalars that are falling back to the default resolver will fail.
|
|
7
|
+
* @param schema A schema object, the result of calling `makeExecutableSchema` from `@graphql-tools/schema`
|
|
8
|
+
*/
|
|
9
|
+
export declare function requireExplicitResolversForScalars(schema: GraphQLSchema, ...scalarsToIgnore: string[]): void;
|
package/shield.d.cts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { allow, and, chain, or, race } from 'graphql-shield';
|
|
2
|
+
import { shield } from 'graphql-shield';
|
|
3
|
+
import type { Primitive } from './type-utils';
|
|
4
|
+
export type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>;
|
|
5
|
+
type RuleCombinator = typeof chain | typeof race | typeof or | typeof and;
|
|
6
|
+
export type ShieldRule = ReturnType<(typeof allow)['getRules']>[number];
|
|
7
|
+
/**
|
|
8
|
+
* Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.
|
|
9
|
+
*
|
|
10
|
+
* Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a
|
|
11
|
+
* {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —
|
|
12
|
+
* graphql-shield's native `rule` types these as `any`.
|
|
13
|
+
*
|
|
14
|
+
* Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on
|
|
15
|
+
* `ctx` (e.g. the current user), so it can be reused across fields in the same request.
|
|
16
|
+
*
|
|
17
|
+
* @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.
|
|
18
|
+
* @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
*
|
|
22
|
+
* const isOwner = createRule<GraphQLContext, { ownerId: string }>(
|
|
23
|
+
* (parent, _, ctx) => parent.ownerId === ctx.user?.id,
|
|
24
|
+
* )
|
|
25
|
+
*/
|
|
26
|
+
export declare const createRule: <TContext, TParent = unknown, TArgs = unknown>(logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error, cache?: "contextual" | "strict") => ShieldRule;
|
|
27
|
+
/**
|
|
28
|
+
* Combines a given rule with all existing defined rules using the provided combinator
|
|
29
|
+
* @param schema The existing shield schema with rules
|
|
30
|
+
* @param rule The rule to be combined with existing rules
|
|
31
|
+
* @param combinator The combinator to use. Eg. and | or | chain | race
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
*
|
|
35
|
+
* const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)
|
|
36
|
+
*/
|
|
37
|
+
export declare function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Creates graphql shield middleware making use of generics to type the definition of the rules.
|
|
40
|
+
* @param schema A mapping of schema objects to the shield rules that define the access to that object
|
|
41
|
+
* @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* import { Resolvers } from '../generated/types'
|
|
45
|
+
*
|
|
46
|
+
* const isCoordinator = hasRoleRule(UserRoles.Coordinator)
|
|
47
|
+
* const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)
|
|
48
|
+
* const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)
|
|
49
|
+
*
|
|
50
|
+
* const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)
|
|
51
|
+
* const isDataAuthor = or(isCoordinator, isSystemAdmin)
|
|
52
|
+
*
|
|
53
|
+
* const shieldSchema = createShieldSchema<Resolvers>({
|
|
54
|
+
* Query: {
|
|
55
|
+
* '*': isAuthorisedUser,
|
|
56
|
+
* user: isCoordinator,
|
|
57
|
+
* findUsers: isCoordinator,
|
|
58
|
+
* },
|
|
59
|
+
* Mutation: {
|
|
60
|
+
* '*': isDataAuthor,
|
|
61
|
+
* createUser: isCoordinator,
|
|
62
|
+
* },
|
|
63
|
+
* // specific rules for mutations, types, fields...
|
|
64
|
+
* }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})
|
|
65
|
+
*
|
|
66
|
+
* const unprotected = makeExecutableSchema({...})
|
|
67
|
+
* return applyMiddleware(unprotected, shieldSchema)
|
|
68
|
+
*/
|
|
69
|
+
export declare function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions): import("graphql-middleware").IMiddlewareGenerator<any, any, any>;
|
|
70
|
+
/**
|
|
71
|
+
* A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).
|
|
72
|
+
*
|
|
73
|
+
* Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.
|
|
74
|
+
* Object levels may also include a `'*'` key — a fallback rule applied to any field at that
|
|
75
|
+
* level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single
|
|
76
|
+
* {@link ShieldRule}.
|
|
77
|
+
*
|
|
78
|
+
* Usage:
|
|
79
|
+
*
|
|
80
|
+
* const schema: ShieldSchema<Resolvers> = {
|
|
81
|
+
* Query: {
|
|
82
|
+
* '*': isAuthorisedUser,
|
|
83
|
+
* publicThing: allow,
|
|
84
|
+
* },
|
|
85
|
+
* Mutation: { createUser: isCoordinator },
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
export type ShieldSchema<TResolver> = TResolver extends Primitive ? ShieldRule : {
|
|
89
|
+
[key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule;
|
|
90
|
+
} & {
|
|
91
|
+
'*'?: ShieldRule;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Shield rule that passes when the current user has the given role.
|
|
95
|
+
* @param role The role the user must have on `ctx.user.roles`.
|
|
96
|
+
* @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.
|
|
97
|
+
*/
|
|
98
|
+
export declare function hasRoleRule(role: string, ruleName?: string): ShieldRule;
|
|
99
|
+
/**
|
|
100
|
+
* Shield rule that passes when the current user has at least one of the given roles.
|
|
101
|
+
* Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.
|
|
102
|
+
* @param roles The set of roles, any of which satisfies the rule.
|
|
103
|
+
*/
|
|
104
|
+
export declare function hasAnyRoleRule(...roles: string[]): ShieldRule;
|
|
105
|
+
/**
|
|
106
|
+
* Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.
|
|
107
|
+
* Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.
|
|
108
|
+
* @param ruleName The name (and cache key) for the rule.
|
|
109
|
+
* @param roles The set of roles, any of which satisfies the rule.
|
|
110
|
+
*/
|
|
111
|
+
export declare function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]): ShieldRule;
|
|
112
|
+
/**
|
|
113
|
+
* Shield rule that passes when the current user has the given scope.
|
|
114
|
+
* @param scope The scope the user must have on `ctx.user.scopes`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function hasScopeRule(scope: string): ShieldRule;
|
|
117
|
+
/**
|
|
118
|
+
* Shield rule that passes when the current user has at least one of the given scopes.
|
|
119
|
+
* @param scopes The set of scopes, any of which satisfies the rule.
|
|
120
|
+
*/
|
|
121
|
+
export declare function hasAnyScopeRule(...scopes: string[]): ShieldRule;
|
|
122
|
+
export {};
|
package/shield.d.mts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { allow, and, chain, or, race } from 'graphql-shield';
|
|
2
|
+
import { shield } from 'graphql-shield';
|
|
3
|
+
import type { Primitive } from './type-utils.js';
|
|
4
|
+
export type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>;
|
|
5
|
+
type RuleCombinator = typeof chain | typeof race | typeof or | typeof and;
|
|
6
|
+
export type ShieldRule = ReturnType<(typeof allow)['getRules']>[number];
|
|
7
|
+
/**
|
|
8
|
+
* Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.
|
|
9
|
+
*
|
|
10
|
+
* Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a
|
|
11
|
+
* {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —
|
|
12
|
+
* graphql-shield's native `rule` types these as `any`.
|
|
13
|
+
*
|
|
14
|
+
* Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on
|
|
15
|
+
* `ctx` (e.g. the current user), so it can be reused across fields in the same request.
|
|
16
|
+
*
|
|
17
|
+
* @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.
|
|
18
|
+
* @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
*
|
|
22
|
+
* const isOwner = createRule<GraphQLContext, { ownerId: string }>(
|
|
23
|
+
* (parent, _, ctx) => parent.ownerId === ctx.user?.id,
|
|
24
|
+
* )
|
|
25
|
+
*/
|
|
26
|
+
export declare const createRule: <TContext, TParent = unknown, TArgs = unknown>(logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error, cache?: "contextual" | "strict") => ShieldRule;
|
|
27
|
+
/**
|
|
28
|
+
* Combines a given rule with all existing defined rules using the provided combinator
|
|
29
|
+
* @param schema The existing shield schema with rules
|
|
30
|
+
* @param rule The rule to be combined with existing rules
|
|
31
|
+
* @param combinator The combinator to use. Eg. and | or | chain | race
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
*
|
|
35
|
+
* const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)
|
|
36
|
+
*/
|
|
37
|
+
export declare function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Creates graphql shield middleware making use of generics to type the definition of the rules.
|
|
40
|
+
* @param schema A mapping of schema objects to the shield rules that define the access to that object
|
|
41
|
+
* @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* import { Resolvers } from '../generated/types'
|
|
45
|
+
*
|
|
46
|
+
* const isCoordinator = hasRoleRule(UserRoles.Coordinator)
|
|
47
|
+
* const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)
|
|
48
|
+
* const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)
|
|
49
|
+
*
|
|
50
|
+
* const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)
|
|
51
|
+
* const isDataAuthor = or(isCoordinator, isSystemAdmin)
|
|
52
|
+
*
|
|
53
|
+
* const shieldSchema = createShieldSchema<Resolvers>({
|
|
54
|
+
* Query: {
|
|
55
|
+
* '*': isAuthorisedUser,
|
|
56
|
+
* user: isCoordinator,
|
|
57
|
+
* findUsers: isCoordinator,
|
|
58
|
+
* },
|
|
59
|
+
* Mutation: {
|
|
60
|
+
* '*': isDataAuthor,
|
|
61
|
+
* createUser: isCoordinator,
|
|
62
|
+
* },
|
|
63
|
+
* // specific rules for mutations, types, fields...
|
|
64
|
+
* }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})
|
|
65
|
+
*
|
|
66
|
+
* const unprotected = makeExecutableSchema({...})
|
|
67
|
+
* return applyMiddleware(unprotected, shieldSchema)
|
|
68
|
+
*/
|
|
69
|
+
export declare function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions): import("graphql-middleware").IMiddlewareGenerator<any, any, any>;
|
|
70
|
+
/**
|
|
71
|
+
* A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).
|
|
72
|
+
*
|
|
73
|
+
* Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.
|
|
74
|
+
* Object levels may also include a `'*'` key — a fallback rule applied to any field at that
|
|
75
|
+
* level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single
|
|
76
|
+
* {@link ShieldRule}.
|
|
77
|
+
*
|
|
78
|
+
* Usage:
|
|
79
|
+
*
|
|
80
|
+
* const schema: ShieldSchema<Resolvers> = {
|
|
81
|
+
* Query: {
|
|
82
|
+
* '*': isAuthorisedUser,
|
|
83
|
+
* publicThing: allow,
|
|
84
|
+
* },
|
|
85
|
+
* Mutation: { createUser: isCoordinator },
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
export type ShieldSchema<TResolver> = TResolver extends Primitive ? ShieldRule : {
|
|
89
|
+
[key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule;
|
|
90
|
+
} & {
|
|
91
|
+
'*'?: ShieldRule;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Shield rule that passes when the current user has the given role.
|
|
95
|
+
* @param role The role the user must have on `ctx.user.roles`.
|
|
96
|
+
* @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.
|
|
97
|
+
*/
|
|
98
|
+
export declare function hasRoleRule(role: string, ruleName?: string): ShieldRule;
|
|
99
|
+
/**
|
|
100
|
+
* Shield rule that passes when the current user has at least one of the given roles.
|
|
101
|
+
* Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.
|
|
102
|
+
* @param roles The set of roles, any of which satisfies the rule.
|
|
103
|
+
*/
|
|
104
|
+
export declare function hasAnyRoleRule(...roles: string[]): ShieldRule;
|
|
105
|
+
/**
|
|
106
|
+
* Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.
|
|
107
|
+
* Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.
|
|
108
|
+
* @param ruleName The name (and cache key) for the rule.
|
|
109
|
+
* @param roles The set of roles, any of which satisfies the rule.
|
|
110
|
+
*/
|
|
111
|
+
export declare function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]): ShieldRule;
|
|
112
|
+
/**
|
|
113
|
+
* Shield rule that passes when the current user has the given scope.
|
|
114
|
+
* @param scope The scope the user must have on `ctx.user.scopes`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function hasScopeRule(scope: string): ShieldRule;
|
|
117
|
+
/**
|
|
118
|
+
* Shield rule that passes when the current user has at least one of the given scopes.
|
|
119
|
+
* @param scopes The set of scopes, any of which satisfies the rule.
|
|
120
|
+
*/
|
|
121
|
+
export declare function hasAnyScopeRule(...scopes: string[]): ShieldRule;
|
|
122
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { IncomingMessage } from 'http';
|
|
3
|
+
import { User } from '../User';
|
|
4
|
+
import type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context';
|
|
5
|
+
export interface SubscriptionContextInput {
|
|
6
|
+
connectRequest: IncomingMessage;
|
|
7
|
+
connectionParams?: Readonly<Record<string, unknown>>;
|
|
8
|
+
claims?: JwtPayload;
|
|
9
|
+
}
|
|
10
|
+
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
|
|
11
|
+
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
12
|
+
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
13
|
+
export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
14
|
+
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
15
|
+
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
16
|
+
claimsToLog?: string[];
|
|
17
|
+
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
18
|
+
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
19
|
+
} & ([User | undefined] extends [TUser] ? {
|
|
20
|
+
createUser?: CreateSubscriptionUser<TUser>;
|
|
21
|
+
} : {
|
|
22
|
+
createUser: CreateSubscriptionUser<TUser>;
|
|
23
|
+
});
|
|
24
|
+
export declare const createSubscriptionContextFactory: <TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger>(config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>) => CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { IncomingMessage } from 'http';
|
|
3
|
+
import { User } from '../User.js';
|
|
4
|
+
import type { CreateRequestLogger, GraphQLContext, JwtPayload, RequestInfo, RequestInfoLogKey } from '../context.js';
|
|
5
|
+
export interface SubscriptionContextInput {
|
|
6
|
+
connectRequest: IncomingMessage;
|
|
7
|
+
connectionParams?: Readonly<Record<string, unknown>>;
|
|
8
|
+
claims?: JwtPayload;
|
|
9
|
+
}
|
|
10
|
+
export type CreateSubscriptionUser<T = User | undefined> = (input: SubscriptionContextInput) => Promise<T> | T;
|
|
11
|
+
export type CreateSubscriptionContext<TContext = GraphQLContext> = (input: SubscriptionContextInput) => Promise<TContext>;
|
|
12
|
+
export type AugmentSubscriptionRequestInfo = (input: SubscriptionContextInput) => Record<string, unknown>;
|
|
13
|
+
export type CreateSubscriptionContextConfig<TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger> = {
|
|
14
|
+
requestLogger: CreateRequestLogger<TUser, TLogger> | TLogger;
|
|
15
|
+
augmentRequestInfo?: AugmentSubscriptionRequestInfo;
|
|
16
|
+
claimsToLog?: string[];
|
|
17
|
+
requestInfoToLog?: Array<RequestInfoLogKey>;
|
|
18
|
+
augmentContext?: (context: GraphQLContext<TLogger, RequestInfo, TUser>) => TAugment | Promise<TAugment>;
|
|
19
|
+
} & ([User | undefined] extends [TUser] ? {
|
|
20
|
+
createUser?: CreateSubscriptionUser<TUser>;
|
|
21
|
+
} : {
|
|
22
|
+
createUser: CreateSubscriptionUser<TUser>;
|
|
23
|
+
});
|
|
24
|
+
export declare const createSubscriptionContextFactory: <TUser = User | undefined, TAugment extends Record<string, unknown> = Record<string, never>, TLogger extends Logger = Logger>(config: CreateSubscriptionContextConfig<TUser, TAugment, TLogger>) => CreateSubscriptionContext<GraphQLContext<TLogger, RequestInfo, TUser> & TAugment>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { Server } from 'http';
|
|
4
|
+
import type { GraphQLContext, JwtPayload } from '../context';
|
|
5
|
+
import type { CreateSubscriptionContext } from './context';
|
|
6
|
+
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, }: {
|
|
7
|
+
schema: GraphQLSchema;
|
|
8
|
+
httpServer: Server;
|
|
9
|
+
createSubscriptionContext: CreateSubscriptionContext;
|
|
10
|
+
logger: TLogger;
|
|
11
|
+
operationLogLevel?: keyof TLogger;
|
|
12
|
+
path?: string;
|
|
13
|
+
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
requireAuth?: boolean;
|
|
15
|
+
jwtClaimsToLog?: string[];
|
|
16
|
+
resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
|
|
17
|
+
}): import("graphql-ws").Disposable;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Logger } from '@makerx/node-common';
|
|
2
|
+
import type { GraphQLSchema } from 'graphql';
|
|
3
|
+
import type { Server } from 'http';
|
|
4
|
+
import type { GraphQLContext, JwtPayload } from '../context.js';
|
|
5
|
+
import type { CreateSubscriptionContext } from './context.js';
|
|
6
|
+
export declare function useSubscriptionsServer<TLogger extends Logger = Logger>({ schema, httpServer, createSubscriptionContext, logger, operationLogLevel, path, verifyToken, requireAuth, jwtClaimsToLog, resolveSubscriptionOperationLogger, }: {
|
|
7
|
+
schema: GraphQLSchema;
|
|
8
|
+
httpServer: Server;
|
|
9
|
+
createSubscriptionContext: CreateSubscriptionContext;
|
|
10
|
+
logger: TLogger;
|
|
11
|
+
operationLogLevel?: keyof TLogger;
|
|
12
|
+
path?: string;
|
|
13
|
+
verifyToken?: (host: string, token: string) => Promise<JwtPayload>;
|
|
14
|
+
requireAuth?: boolean;
|
|
15
|
+
jwtClaimsToLog?: string[];
|
|
16
|
+
resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger;
|
|
17
|
+
}): import("graphql-ws").Disposable;
|
package/subscriptions/server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var graphqlWs = require('graphql-ws');
|
|
4
|
-
var ws$1 = require('graphql-ws/lib/use/ws');
|
|
5
3
|
var compat = require('es-toolkit/compat');
|
|
4
|
+
var graphqlWs = require('graphql-ws');
|
|
5
|
+
var ws$1 = require('graphql-ws/use/ws');
|
|
6
6
|
var ws = require('ws');
|
|
7
7
|
var logging = require('../logging.js');
|
|
8
8
|
var utils = require('./utils.js');
|
|
@@ -76,11 +76,11 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
76
76
|
claims: ctx.extra.claims,
|
|
77
77
|
});
|
|
78
78
|
},
|
|
79
|
-
onOperation(_ctx,
|
|
80
|
-
logging.logSubscriptionOperation({ args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger });
|
|
79
|
+
onOperation(_ctx, id, _payload, args) {
|
|
80
|
+
logging.logSubscriptionOperation({ id, args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger });
|
|
81
81
|
},
|
|
82
|
-
onNext(_ctx,
|
|
83
|
-
logging.logSubscriptionOperation({ args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger });
|
|
82
|
+
onNext(_ctx, id, _payload, args, { data, ...result }) {
|
|
83
|
+
logging.logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger });
|
|
84
84
|
},
|
|
85
85
|
}, wsServer);
|
|
86
86
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport { extractTokenFromConnectionParams, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":["WebSocketServer","useServer","extractTokenFromConnectionParams","getHost","pick","CloseCode","logSubscriptionOperation"],"mappings":"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"}
|
package/subscriptions/server.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CloseCode } from 'graphql-ws';
|
|
2
|
-
import { useServer } from 'graphql-ws/lib/use/ws';
|
|
3
1
|
import { pick } from 'es-toolkit/compat';
|
|
2
|
+
import { CloseCode } from 'graphql-ws';
|
|
3
|
+
import { useServer } from 'graphql-ws/use/ws';
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
5
|
import { logSubscriptionOperation } from '../logging.mjs';
|
|
6
6
|
import { extractTokenFromConnectionParams, getHost } from './utils.mjs';
|
|
@@ -74,11 +74,11 @@ function useSubscriptionsServer({ schema, httpServer, createSubscriptionContext,
|
|
|
74
74
|
claims: ctx.extra.claims,
|
|
75
75
|
});
|
|
76
76
|
},
|
|
77
|
-
onOperation(_ctx,
|
|
78
|
-
logSubscriptionOperation({ args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger });
|
|
77
|
+
onOperation(_ctx, id, _payload, args) {
|
|
78
|
+
logSubscriptionOperation({ id, args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger });
|
|
79
79
|
},
|
|
80
|
-
onNext(_ctx,
|
|
81
|
-
logSubscriptionOperation({ args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger });
|
|
80
|
+
onNext(_ctx, id, _payload, args, { data, ...result }) {
|
|
81
|
+
logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger });
|
|
82
82
|
},
|
|
83
83
|
}, wsServer);
|
|
84
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/
|
|
1
|
+
{"version":3,"file":"server.mjs","sources":["../../src/subscriptions/server.ts"],"sourcesContent":["import type { Logger } from '@makerx/node-common'\nimport { pick } from 'es-toolkit/compat'\nimport type { GraphQLSchema } from 'graphql'\nimport { CloseCode } from 'graphql-ws'\nimport { useServer } from 'graphql-ws/use/ws'\nimport type { Server } from 'http'\nimport { WebSocketServer } from 'ws'\nimport type { GraphQLContext, JwtPayload } from '../context'\nimport { logSubscriptionOperation } from '../logging'\nimport type { CreateSubscriptionContext } from './context'\nimport { extractTokenFromConnectionParams, getHost } from './utils'\n\nexport function useSubscriptionsServer<TLogger extends Logger = Logger>({\n schema,\n httpServer,\n createSubscriptionContext,\n logger,\n operationLogLevel = 'info',\n path = '/graphql',\n verifyToken,\n requireAuth,\n jwtClaimsToLog = ['oid', 'iss'],\n resolveSubscriptionOperationLogger,\n}: {\n schema: GraphQLSchema\n httpServer: Server\n createSubscriptionContext: CreateSubscriptionContext\n logger: TLogger\n operationLogLevel?: keyof TLogger\n path?: string\n verifyToken?: (host: string, token: string) => Promise<JwtPayload>\n requireAuth?: boolean\n jwtClaimsToLog?: string[]\n resolveSubscriptionOperationLogger?: (context: GraphQLContext) => TLogger\n}) {\n if (requireAuth && !verifyToken) throw new Error('verifyToken must be supplied when requireAuth is true')\n\n const wsServer = new WebSocketServer({\n server: httpServer,\n path,\n })\n\n return useServer(\n {\n schema,\n onError(_ctx, message, errors) {\n logger.error('GraphQL subscriptions server error', { message, errors })\n },\n onConnect: async (ctx) => {\n const connectionEstablished = 'Subscription connection established'\n if (!verifyToken) {\n logger.info(connectionEstablished)\n return true\n }\n\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n return false\n }\n logger.info(connectionEstablished)\n return true\n }\n\n try {\n const claims = await verifyToken(getHost(ctx.extra.request), token)\n ctx.extra.claims = claims as unknown as undefined\n logger.info(connectionEstablished, {\n claims: pick(claims, jwtClaimsToLog),\n })\n return true\n } catch (error) {\n logger.error('Failed to verify subscription connection auth token', { error })\n return false\n }\n },\n onSubscribe: async (ctx) => {\n if (!verifyToken) return\n const token = extractTokenFromConnectionParams(ctx.connectionParams)\n if (!token) {\n if (requireAuth) {\n logger.error('No authorization parameter was supplied via websocket connection params')\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n return\n }\n try {\n await verifyToken(getHost(ctx.extra.request), token)\n } catch (error) {\n logger.warn('Subscription connection auth token is no longer valid', { claims: pick(ctx.extra.claims, jwtClaimsToLog), error })\n ctx.extra.socket.close(CloseCode.Forbidden, 'Forbidden')\n }\n },\n onDisconnect({ extra: { claims } }) {\n logger.info('Subscription connection disconnected', { claims: pick(claims, jwtClaimsToLog) })\n },\n context: async (ctx) => {\n return createSubscriptionContext({\n connectRequest: ctx.extra.request,\n connectionParams: ctx.connectionParams,\n claims: ctx.extra.claims as JwtPayload | undefined,\n })\n },\n onOperation(_ctx, id, _payload, args) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, resolveLogger: resolveSubscriptionOperationLogger })\n },\n onNext(_ctx, id, _payload, args, { data, ...result }) {\n logSubscriptionOperation({ id, args, logLevel: operationLogLevel, result, resolveLogger: resolveSubscriptionOperationLogger })\n },\n },\n wsServer,\n )\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
4
|
+
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
5
|
+
* Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.
|
|
6
|
+
* @param connectionParams
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
10
|
+
export declare function getHost(request: IncomingMessage): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts a token from a connection parameter named `authorization` or `Authorization`.
|
|
4
|
+
* The expected format of the parameter value is: Bearer <token>, consistent with an HTTP Authorization header.
|
|
5
|
+
* Apollo Sandbox sets an `Authorization` connection parameter when you specify an HTTP Authorization header via the UI.
|
|
6
|
+
* @param connectionParams
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractTokenFromConnectionParams(connectionParams?: Readonly<Record<string, unknown>>): string | undefined;
|
|
10
|
+
export declare function getHost(request: IncomingMessage): string;
|
package/type-utils.d.cts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type Primitive = string | number | symbol | bigint | boolean | null | undefined | Date;
|
|
2
|
+
type ScalarPropsInternal<T, TKey extends keyof T, TAdditionalScalars = never> = TKey extends any ? T[TKey] extends Primitive | TAdditionalScalars ? TKey : never : never;
|
|
3
|
+
export type ScalarProps<T, TAdditionalScalars = never> = Pick<T, ScalarPropsInternal<T, keyof T, TAdditionalScalars>>;
|
|
4
|
+
export {};
|
package/type-utils.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type Primitive = string | number | symbol | bigint | boolean | null | undefined | Date;
|
|
2
|
+
type ScalarPropsInternal<T, TKey extends keyof T, TAdditionalScalars = never> = TKey extends any ? T[TKey] extends Primitive | TAdditionalScalars ? TKey : never : never;
|
|
3
|
+
export type ScalarProps<T, TAdditionalScalars = never> = Pick<T, ScalarPropsInternal<T, keyof T, TAdditionalScalars>>;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a `GraphQLError` representing an authorization failure.
|
|
4
|
+
*
|
|
5
|
+
* Sets the Apollo-standard `FORBIDDEN` extension code and an HTTP 403 status, so clients and
|
|
6
|
+
* gateways that inspect `extensions.code` / `extensions.http.status` see a consistent shape.
|
|
7
|
+
*
|
|
8
|
+
* @param message Error message shown to the client. Defaults to `'Not Authorized!'`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const unauthorisedError: (message?: string) => GraphQLError;
|
|
11
|
+
/**
|
|
12
|
+
* Throws an {@link unauthorisedError}. Return type is `never` so TypeScript narrows control flow past the call.
|
|
13
|
+
* @param message Optional override for the error message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function throwUnauthorised(message?: string): never;
|
|
16
|
+
/**
|
|
17
|
+
* Assertion helper: throws an {@link unauthorisedError} when `hasPermission` is false, otherwise narrows it to `true`.
|
|
18
|
+
*
|
|
19
|
+
* Use at the top of resolvers or services to fail fast on authorization checks while keeping
|
|
20
|
+
* the happy path unindented.
|
|
21
|
+
*
|
|
22
|
+
* @param hasPermission Boolean guard; the function returns normally only when this is `true`.
|
|
23
|
+
* @param message Optional override for the error message thrown on failure.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
*
|
|
27
|
+
* permissionInvariant(ctx.user?.id === record.ownerId, 'Only the owner can edit this record')
|
|
28
|
+
* // past this line, TypeScript knows the condition held
|
|
29
|
+
*/
|
|
30
|
+
export declare function permissionInvariant(hasPermission: boolean, message?: string): asserts hasPermission;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a `GraphQLError` representing an authorization failure.
|
|
4
|
+
*
|
|
5
|
+
* Sets the Apollo-standard `FORBIDDEN` extension code and an HTTP 403 status, so clients and
|
|
6
|
+
* gateways that inspect `extensions.code` / `extensions.http.status` see a consistent shape.
|
|
7
|
+
*
|
|
8
|
+
* @param message Error message shown to the client. Defaults to `'Not Authorized!'`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const unauthorisedError: (message?: string) => GraphQLError;
|
|
11
|
+
/**
|
|
12
|
+
* Throws an {@link unauthorisedError}. Return type is `never` so TypeScript narrows control flow past the call.
|
|
13
|
+
* @param message Optional override for the error message.
|
|
14
|
+
*/
|
|
15
|
+
export declare function throwUnauthorised(message?: string): never;
|
|
16
|
+
/**
|
|
17
|
+
* Assertion helper: throws an {@link unauthorisedError} when `hasPermission` is false, otherwise narrows it to `true`.
|
|
18
|
+
*
|
|
19
|
+
* Use at the top of resolvers or services to fail fast on authorization checks while keeping
|
|
20
|
+
* the happy path unindented.
|
|
21
|
+
*
|
|
22
|
+
* @param hasPermission Boolean guard; the function returns normally only when this is `true`.
|
|
23
|
+
* @param message Optional override for the error message thrown on failure.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
*
|
|
27
|
+
* permissionInvariant(ctx.user?.id === record.ownerId, 'Only the owner can edit this record')
|
|
28
|
+
* // past this line, TypeScript knows the condition held
|
|
29
|
+
*/
|
|
30
|
+
export declare function permissionInvariant(hasPermission: boolean, message?: string): asserts hasPermission;
|
package/utils.d.cts
ADDED
package/utils.d.mts
ADDED