graphql-shield-node23 7.6.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/cjs/constructors.js +134 -0
  3. package/dist/cjs/generator.js +205 -0
  4. package/dist/cjs/index.js +15 -0
  5. package/dist/cjs/package.json +1 -0
  6. package/dist/cjs/rules.js +402 -0
  7. package/dist/cjs/shield.js +52 -0
  8. package/dist/cjs/types.js +2 -0
  9. package/dist/cjs/utils.js +97 -0
  10. package/dist/cjs/validation.js +84 -0
  11. package/dist/esm/constructors.js +124 -0
  12. package/dist/esm/generator.js +201 -0
  13. package/dist/esm/index.js +2 -0
  14. package/dist/esm/rules.js +366 -0
  15. package/dist/esm/shield.js +45 -0
  16. package/dist/esm/types.js +1 -0
  17. package/dist/esm/utils.js +88 -0
  18. package/dist/esm/validation.js +79 -0
  19. package/dist/package.json +47 -0
  20. package/dist/typings/constructors.d.cts +91 -0
  21. package/dist/typings/constructors.d.ts +91 -0
  22. package/dist/typings/generator.d.cts +11 -0
  23. package/dist/typings/generator.d.ts +11 -0
  24. package/dist/typings/index.d.cts +3 -0
  25. package/dist/typings/index.d.ts +3 -0
  26. package/dist/typings/rules.d.cts +159 -0
  27. package/dist/typings/rules.d.ts +159 -0
  28. package/dist/typings/shield.d.cts +11 -0
  29. package/dist/typings/shield.d.ts +11 -0
  30. package/dist/typings/types.d.cts +64 -0
  31. package/dist/typings/types.d.ts +64 -0
  32. package/dist/typings/utils.d.cts +52 -0
  33. package/dist/typings/utils.d.ts +52 -0
  34. package/dist/typings/validation.d.cts +19 -0
  35. package/dist/typings/validation.d.ts +19 -0
  36. package/package.json +67 -0
  37. package/src/constructors.ts +157 -0
  38. package/src/generator.ts +294 -0
  39. package/src/index.ts +13 -0
  40. package/src/rules.ts +521 -0
  41. package/src/shield.ts +53 -0
  42. package/src/types.ts +94 -0
  43. package/src/utils.ts +101 -0
  44. package/src/validation.ts +90 -0
  45. package/tests/__snapshots__/input.test.ts.snap +7 -0
  46. package/tests/cache.test.ts +545 -0
  47. package/tests/constructors.test.ts +136 -0
  48. package/tests/fallback.test.ts +618 -0
  49. package/tests/fragments.test.ts +113 -0
  50. package/tests/generator.test.ts +356 -0
  51. package/tests/input.test.ts +63 -0
  52. package/tests/integration.test.ts +65 -0
  53. package/tests/logic.test.ts +530 -0
  54. package/tests/utils.test.ts +55 -0
  55. package/tests/validation.test.ts +139 -0
  56. package/tsconfig.json +10 -0
