@makerx/graphql-core 3.0.0-beta.3 → 3.0.0-beta.5

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 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
@@ -0,0 +1,8 @@
1
+ export * from './context';
2
+ export * from './logging';
3
+ export * from './request-info';
4
+ export * from './schema-util';
5
+ export * from './type-utils';
6
+ export * from './unauthorised';
7
+ export * from './User';
8
+ export * from './utils';
package/index.d.mts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './context.js';
2
+ export * from './logging.js';
3
+ export * from './request-info.js';
4
+ export * from './schema-util.js';
5
+ export * from './type-utils.js';
6
+ export * from './unauthorised.js';
7
+ export * from './User.js';
8
+ export * from './utils.js';
package/logging.d.cts ADDED
@@ -0,0 +1,81 @@
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>({ args, result, message, logLevel, resolveLogger, }: {
75
+ args: ExecutionArgs;
76
+ result?: ExecutionResult;
77
+ message?: string;
78
+ logLevel?: keyof LoggerLogFunctions<TLogger>;
79
+ resolveLogger?: (context: GraphQLContext) => TLogger;
80
+ }) => void;
81
+ export {};
package/logging.d.mts ADDED
@@ -0,0 +1,81 @@
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>({ args, result, message, logLevel, resolveLogger, }: {
75
+ args: ExecutionArgs;
76
+ result?: ExecutionResult;
77
+ message?: string;
78
+ logLevel?: keyof LoggerLogFunctions<TLogger>;
79
+ resolveLogger?: (context: GraphQLContext) => TLogger;
80
+ }) => void;
81
+ export {};
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "**"
5
5
  ],
6
6
  "name": "@makerx/graphql-core",
7
- "version": "3.0.0-beta.3",
7
+ "version": "3.0.0-beta.5",
8
8
  "private": false,
9
9
  "description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
10
10
  "author": "MakerX",
@@ -46,19 +46,34 @@
46
46
  "type": "commonjs",
