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

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/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.4",
8
8
  "private": false,
9
9
  "description": "A set of core GraphQL utilities that MakerX uses to build GraphQL APIs",
10
10
  "author": "MakerX",
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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"}