@@ -0,0 +1,402 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.RuleFalse = exports.RuleTrue = exports.RuleNot = exports.RuleRace = exports.RuleChain = exports.RuleAnd = exports.RuleOr = exports.LogicRule = exports.InputRule = exports.Rule = void 0;
27
+ const Yup = __importStar(require("yup"));
28
+ const utils_js_1 = require("./utils.js");
29
+ class Rule {
30
+ constructor(name, func, constructorOptions) {
31
+ const options = this.normalizeOptions(constructorOptions);
32
+ this.name = name;
33
+ this.func = func;
34
+ this.cache = options.cache;
35
+ this.fragment = options.fragment;
36
+ }
37
+ /**
38
+ *
39
+ * @param parent
40
+ * @param args
41
+ * @param ctx
42
+ * @param info
43
+ *
44
+ * Resolves rule and writes to cache its result.
45
+ *
46
+ */
47
+ async resolve(parent, args, ctx, info, options) {
48
+ try {
49
+ /* Resolve */
50
+ const res = await this.executeRule(parent, args, ctx, info, options);
51
+ if (res instanceof Error) {
52
+ return res;
53
+ }
54
+ else if (typeof res === 'string') {
55
+ return new Error(res);
56
+ }
57
+ else if (res === true) {
58
+ return true;
59
+ }
60
+ else {
61
+ return false;
62
+ }
63
+ }
64
+ catch (err) {
65
+ if (options.debug) {
66
+ throw err;
67
+ }
68
+ else {
69
+ return false;
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ *
75
+ * @param rule
76
+ *
77
+ * Compares a given rule with the current one
78
+ * and checks whether their functions are equal.
79
+ *
80
+ */
81
+ equals(rule) {
82
+ return this.func === rule.func;
83
+ }
84
+ /**
85
+ *
86
+ * Extracts fragment from the rule.
87
+ *
88
+ */
89
+ extractFragment() {
90
+ return this.fragment;
91
+ }
92
+ /**
93
+ *
94
+ * @param options
95
+ *
96
+ * Sets default values for options.
97
+ *
98
+ */
99
+ normalizeOptions(options) {
100
+ return {
101
+ cache: options.cache !== undefined
102
+ ? this.normalizeCacheOption(options.cache)
103
+ : 'no_cache',
104
+ fragment: options.fragment !== undefined ? options.fragment : undefined,
105
+ };
106
+ }
107
+ /**
108
+ *
109
+ * @param cache
110
+ *
111
+ * This ensures backward capability of shield.
112
+ *
113
+ */
114
+ normalizeCacheOption(cache) {
115
+ switch (cache) {
116
+ case true: {
117
+ return 'strict';
118
+ }
119
+ case false: {
120
+ return 'no_cache';
121
+ }
122
+ default: {
123
+ return cache;
124
+ }
125
+ }
126
+ }
127
+ /**
128
+ * Executes a rule and writes to cache if needed.
129
+ *
130
+ * @param parent
131
+ * @param args
132
+ * @param ctx
133
+ * @param info
134
+ */
135
+ executeRule(parent, args, ctx, info, options) {
136
+ switch (typeof this.cache) {
137
+ case 'function': {
138
+ /* User defined cache function. */
139
+ const key = `${this.name}-${this.cache(parent, args, ctx, info)}`;
140
+ return this.writeToCache(key)(parent, args, ctx, info);
141
+ }
142
+ case 'string': {
143
+ /* Standard cache option. */
144
+ switch (this.cache) {
145
+ case 'strict': {
146
+ const key = options.hashFunction({ parent, args });
147
+ return this.writeToCache(`${this.name}-${key}`)(parent, args, ctx, info);
148
+ }
149
+ case 'contextual': {
150
+ return this.writeToCache(this.name)(parent, args, ctx, info);
151
+ }
152
+ case 'no_cache': {
153
+ return this.func(parent, args, ctx, info);
154
+ }
155
+ }
156
+ }
157
+ /* istanbul ignore next */
158
+ default: {
159
+ throw new Error(`Unsupported cache format: ${typeof this.cache}`);
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Writes or reads result from cache.
165
+ *
166
+ * @param key
167
+ */
168
+ writeToCache(key) {
169
+ return (parent, args, ctx, info) => {
170
+ if (!ctx._shield.cache[key]) {
171
+ ctx._shield.cache[key] = this.func(parent, args, ctx, info);
172
+ }
173
+ return ctx._shield.cache[key];
174
+ };
175
+ }
176
+ }
177
+ exports.Rule = Rule;
178
+ class InputRule extends Rule {
179
+ constructor(name, schema, options) {
180
+ const validationFunction = (parent, args, ctx) => schema(Yup, ctx)
181
+ .validate(args, options)
182
+ .then(() => true)
183
+ .catch((err) => err);
184
+ super(name, validationFunction, { cache: 'strict', fragment: undefined });
185
+ }
186
+ }
187
+ exports.InputRule = InputRule;
188
+ class LogicRule {
189
+ constructor(rules) {
190
+ this.rules = rules;
191
+ }
192
+ /**
193
+ * By default logic rule resolves to false.
194
+ */
195
+ async resolve(parent, args, ctx, info, options) {
196
+ return false;
197
+ }
198
+ /**
199
+ * Evaluates all the rules.
200
+ */
201
+ async evaluate(parent, args, ctx, info, options) {
202
+ const rules = this.getRules();
203
+ const tasks = rules.map((rule) => rule.resolve(parent, args, ctx, info, options));
204
+ return Promise.all(tasks);
205
+ }
206
+ /**
207
+ * Returns rules in a logic rule.
208
+ */
209
+ getRules() {
210
+ return this.rules;
211
+ }
212
+ /**
213
+ * Extracts fragments from the defined rules.
214
+ */
215
+ extractFragments() {
216
+ const fragments = this.rules.reduce((fragments, rule) => {
217
+ if ((0, utils_js_1.isLogicRule)(rule)) {
218
+ return fragments.concat(...rule.extractFragments());
219
+ }
220
+ const fragment = rule.extractFragment();
221
+ if (fragment)
222
+ return fragments.concat(fragment);
223
+ return fragments;
224
+ }, []);
225
+ return fragments;
226
+ }
227
+ }
228
+ exports.LogicRule = LogicRule;
229
+ // Extended Types
230
+ class RuleOr extends LogicRule {
231
+ constructor(rules) {
232
+ super(rules);
233
+ }
234
+ /**
235
+ * Makes sure that at least one of them has evaluated to true.
236
+ */
237
+ async resolve(parent, args, ctx, info, options) {
238
+ const result = await this.evaluate(parent, args, ctx, info, options);
239
+ if (result.every((res) => res !== true)) {
240
+ const customError = result.find((res) => res instanceof Error);
241
+ return customError || false;
242
+ }
243
+ else {
244
+ return true;
245
+ }
246
+ }
247
+ }
248
+ exports.RuleOr = RuleOr;
249
+ class RuleAnd extends LogicRule {
250
+ constructor(rules) {
251
+ super(rules);
252
+ }
253
+ /**
254
+ * Makes sure that all of them have resolved to true.
255
+ */
256
+ async resolve(parent, args, ctx, info, options) {
257
+ const result = await this.evaluate(parent, args, ctx, info, options);
258
+ if (result.some((res) => res !== true)) {
259
+ const customError = result.find((res) => res instanceof Error);
260
+ return customError || false;
261
+ }
262
+ else {
263
+ return true;
264
+ }
265
+ }
266
+ }
267
+ exports.RuleAnd = RuleAnd;
268
+ class RuleChain extends LogicRule {
269
+ constructor(rules) {
270
+ super(rules);
271
+ }
272
+ /**
273
+ * Makes sure that all of them have resolved to true.
274
+ */
275
+ async resolve(parent, args, ctx, info, options) {
276
+ const result = await this.evaluate(parent, args, ctx, info, options);
277
+ if (result.some((res) => res !== true)) {
278
+ const customError = result.find((res) => res instanceof Error);
279
+ return customError || false;
280
+ }
281
+ else {
282
+ return true;
283
+ }
284
+ }
285
+ /**
286
+ * Evaluates all the rules.
287
+ */
288
+ async evaluate(parent, args, ctx, info, options) {
289
+ const rules = this.getRules();
290
+ return iterate(rules);
291
+ async function iterate([rule, ...otherRules]) {
292
+ if (rule === undefined)
293
+ return [];
294
+ return rule.resolve(parent, args, ctx, info, options).then((res) => {
295
+ if (res !== true) {
296
+ return [res];
297
+ }
298
+ else {
299
+ return iterate(otherRules).then((ress) => ress.concat(res));
300
+ }
301
+ });
302
+ }
303
+ }
304
+ }
305
+ exports.RuleChain = RuleChain;
306
+ class RuleRace extends LogicRule {
307
+ constructor(rules) {
308
+ super(rules);
309
+ }
310
+ /**
311
+ * Makes sure that at least one of them resolved to true.
312
+ */
313
+ async resolve(parent, args, ctx, info, options) {
314
+ const result = await this.evaluate(parent, args, ctx, info, options);
315
+ if (result.some((res) => res === true)) {
316
+ return true;
317
+ }
318
+ else {
319
+ const customError = result.find((res) => res instanceof Error);
320
+ return customError || false;
321
+ }
322
+ }
323
+ /**
324
+ * Evaluates all the rules.
325
+ */
326
+ async evaluate(parent, args, ctx, info, options) {
327
+ const rules = this.getRules();
328
+ return iterate(rules);
329
+ async function iterate([rule, ...otherRules]) {
330
+ if (rule === undefined)
331
+ return [];
332
+ return rule.resolve(parent, args, ctx, info, options).then((res) => {
333
+ if (res === true) {
334
+ return [res];
335
+ }
336
+ else {
337
+ return iterate(otherRules).then((ress) => ress.concat(res));
338
+ }
339
+ });
340
+ }
341
+ }
342
+ }
343
+ exports.RuleRace = RuleRace;
344
+ class RuleNot extends LogicRule {
345
+ constructor(rule, error) {
346
+ super([rule]);
347
+ this.error = error;
348
+ }
349
+ /**
350
+ *
351
+ * @param parent
352
+ * @param args
353
+ * @param ctx
354
+ * @param info
355
+ *
356
+ * Negates the result.
357
+ *
358
+ */
359
+ async resolve(parent, args, ctx, info, options) {
360
+ const [res] = await this.evaluate(parent, args, ctx, info, options);
361
+ if (res instanceof Error) {
362
+ return true;
363
+ }
364
+ else if (res !== true) {
365
+ return true;
366
+ }
367
+ else {
368
+ if (this.error)
369
+ return this.error;
370
+ return false;
371
+ }
372
+ }
373
+ }
374
+ exports.RuleNot = RuleNot;
375
+ class RuleTrue extends LogicRule {
376
+ constructor() {
377
+ super([]);
378
+ }
379
+ /**
380
+ *
381
+ * Always true.
382
+ *
383
+ */
384
+ async resolve() {
385
+ return true;
386
+ }
387
+ }
388
+ exports.RuleTrue = RuleTrue;
389
+ class RuleFalse extends LogicRule {
390
+ constructor() {
391
+ super([]);
392
+ }
393
+ /**
394
+ *
395
+ * Always false.
396
+ *
397
+ */
398
+ async resolve() {
399
+ return false;
400
+ }
401
+ }
402
+ exports.RuleFalse = RuleFalse;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.shield = void 0;
7
+ const object_hash_1 = __importDefault(require("object-hash"));
8
+ const graphql_middleware_1 = require("graphql-middleware");
9
+ const validation_js_1 = require("./validation.js");
10
+ const generator_js_1 = require("./generator.js");
11
+ const constructors_js_1 = require("./constructors.js");
12
+ const utils_js_1 = require("./utils.js");
13
+ /**
14
+ *
15
+ * @param options
16
+ *
17
+ * Makes sure all of defined rules are in accord with the options
18
+ * shield can process.
19
+ *
20
+ */
21
+ function normalizeOptions(options) {
22
+ if (typeof options.fallbackError === 'string') {
23
+ options.fallbackError = new Error(options.fallbackError);
24
+ }
25
+ return {
26
+ debug: options.debug !== undefined ? options.debug : false,
27
+ allowExternalErrors: (0, utils_js_1.withDefault)(false)(options.allowExternalErrors),
28
+ fallbackRule: (0, utils_js_1.withDefault)(constructors_js_1.allow)(options.fallbackRule),
29
+ fallbackError: (0, utils_js_1.withDefault)(new Error('Not Authorised!'))(options.fallbackError),
30
+ hashFunction: (0, utils_js_1.withDefault)(object_hash_1.default)(options.hashFunction),
31
+ };
32
+ }
33
+ /**
34
+ *
35
+ * @param ruleTree
36
+ * @param options
37
+ *
38
+ * Validates rules and generates middleware from defined rule tree.
39
+ *
40
+ */
41
+ function shield(ruleTree, options = {}) {
42
+ const normalizedOptions = normalizeOptions(options);
43
+ const ruleTreeValidity = (0, validation_js_1.validateRuleTree)(ruleTree);
44
+ if (ruleTreeValidity.status === 'ok') {
45
+ const generatorFunction = (0, generator_js_1.generateMiddlewareGeneratorFromRuleTree)(ruleTree, normalizedOptions);
46
+ return (0, graphql_middleware_1.middleware)(generatorFunction);
47
+ }
48
+ else {
49
+ throw new validation_js_1.ValidationError(ruleTreeValidity.message);
50
+ }
51
+ }
52
+ exports.shield = shield;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withDefault = exports.flattenObjectOf = exports.isRuleFieldMap = exports.isRuleFunction = exports.isLogicRule = exports.isRule = void 0;
4
+ const rules_js_1 = require("./rules.js");
5
+ /**
6
+ *
7
+ * @param x
8
+ *
9
+ * Makes sure that a certain field is a rule.
10
+ *
11
+ */
12
+ function isRule(x) {
13
+ return (x instanceof rules_js_1.Rule || (x && x.constructor && x.constructor.name === 'Rule'));
14
+ }
15
+ exports.isRule = isRule;
16
+ /**
17
+ *
18
+ * @param x
19
+ *
20
+ * Makes sure that a certain field is a logic rule.
21
+ *
22
+ */
23
+ function isLogicRule(x) {
24
+ return (x instanceof rules_js_1.LogicRule ||
25
+ (x &&
26
+ x.constructor &&
27
+ (x.constructor.name === 'RuleOr' ||
28
+ x.constructor.name === 'RuleAnd' ||
29
+ x.constructor.name === 'RuleChain' ||
30
+ x.constructor.name === 'RuleRace' ||
31
+ x.constructor.name === 'RuleNot' ||
32
+ x.constructor.name === 'RuleTrue' ||
33
+ x.constructor.name === 'RuleFalse')));
34
+ }
35
+ exports.isLogicRule = isLogicRule;
36
+ /**
37
+ *
38
+ * @param x
39
+ *
40
+ * Makes sure that a certain field is a rule or a logic rule.
41
+ *
42
+ */
43
+ function isRuleFunction(x) {
44
+ return isRule(x) || isLogicRule(x);
45
+ }
46
+ exports.isRuleFunction = isRuleFunction;
47
+ /**
48
+ *
49
+ * @param x
50
+ *
51
+ * Determines whether a certain field is rule field map or not.
52
+ *
53
+ */
54
+ function isRuleFieldMap(x) {
55
+ return (typeof x === 'object' &&
56
+ Object.values(x).every((rule) => isRuleFunction(rule)));
57
+ }
58
+ exports.isRuleFieldMap = isRuleFieldMap;
59
+ /**
60
+ *
61
+ * @param obj
62
+ * @param func
63
+ *
64
+ * Flattens object of particular type by checking if the leaf
65
+ * evaluates to true from particular function.
66
+ *
67
+ */
68
+ function flattenObjectOf(obj, f) {
69
+ const values = Object.keys(obj).reduce((acc, key) => {
70
+ const val = obj[key];
71
+ if (f(val)) {
72
+ return [...acc, val];
73
+ }
74
+ else if (typeof val === 'object' && !f(val)) {
75
+ return [...acc, ...flattenObjectOf(val, f)];
76
+ }
77
+ else {
78
+ return acc;
79
+ }
80
+ }, []);
81
+ return values;
82
+ }
83
+ exports.flattenObjectOf = flattenObjectOf;
84
+ /**
85
+ *
86
+ * Returns fallback is provided value is undefined
87
+ *
88
+ * @param fallback
89
+ */
90
+ function withDefault(fallback) {
91
+ return (value) => {
92
+ if (value === undefined)
93
+ return fallback;
94
+ return value;
95
+ };
96
+ }
97
+ exports.withDefault = withDefault;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationError = exports.validateRuleTree = void 0;
4
+ const utils_js_1 = require("./utils.js");
5
+ /**
6
+ *
7
+ * @param ruleTree
8
+ *
9
+ * Validates the rule tree declaration by checking references of rule
10
+ * functions. We deem rule tree valid if no two rules with the same name point
11
+ * to different rules.
12
+ *
13
+ */
14
+ function validateRuleTree(ruleTree) {
15
+ const rules = extractRules(ruleTree);
16
+ const valid = rules.reduce(({ map, duplicates }, rule) => {
17
+ if (!map.has(rule.name)) {
18
+ return { map: map.set(rule.name, rule), duplicates };
19
+ }
20
+ else if (!map.get(rule.name).equals(rule) &&
21
+ !duplicates.includes(rule.name)) {
22
+ return {
23
+ map: map.set(rule.name, rule),
24
+ duplicates: [...duplicates, rule.name],
25
+ };
26
+ }
27
+ else {
28
+ return { map, duplicates };
29
+ }
30
+ }, { map: new Map(), duplicates: [] });
31
+ if (valid.duplicates.length === 0) {
32
+ return { status: 'ok' };
33
+ }
34
+ else {
35
+ const duplicates = valid.duplicates.join(', ');
36
+ return {
37
+ status: 'err',
38
+ message: `There seem to be multiple definitions of these rules: ${duplicates}`,
39
+ };
40
+ }
41
+ /* Helper functions */
42
+ /**
43
+ *
44
+ * @param ruleTree
45
+ *
46
+ * Extracts rules from rule tree.
47
+ *
48
+ */
49
+ function extractRules(ruleTree) {
50
+ const resolvers = (0, utils_js_1.flattenObjectOf)(ruleTree, utils_js_1.isRuleFunction);
51
+ const rules = resolvers.reduce((rules, rule) => {
52
+ if ((0, utils_js_1.isLogicRule)(rule)) {
53
+ return [...rules, ...extractLogicRules(rule)];
54
+ }
55
+ else {
56
+ return [...rules, rule];
57
+ }
58
+ }, []);
59
+ return rules;
60
+ }
61
+ /**
62
+ *
63
+ * Recursively extracts Rules from LogicRule
64
+ *
65
+ * @param rule
66
+ */
67
+ function extractLogicRules(rule) {
68
+ return rule.getRules().reduce((acc, shieldRule) => {
69
+ if ((0, utils_js_1.isLogicRule)(shieldRule)) {
70
+ return [...acc, ...extractLogicRules(shieldRule)];
71
+ }
72
+ else {
73
+ return [...acc, shieldRule];
74
+ }
75
+ }, []);
76
+ }
77
+ }
78
+ exports.validateRuleTree = validateRuleTree;
79
+ class ValidationError extends Error {
80
+ constructor(message) {
81
+ super(message);
82
+ }
83
+ }
84
+ exports.ValidationError = ValidationError;