47
47
  "exports": {
48
48
  ".": {
49
- "types": "./index.d.ts",
50
- "import": "./index.mjs",
51
- "require": "./index.js"
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
- "types": "./shield.d.ts",
55
- "import": "./shield.mjs",
56
- "require": "./shield.js"
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
- "types": "./subscriptions/index.d.ts",
60
- "import": "./subscriptions/index.mjs",
61
- "require": "./subscriptions/index.js"
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 {};
package/shield.d.ts 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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"shield.js","sources":["../src/shield.ts"],"sourcesContent":["import type { allow, and, chain, IRules, or, race } from 'graphql-shield'\nimport { rule, shield } from 'graphql-shield'\nimport type { GraphQLContext } from './context'\nimport type { Primitive } from './type-utils'\n\nexport type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>\n\ntype RuleCombinator = typeof chain | typeof race | typeof or | typeof and\n// For whatever reason, graphql-shield doesn't export this type, but we can extract if from\n// what it does export.\nexport type ShieldRule = ReturnType<(typeof allow)['getRules']>[number]\n\n/**\n * Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.\n *\n * Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a\n * {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —\n * graphql-shield's native `rule` types these as `any`.\n *\n * Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on\n * `ctx` (e.g. the current user), so it can be reused across fields in the same request.\n *\n * @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.\n * @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.\n *\n * Usage:\n *\n * const isOwner = createRule<GraphQLContext, { ownerId: string }>(\n * (parent, _, ctx) => parent.ownerId === ctx.user?.id,\n * )\n */\nexport const createRule = <TContext, TParent = unknown, TArgs = unknown>(\n logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error,\n cache: 'contextual' | 'strict' = 'strict',\n): ShieldRule => rule({ cache })(async (parent, args, ctx) => logic(parent, args, ctx))\n\n/**\n * Combines a given rule with all existing defined rules using the provided combinator\n * @param schema The existing shield schema with rules\n * @param rule The rule to be combined with existing rules\n * @param combinator The combinator to use. Eg. and | or | chain | race\n *\n * Usage:\n *\n * const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)\n */\nexport function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T> {\n return Object.fromEntries(\n Object.entries(schema).map(([key, value]) => {\n if (value.constructor.name !== 'Object') {\n return [key, combinator(rule, value)]\n }\n return [key, combineRuleWithAll(value, rule, combinator)]\n }),\n ) as ShieldSchema<T>\n}\n\n/**\n * Creates graphql shield middleware making use of generics to type the definition of the rules.\n * @param schema A mapping of schema objects to the shield rules that define the access to that object\n * @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.\n *\n * Usage:\n * import { Resolvers } from '../generated/types'\n *\n * const isCoordinator = hasRoleRule(UserRoles.Coordinator)\n * const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)\n * const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)\n *\n * const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)\n * const isDataAuthor = or(isCoordinator, isSystemAdmin)\n *\n * const shieldSchema = createShieldSchema<Resolvers>({\n * Query: {\n * '*': isAuthorisedUser,\n * user: isCoordinator,\n * findUsers: isCoordinator,\n * },\n * Mutation: {\n * '*': isDataAuthor,\n * createUser: isCoordinator,\n * },\n * // specific rules for mutations, types, fields...\n * }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})\n *\n * const unprotected = makeExecutableSchema({...})\n * return applyMiddleware(unprotected, shieldSchema)\n */\nexport function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions) {\n return shield(schema as IRules, {\n allowExternalErrors: true,\n ...options,\n })\n}\n\n/**\n * A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).\n *\n * Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.\n * Object levels may also include a `'*'` key — a fallback rule applied to any field at that\n * level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single\n * {@link ShieldRule}.\n *\n * Usage:\n *\n * const schema: ShieldSchema<Resolvers> = {\n * Query: {\n * '*': isAuthorisedUser,\n * publicThing: allow,\n * },\n * Mutation: { createUser: isCoordinator },\n * }\n */\nexport type ShieldSchema<TResolver> = TResolver extends Primitive\n ? ShieldRule\n : {\n [key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule\n } & {\n '*'?: ShieldRule\n }\n\n/**\n * Shield rule that passes when the current user has the given role.\n * @param role The role the user must have on `ctx.user.roles`.\n * @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.\n */\nexport function hasRoleRule(role: string, ruleName?: string) {\n return rule(ruleName ?? `hasRole-${role}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.roles.includes(role) === true,\n )\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles.\n * Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRule(...roles: string[]) {\n return hasAnyRoleRuleWithName(`hasAnyRole-${roles.join(',')}`, ...roles)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.\n * Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.\n * @param ruleName The name (and cache key) for the rule.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]) {\n return rule(ruleName, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => {\n return user?.roles.some((role: string) => roles.includes(role)) === true\n })\n}\n\n/**\n * Shield rule that passes when the current user has the given scope.\n * @param scope The scope the user must have on `ctx.user.scopes`.\n */\nexport function hasScopeRule(scope: string) {\n return rule(`hasScope-${scope}`, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => user?.scopes.includes(scope) === true)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given scopes.\n * @param scopes The set of scopes, any of which satisfies the rule.\n */\nexport function hasAnyScopeRule(...scopes: string[]) {\n return rule(`hasAnyScope-${scopes.join(',')}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.scopes.some((scope: string) => scopes.includes(scope)) === true,\n )\n}\n"],"names":["rule","shield"],"mappings":"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"}
1
+ {"version":3,"file":"shield.js","sources":["../src/shield.ts"],"sourcesContent":["import type { allow, and, chain, IRules, or, race } from 'graphql-shield'\nimport { rule, shield } from 'graphql-shield'\nimport type { GraphQLContext } from './context'\nimport type { Primitive } from './type-utils'\n\nexport type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>\n\ntype RuleCombinator = typeof chain | typeof race | typeof or | typeof and\n// For whatever reason, graphql-shield doesn't export this type, but we can extract if from\n// what it does export.\nexport type ShieldRule = ReturnType<(typeof allow)['getRules']>[number]\n\n/**\n * Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.\n *\n * Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a\n * {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —\n * graphql-shield's native `rule` types these as `any`.\n *\n * Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on\n * `ctx` (e.g. the current user), so it can be reused across fields in the same request.\n *\n * @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.\n * @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.\n *\n * Usage:\n *\n * const isOwner = createRule<GraphQLContext, { ownerId: string }>(\n * (parent, _, ctx) => parent.ownerId === ctx.user?.id,\n * )\n */\nexport const createRule = <TContext, TParent = unknown, TArgs = unknown>(\n logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error,\n cache: 'contextual' | 'strict' = 'strict',\n): ShieldRule => rule({ cache })(async (parent, args, ctx) => logic(parent, args, ctx))\n\n/**\n * Combines a given rule with all existing defined rules using the provided combinator\n * @param schema The existing shield schema with rules\n * @param rule The rule to be combined with existing rules\n * @param combinator The combinator to use. Eg. and | or | chain | race\n *\n * Usage:\n *\n * const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)\n */\nexport function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T> {\n return Object.fromEntries(\n Object.entries(schema).map(([key, value]) => {\n if (value.constructor.name !== 'Object') {\n return [key, combinator(rule, value)]\n }\n return [key, combineRuleWithAll(value, rule, combinator)]\n }),\n ) as ShieldSchema<T>\n}\n\n/**\n * Creates graphql shield middleware making use of generics to type the definition of the rules.\n * @param schema A mapping of schema objects to the shield rules that define the access to that object\n * @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.\n *\n * Usage:\n * import { Resolvers } from '../generated/types'\n *\n * const isCoordinator = hasRoleRule(UserRoles.Coordinator)\n * const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)\n * const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)\n *\n * const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)\n * const isDataAuthor = or(isCoordinator, isSystemAdmin)\n *\n * const shieldSchema = createShieldSchema<Resolvers>({\n * Query: {\n * '*': isAuthorisedUser,\n * user: isCoordinator,\n * findUsers: isCoordinator,\n * },\n * Mutation: {\n * '*': isDataAuthor,\n * createUser: isCoordinator,\n * },\n * // specific rules for mutations, types, fields...\n * }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})\n *\n * const unprotected = makeExecutableSchema({...})\n * return applyMiddleware(unprotected, shieldSchema)\n */\nexport function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions) {\n return shield(schema as IRules, {\n allowExternalErrors: true,\n ...options,\n })\n}\n\n/**\n * A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).\n *\n * Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.\n * Object levels may also include a `'*'` key — a fallback rule applied to any field at that\n * level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single\n * {@link ShieldRule}.\n *\n * Usage:\n *\n * const schema: ShieldSchema<Resolvers> = {\n * Query: {\n * '*': isAuthorisedUser,\n * publicThing: allow,\n * },\n * Mutation: { createUser: isCoordinator },\n * }\n */\nexport type ShieldSchema<TResolver> = TResolver extends Primitive\n ? ShieldRule\n : {\n [key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule\n } & {\n '*'?: ShieldRule\n }\n\n/**\n * Shield rule that passes when the current user has the given role.\n * @param role The role the user must have on `ctx.user.roles`.\n * @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.\n */\nexport function hasRoleRule(role: string, ruleName?: string): ShieldRule {\n return rule(ruleName ?? `hasRole-${role}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.roles.includes(role) === true,\n )\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles.\n * Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRule(...roles: string[]): ShieldRule {\n return hasAnyRoleRuleWithName(`hasAnyRole-${roles.join(',')}`, ...roles)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.\n * Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.\n * @param ruleName The name (and cache key) for the rule.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]): ShieldRule {\n return rule(ruleName, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => {\n return user?.roles.some((role: string) => roles.includes(role)) === true\n })\n}\n\n/**\n * Shield rule that passes when the current user has the given scope.\n * @param scope The scope the user must have on `ctx.user.scopes`.\n */\nexport function hasScopeRule(scope: string): ShieldRule {\n return rule(`hasScope-${scope}`, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => user?.scopes.includes(scope) === true)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given scopes.\n * @param scopes The set of scopes, any of which satisfies the rule.\n */\nexport function hasAnyScopeRule(...scopes: string[]): ShieldRule {\n return rule(`hasAnyScope-${scopes.join(',')}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.scopes.some((scope: string) => scopes.includes(scope)) === true,\n )\n}\n"],"names":["rule","shield"],"mappings":"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"}
package/shield.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"shield.mjs","sources":["../src/shield.ts"],"sourcesContent":["import type { allow, and, chain, IRules, or, race } from 'graphql-shield'\nimport { rule, shield } from 'graphql-shield'\nimport type { GraphQLContext } from './context'\nimport type { Primitive } from './type-utils'\n\nexport type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>\n\ntype RuleCombinator = typeof chain | typeof race | typeof or | typeof and\n// For whatever reason, graphql-shield doesn't export this type, but we can extract if from\n// what it does export.\nexport type ShieldRule = ReturnType<(typeof allow)['getRules']>[number]\n\n/**\n * Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.\n *\n * Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a\n * {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —\n * graphql-shield's native `rule` types these as `any`.\n *\n * Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on\n * `ctx` (e.g. the current user), so it can be reused across fields in the same request.\n *\n * @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.\n * @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.\n *\n * Usage:\n *\n * const isOwner = createRule<GraphQLContext, { ownerId: string }>(\n * (parent, _, ctx) => parent.ownerId === ctx.user?.id,\n * )\n */\nexport const createRule = <TContext, TParent = unknown, TArgs = unknown>(\n logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error,\n cache: 'contextual' | 'strict' = 'strict',\n): ShieldRule => rule({ cache })(async (parent, args, ctx) => logic(parent, args, ctx))\n\n/**\n * Combines a given rule with all existing defined rules using the provided combinator\n * @param schema The existing shield schema with rules\n * @param rule The rule to be combined with existing rules\n * @param combinator The combinator to use. Eg. and | or | chain | race\n *\n * Usage:\n *\n * const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)\n */\nexport function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T> {\n return Object.fromEntries(\n Object.entries(schema).map(([key, value]) => {\n if (value.constructor.name !== 'Object') {\n return [key, combinator(rule, value)]\n }\n return [key, combineRuleWithAll(value, rule, combinator)]\n }),\n ) as ShieldSchema<T>\n}\n\n/**\n * Creates graphql shield middleware making use of generics to type the definition of the rules.\n * @param schema A mapping of schema objects to the shield rules that define the access to that object\n * @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.\n *\n * Usage:\n * import { Resolvers } from '../generated/types'\n *\n * const isCoordinator = hasRoleRule(UserRoles.Coordinator)\n * const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)\n * const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)\n *\n * const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)\n * const isDataAuthor = or(isCoordinator, isSystemAdmin)\n *\n * const shieldSchema = createShieldSchema<Resolvers>({\n * Query: {\n * '*': isAuthorisedUser,\n * user: isCoordinator,\n * findUsers: isCoordinator,\n * },\n * Mutation: {\n * '*': isDataAuthor,\n * createUser: isCoordinator,\n * },\n * // specific rules for mutations, types, fields...\n * }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})\n *\n * const unprotected = makeExecutableSchema({...})\n * return applyMiddleware(unprotected, shieldSchema)\n */\nexport function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions) {\n return shield(schema as IRules, {\n allowExternalErrors: true,\n ...options,\n })\n}\n\n/**\n * A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).\n *\n * Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.\n * Object levels may also include a `'*'` key — a fallback rule applied to any field at that\n * level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single\n * {@link ShieldRule}.\n *\n * Usage:\n *\n * const schema: ShieldSchema<Resolvers> = {\n * Query: {\n * '*': isAuthorisedUser,\n * publicThing: allow,\n * },\n * Mutation: { createUser: isCoordinator },\n * }\n */\nexport type ShieldSchema<TResolver> = TResolver extends Primitive\n ? ShieldRule\n : {\n [key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule\n } & {\n '*'?: ShieldRule\n }\n\n/**\n * Shield rule that passes when the current user has the given role.\n * @param role The role the user must have on `ctx.user.roles`.\n * @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.\n */\nexport function hasRoleRule(role: string, ruleName?: string) {\n return rule(ruleName ?? `hasRole-${role}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.roles.includes(role) === true,\n )\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles.\n * Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRule(...roles: string[]) {\n return hasAnyRoleRuleWithName(`hasAnyRole-${roles.join(',')}`, ...roles)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.\n * Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.\n * @param ruleName The name (and cache key) for the rule.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]) {\n return rule(ruleName, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => {\n return user?.roles.some((role: string) => roles.includes(role)) === true\n })\n}\n\n/**\n * Shield rule that passes when the current user has the given scope.\n * @param scope The scope the user must have on `ctx.user.scopes`.\n */\nexport function hasScopeRule(scope: string) {\n return rule(`hasScope-${scope}`, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => user?.scopes.includes(scope) === true)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given scopes.\n * @param scopes The set of scopes, any of which satisfies the rule.\n */\nexport function hasAnyScopeRule(...scopes: string[]) {\n return rule(`hasAnyScope-${scopes.join(',')}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.scopes.some((scope: string) => scopes.includes(scope)) === true,\n )\n}\n"],"names":[],"mappings":";;AAYA;;;;;;;;;;;;;;;;;;AAkBG;AACI,MAAM,UAAU,GAAG,CACxB,KAA0F,EAC1F,KAAA,GAAiC,QAAQ,KAC1B,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;AAEtF;;;;;;;;;AASG;SACa,kBAAkB,CAAI,MAAuB,EAAE,IAAgB,EAAE,UAA0B,EAAA;IACzG,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAI;QAC1C,IAAI,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ,EAAE;YACvC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC;AACA,QAAA,OAAO,CAAC,GAAG,EAAE,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CACgB;AACtB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACG,SAAU,kBAAkB,CAAiB,MAAoC,EAAE,OAAuB,EAAA;IAC9G,OAAO,MAAM,CAAC,MAAgB,EAAE;AAC9B,QAAA,mBAAmB,EAAE,IAAI;AACzB,QAAA,GAAG,OAAO;AACX,KAAA,CAAC;AACJ;AA4BA;;;;AAIG;AACG,SAAU,WAAW,CAAC,IAAY,EAAE,QAAiB,EAAA;AACzD,IAAA,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAA,QAAA,EAAW,IAAI,CAAA,CAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CACjE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAkB,KAAK,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CACzE;AACH;AAEA;;;;AAIG;AACG,SAAU,cAAc,CAAC,GAAG,KAAe,EAAA;AAC/C,IAAA,OAAO,sBAAsB,CAAC,CAAA,WAAA,EAAc,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,EAAE,GAAG,KAAK,CAAC;AAC1E;AAEA;;;;;AAKG;SACa,sBAAsB,CAAC,QAAgB,EAAE,GAAG,KAAe,EAAA;IACzE,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAkB,KAAI;QACjF,OAAO,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAY,KAAK,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI;AAC1E,IAAA,CAAC,CAAC;AACJ;AAEA;;;AAGG;AACG,SAAU,YAAY,CAAC,KAAa,EAAA;AACxC,IAAA,OAAO,IAAI,CAAC,CAAA,SAAA,EAAY,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAkB,KAAK,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACvI;AAEA;;;AAGG;AACG,SAAU,eAAe,CAAC,GAAG,MAAgB,EAAA;IACjD,OAAO,IAAI,CAAC,CAAA,YAAA,EAAe,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CACrE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAkB,KAAK,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAa,KAAK,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAC3G;AACH;;;;"}
1
+ {"version":3,"file":"shield.mjs","sources":["../src/shield.ts"],"sourcesContent":["import type { allow, and, chain, IRules, or, race } from 'graphql-shield'\nimport { rule, shield } from 'graphql-shield'\nimport type { GraphQLContext } from './context'\nimport type { Primitive } from './type-utils'\n\nexport type ShieldOptions = NonNullable<Parameters<typeof shield>[1]>\n\ntype RuleCombinator = typeof chain | typeof race | typeof or | typeof and\n// For whatever reason, graphql-shield doesn't export this type, but we can extract if from\n// what it does export.\nexport type ShieldRule = ReturnType<(typeof allow)['getRules']>[number]\n\n/**\n * Thin typed wrapper around graphql-shield's `rule(...)` for building ad-hoc shield rules.\n *\n * Lets you pass a plain predicate (sync or async, or returning an `Error`) and get back a\n * {@link ShieldRule} with the `parent`, `args`, and `ctx` arguments typed to your generics —\n * graphql-shield's native `rule` types these as `any`.\n *\n * Defaults the rule's cache to `'strict'`; use `'contextual'` when the result depends only on\n * `ctx` (e.g. the current user), so it can be reused across fields in the same request.\n *\n * @param logic Predicate returning `true` to allow, `false` to deny, or an `Error` to deny with a specific error.\n * @param cache graphql-shield cache strategy. `'strict'` (default) keys on parent+args+ctx; `'contextual'` keys on ctx only.\n *\n * Usage:\n *\n * const isOwner = createRule<GraphQLContext, { ownerId: string }>(\n * (parent, _, ctx) => parent.ownerId === ctx.user?.id,\n * )\n */\nexport const createRule = <TContext, TParent = unknown, TArgs = unknown>(\n logic: (parent: TParent, args: TArgs, ctx: TContext) => boolean | Promise<boolean> | Error,\n cache: 'contextual' | 'strict' = 'strict',\n): ShieldRule => rule({ cache })(async (parent, args, ctx) => logic(parent, args, ctx))\n\n/**\n * Combines a given rule with all existing defined rules using the provided combinator\n * @param schema The existing shield schema with rules\n * @param rule The rule to be combined with existing rules\n * @param combinator The combinator to use. Eg. and | or | chain | race\n *\n * Usage:\n *\n * const updatedSchema = combineRuleWithAll(shieldSchema, rules.accountNotBlocked, chain)\n */\nexport function combineRuleWithAll<T>(schema: ShieldSchema<T>, rule: ShieldRule, combinator: RuleCombinator): ShieldSchema<T> {\n return Object.fromEntries(\n Object.entries(schema).map(([key, value]) => {\n if (value.constructor.name !== 'Object') {\n return [key, combinator(rule, value)]\n }\n return [key, combineRuleWithAll(value, rule, combinator)]\n }),\n ) as ShieldSchema<T>\n}\n\n/**\n * Creates graphql shield middleware making use of generics to type the definition of the rules.\n * @param schema A mapping of schema objects to the shield rules that define the access to that object\n * @param options Shield options forwarded to `shield`. Defaults `allowExternalErrors` to `true`.\n *\n * Usage:\n * import { Resolvers } from '../generated/types'\n *\n * const isCoordinator = hasRoleRule(UserRoles.Coordinator)\n * const isSystemAdmin = hasRoleRule(UserRoles.SystemAdmin)\n * const isQualityAssurance = hasRoleRule(UserRoles.QualityAssurance)\n *\n * const isAuthorisedUser = or(isCoordinator, isSystemAdmin, isQualityAssurance)\n * const isDataAuthor = or(isCoordinator, isSystemAdmin)\n *\n * const shieldSchema = createShieldSchema<Resolvers>({\n * Query: {\n * '*': isAuthorisedUser,\n * user: isCoordinator,\n * findUsers: isCoordinator,\n * },\n * Mutation: {\n * '*': isDataAuthor,\n * createUser: isCoordinator,\n * },\n * // specific rules for mutations, types, fields...\n * }, { fallbackRule: isAuthorisedUser, fallbackError: unauthorisedError()})\n *\n * const unprotected = makeExecutableSchema({...})\n * return applyMiddleware(unprotected, shieldSchema)\n */\nexport function createShieldSchema<TRootResolvers>(schema: ShieldSchema<TRootResolvers>, options?: ShieldOptions) {\n return shield(schema as IRules, {\n allowExternalErrors: true,\n ...options,\n })\n}\n\n/**\n * A shield rule tree typed against a resolver map (typically your generated `Resolvers` type).\n *\n * Mirrors the shape of `TResolver` so each type/field can be assigned a {@link ShieldRule}.\n * Object levels may also include a `'*'` key — a fallback rule applied to any field at that\n * level that doesn't have an explicit rule. Leaf (primitive) positions collapse to a single\n * {@link ShieldRule}.\n *\n * Usage:\n *\n * const schema: ShieldSchema<Resolvers> = {\n * Query: {\n * '*': isAuthorisedUser,\n * publicThing: allow,\n * },\n * Mutation: { createUser: isCoordinator },\n * }\n */\nexport type ShieldSchema<TResolver> = TResolver extends Primitive\n ? ShieldRule\n : {\n [key in keyof TResolver]?: ShieldSchema<TResolver[key]> | ShieldRule\n } & {\n '*'?: ShieldRule\n }\n\n/**\n * Shield rule that passes when the current user has the given role.\n * @param role The role the user must have on `ctx.user.roles`.\n * @param ruleName Optional override for the rule's cache key. Defaults to `hasRole-${role}`.\n */\nexport function hasRoleRule(role: string, ruleName?: string): ShieldRule {\n return rule(ruleName ?? `hasRole-${role}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.roles.includes(role) === true,\n )\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles.\n * Rule name is derived from the roles; use {@link hasAnyRoleRuleWithName} to supply your own.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRule(...roles: string[]): ShieldRule {\n return hasAnyRoleRuleWithName(`hasAnyRole-${roles.join(',')}`, ...roles)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given roles, with an explicit rule name.\n * Prefer this over {@link hasAnyRoleRule} when the role list is large or dynamic and you want a stable cache key.\n * @param ruleName The name (and cache key) for the rule.\n * @param roles The set of roles, any of which satisfies the rule.\n */\nexport function hasAnyRoleRuleWithName(ruleName: string, ...roles: string[]): ShieldRule {\n return rule(ruleName, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => {\n return user?.roles.some((role: string) => roles.includes(role)) === true\n })\n}\n\n/**\n * Shield rule that passes when the current user has the given scope.\n * @param scope The scope the user must have on `ctx.user.scopes`.\n */\nexport function hasScopeRule(scope: string): ShieldRule {\n return rule(`hasScope-${scope}`, { cache: 'contextual' })((_, __, { user }: GraphQLContext) => user?.scopes.includes(scope) === true)\n}\n\n/**\n * Shield rule that passes when the current user has at least one of the given scopes.\n * @param scopes The set of scopes, any of which satisfies the rule.\n */\nexport function hasAnyScopeRule(...scopes: string[]): ShieldRule {\n return rule(`hasAnyScope-${scopes.join(',')}`, { cache: 'contextual' })(\n (_, __, { user }: GraphQLContext) => user?.scopes.some((scope: string) => scopes.includes(scope)) === true,\n )\n}\n"],"names":[],"mappings":"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"}
@@ -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,3 @@
1
+ export * from './context';
2
+ export * from './server';
3
+ export { extractTokenFromConnectionParams } from './utils';
@@ -0,0 +1,3 @@
1
+ export * from './context.js';
2
+ export * from './server.js';
3
+ export { extractTokenFromConnectionParams } from './utils.js';
@@ -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;
@@ -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;
@@ -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,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
@@ -0,0 +1,3 @@
1
+ export declare const isIntrospectionQuery: (query?: string) => boolean | undefined;
2
+ export declare const isNil: (value: unknown) => value is null | undefined;
3
+ export declare const isNotNil: <T>(value: T) => value is Exclude<T, undefined | null>;
package/utils.d.mts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const isIntrospectionQuery: (query?: string) => boolean | undefined;
2
+ export declare const isNil: (value: unknown) => value is null | undefined;
3
+ export declare const isNotNil: <T>(value: T) => value is Exclude<T, undefined | null>;