graphql-query-depth-limit-esm 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/index.cjs +57 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +57 -27
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/graphql-query-depth-limit-esm)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
[](https://nodejs.org/)
|
|
10
|
+
[](https://www.npmjs.com/package/graphql-query-depth-limit-esm)
|
|
10
11
|
|
|
11
12
|
Production-ready GraphQL query depth limiting as a validation rule. Prevents denial-of-service attacks from deeply nested queries by enforcing a configurable maximum depth.
|
|
12
13
|
|
|
@@ -42,12 +43,17 @@ yarn add graphql-query-depth-limit-esm graphql
|
|
|
42
43
|
## Quick Start
|
|
43
44
|
|
|
44
45
|
```ts
|
|
45
|
-
import { validate } from "graphql";
|
|
46
|
+
import { specifiedRules, validate } from "graphql";
|
|
46
47
|
import { depthLimit } from "graphql-query-depth-limit-esm";
|
|
47
48
|
|
|
48
|
-
const errors = validate(schema, document, [
|
|
49
|
+
const errors = validate(schema, document, [
|
|
50
|
+
...specifiedRules,
|
|
51
|
+
depthLimit(7, { useDirective: true }),
|
|
52
|
+
]);
|
|
49
53
|
```
|
|
50
54
|
|
|
55
|
+
When calling `validate()` directly, include `specifiedRules` unless you intentionally want to replace GraphQL's default validation rules.
|
|
56
|
+
|
|
51
57
|
The recommended way to use `graphql-query-depth-limit-esm` is the `@depth` directive — a global maximum protects your API, while per-field overrides can tighten limits (and can opt into deeper nesting when `directiveMode: "override"` is enabled).
|
|
52
58
|
|
|
53
59
|
### Global Limit with Per-Field Overrides
|
|
@@ -85,7 +91,7 @@ Build the schema with the directive type defs, then enable directive support via
|
|
|
85
91
|
```ts
|
|
86
92
|
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
87
93
|
import { depthDirectiveTypeDefs, depthLimit } from "graphql-query-depth-limit-esm";
|
|
88
|
-
import { validate } from "graphql";
|
|
94
|
+
import { specifiedRules, validate } from "graphql";
|
|
89
95
|
|
|
90
96
|
const schema = makeExecutableSchema({
|
|
91
97
|
typeDefs: [depthDirectiveTypeDefs, yourTypeDefs],
|
|
@@ -94,6 +100,7 @@ const schema = makeExecutableSchema({
|
|
|
94
100
|
|
|
95
101
|
// Global max of 7 — @depth directives can tighten but never exceed this limit
|
|
96
102
|
const errors = validate(schema, document, [
|
|
103
|
+
...specifiedRules,
|
|
97
104
|
depthLimit(7, { useDirective: true }),
|
|
98
105
|
]);
|
|
99
106
|
```
|
|
@@ -152,11 +159,11 @@ For straightforward global limiting without per-field overrides, call [`depthLim
|
|
|
152
159
|
|
|
153
160
|
```ts
|
|
154
161
|
import { depthLimit } from "graphql-query-depth-limit-esm";
|
|
155
|
-
import { validate } from "graphql";
|
|
162
|
+
import { specifiedRules, validate } from "graphql";
|
|
156
163
|
|
|
157
164
|
// Reject queries deeper than 10 levels
|
|
158
165
|
const rule = depthLimit(10);
|
|
159
|
-
const errors = validate(schema, document, [rule]);
|
|
166
|
+
const errors = validate(schema, document, [...specifiedRules, rule]);
|
|
160
167
|
```
|
|
161
168
|
|
|
162
169
|
### With Apollo Server
|
package/dist/index.cjs
CHANGED
|
@@ -27,10 +27,10 @@ __export(index_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
29
|
// src/constants.ts
|
|
30
|
-
var ERROR_CODES = {
|
|
30
|
+
var ERROR_CODES = Object.freeze({
|
|
31
31
|
IGNORE_RULE_ERROR: "IGNORE_RULE_ERROR",
|
|
32
32
|
QUERY_TOO_DEEP: "QUERY_TOO_DEEP"
|
|
33
|
-
};
|
|
33
|
+
});
|
|
34
34
|
|
|
35
35
|
// src/depth-limit.ts
|
|
36
36
|
var import_graphql3 = require("graphql");
|
|
@@ -132,6 +132,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
132
132
|
if (!frame.node.selectionSet) {
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
|
+
const nextFrames = [];
|
|
135
136
|
for (const selection of frame.node.selectionSet.selections) {
|
|
136
137
|
switch (selection.kind) {
|
|
137
138
|
case import_graphql2.Kind.FIELD: {
|
|
@@ -213,7 +214,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
213
214
|
deepestViolation = violation;
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
|
-
|
|
217
|
+
nextFrames.push({
|
|
217
218
|
currentDepth: newDepth,
|
|
218
219
|
hasDirectiveLimit,
|
|
219
220
|
ignoredFieldsOnPath,
|
|
@@ -237,7 +238,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
237
238
|
const fragmentVisited = new Set(frame.visitedFragments);
|
|
238
239
|
fragmentVisited.add(fragmentName);
|
|
239
240
|
const parentType2 = fragment.typeCondition ? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
240
|
-
|
|
241
|
+
nextFrames.push({
|
|
241
242
|
currentDepth: frame.currentDepth,
|
|
242
243
|
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
243
244
|
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
@@ -251,7 +252,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
251
252
|
}
|
|
252
253
|
case import_graphql2.Kind.INLINE_FRAGMENT: {
|
|
253
254
|
const parentType2 = selection.typeCondition ? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
254
|
-
|
|
255
|
+
nextFrames.push({
|
|
255
256
|
currentDepth: frame.currentDepth,
|
|
256
257
|
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
257
258
|
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
@@ -265,13 +266,22 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
265
266
|
}
|
|
266
267
|
default: {
|
|
267
268
|
const exhaustiveCheck = selection;
|
|
268
|
-
|
|
269
|
+
throwUnhandledSelectionKind(exhaustiveCheck);
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
272
|
}
|
|
273
|
+
for (let index = nextFrames.length - 1; index >= 0; index--) {
|
|
274
|
+
const nextFrame = nextFrames[index];
|
|
275
|
+
if (nextFrame) {
|
|
276
|
+
stack.push(nextFrame);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
272
279
|
}
|
|
273
280
|
return { depth: globalMaxDepth, violation: deepestViolation };
|
|
274
281
|
}
|
|
282
|
+
function throwUnhandledSelectionKind(selection) {
|
|
283
|
+
throw new Error(`Unhandled selection kind: ${selection.kind}`);
|
|
284
|
+
}
|
|
275
285
|
function collectInterfaces(type) {
|
|
276
286
|
const interfaces = [];
|
|
277
287
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -385,19 +395,13 @@ function assertBooleanOption(name, value) {
|
|
|
385
395
|
function createValidationRule(maxDepth, options, callback) {
|
|
386
396
|
const shortCircuit = options?.shortCircuit ?? callback == null;
|
|
387
397
|
return function depthLimitValidationRule(context) {
|
|
388
|
-
let anonymousCount = 0;
|
|
389
398
|
const caches = createTraversalCaches();
|
|
390
399
|
const document = context.getDocument();
|
|
391
|
-
const depths = callback ?
|
|
400
|
+
const depths = callback ? createDepthResultRecord() : void 0;
|
|
392
401
|
const { fragments, operations } = extractDefinitions(document.definitions);
|
|
393
402
|
const schema = context.getSchema() ?? void 0;
|
|
394
403
|
const useDirective = Boolean(schema) && (options?.useDirective ?? false);
|
|
395
|
-
const
|
|
396
|
-
for (const op of operations) {
|
|
397
|
-
if (op.name?.value) {
|
|
398
|
-
namedOperationNames.add(op.name.value);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
404
|
+
const nextOperationName = createOperationNameAllocator(operations);
|
|
401
405
|
const config = {
|
|
402
406
|
caseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,
|
|
403
407
|
directiveMode: options?.directiveMode ?? "cap",
|
|
@@ -418,18 +422,7 @@ function createValidationRule(maxDepth, options, callback) {
|
|
|
418
422
|
subscription: void 0
|
|
419
423
|
};
|
|
420
424
|
for (const operation of operations) {
|
|
421
|
-
|
|
422
|
-
if (operation.name?.value) {
|
|
423
|
-
operationName = operation.name.value;
|
|
424
|
-
} else {
|
|
425
|
-
let candidate = anonymousCount === 0 ? "anonymous" : `anonymous_${anonymousCount}`;
|
|
426
|
-
while (namedOperationNames.has(candidate)) {
|
|
427
|
-
anonymousCount++;
|
|
428
|
-
candidate = `anonymous_${anonymousCount}`;
|
|
429
|
-
}
|
|
430
|
-
operationName = candidate;
|
|
431
|
-
anonymousCount++;
|
|
432
|
-
}
|
|
425
|
+
const operationName = nextOperationName(operation);
|
|
433
426
|
const rootType = rootTypeMap[operation.operation];
|
|
434
427
|
let result;
|
|
435
428
|
try {
|
|
@@ -475,7 +468,7 @@ function createValidationRule(maxDepth, options, callback) {
|
|
|
475
468
|
}
|
|
476
469
|
}
|
|
477
470
|
if (callback && depths) {
|
|
478
|
-
callback(depths);
|
|
471
|
+
callback({ ...depths });
|
|
479
472
|
}
|
|
480
473
|
return {};
|
|
481
474
|
};
|
|
@@ -540,6 +533,43 @@ function normalizeIgnoreRules(ignore) {
|
|
|
540
533
|
}
|
|
541
534
|
return rules;
|
|
542
535
|
}
|
|
536
|
+
function createDepthResultRecord() {
|
|
537
|
+
return /* @__PURE__ */ Object.create(null);
|
|
538
|
+
}
|
|
539
|
+
function createOperationNameAllocator(operations) {
|
|
540
|
+
const explicitNames = /* @__PURE__ */ new Set();
|
|
541
|
+
for (const operation of operations) {
|
|
542
|
+
if (operation.name?.value) {
|
|
543
|
+
explicitNames.add(operation.name.value);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
547
|
+
const namedCounts = /* @__PURE__ */ new Map();
|
|
548
|
+
let anonymousCount = 0;
|
|
549
|
+
return (operation) => {
|
|
550
|
+
const explicitName = operation.name?.value;
|
|
551
|
+
if (explicitName) {
|
|
552
|
+
let suffix2 = namedCounts.get(explicitName) ?? 0;
|
|
553
|
+
let candidate2 = suffix2 === 0 ? explicitName : `${explicitName}_${suffix2}`;
|
|
554
|
+
while (usedNames.has(candidate2) || suffix2 > 0 && explicitNames.has(candidate2)) {
|
|
555
|
+
suffix2++;
|
|
556
|
+
candidate2 = `${explicitName}_${suffix2}`;
|
|
557
|
+
}
|
|
558
|
+
namedCounts.set(explicitName, suffix2 + 1);
|
|
559
|
+
usedNames.add(candidate2);
|
|
560
|
+
return candidate2;
|
|
561
|
+
}
|
|
562
|
+
let suffix = anonymousCount;
|
|
563
|
+
let candidate = suffix === 0 ? "anonymous" : `anonymous_${suffix}`;
|
|
564
|
+
while (usedNames.has(candidate) || explicitNames.has(candidate)) {
|
|
565
|
+
suffix++;
|
|
566
|
+
candidate = `anonymous_${suffix}`;
|
|
567
|
+
}
|
|
568
|
+
anonymousCount = suffix + 1;
|
|
569
|
+
usedNames.add(candidate);
|
|
570
|
+
return candidate;
|
|
571
|
+
};
|
|
572
|
+
}
|
|
543
573
|
function setDepthResult(target, key, value) {
|
|
544
574
|
Object.defineProperty(target, key, {
|
|
545
575
|
configurable: true,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/depth-limit.ts","../src/depth-engine.ts","../src/directives.ts","../src/ignore.ts"],"sourcesContent":["// Exports are sorted by module path (biome's organizeImports rule), not by\n// export name. This is enforced by the linter and is intentional.\nexport { ERROR_CODES } from \"./constants.js\";\nexport { depthLimit } from \"./depth-limit.js\";\nexport { depthDirectiveTypeDefs } from \"./directives.js\";\nexport type {\n\tDepthCallback,\n\tDepthLimitFunction,\n\tDepthLimitOptions,\n\tDirectiveMode,\n\tIgnoreMode,\n\tIgnoreRule,\n\tIntrospectionMode,\n} from \"./types.js\";\n","/**\n * Error extension codes for depth limit violations.\n */\nexport const ERROR_CODES = {\n\tIGNORE_RULE_ERROR: \"IGNORE_RULE_ERROR\",\n\tQUERY_TOO_DEEP: \"QUERY_TOO_DEEP\",\n} as const;\n","import {\n\ttype ASTVisitor,\n\tGraphQLError,\n\ttype ValidationContext,\n\ttype ValidationRule,\n} from \"graphql\";\n\nimport { ERROR_CODES } from \"./constants.js\";\nimport {\n\tcalculateDepth,\n\tcreateTraversalCaches,\n\textractDefinitions,\n\ttype TraversalConfig,\n} from \"./depth-engine.js\";\nimport type { DepthCallback, DepthLimitFunction, DepthLimitOptions, IgnoreRule } from \"./types.js\";\n\n/** Valid values for the `directiveMode` option. */\nconst DIRECTIVE_MODES = new Set<string>([\"cap\", \"override\"]);\n\n/** Valid values for the `ignoreMode` option. */\nconst IGNORE_MODES = new Set<string>([\"exclude\", \"skip\"]);\n\n/** Valid values for the `ignoreIntrospection` option. */\nconst INTROSPECTION_MODES = new Set<string>([\"all\", \"none\", \"typename\"]);\n\n/**\n * Normalized depthLimit options with validated ignore rules.\n */\ntype NormalizedDepthLimitOptions = Omit<DepthLimitOptions, \"ignore\"> & {\n\tignore?: IgnoreRule[];\n};\n\n/**\n * Creates a GraphQL validation rule that limits query depth.\n *\n * Prevents DoS attacks and resource exhaustion from excessively deep queries\n * by enforcing a maximum depth on operations. Supports per-field overrides\n * via the `@depth` directive, customizable ignore rules, and an optional\n * callback for monitoring.\n *\n * Security considerations:\n * - The global `maxDepth` is a hard ceiling by default (`directiveMode: \"cap\"`)\n * - Correctly handles fragments reused at different depths\n * - Circular fragment references are detected per-path\n * - Only `__typename` is ignored by default (`ignoreIntrospection: \"typename\"`)\n * - Short-circuits on first violation when no callback is provided\n *\n * Limitations:\n * - Variables in `@depth` directives are not supported (falls back to global limit)\n * - Field names are case-sensitive by default (use `caseInsensitiveIgnore` if needed)\n * - `useDirective: true` requires a schema in the validation context; without one\n * it silently falls back to the global limit (directives cannot be resolved)\n * - RegExp ignore rules are executed against field names; patterns with catastrophic\n * backtracking (e.g., `/^(a+)+$/`) may cause performance issues\n *\n * @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)\n * @param options - Optional configuration for ignore rules, directives, and case sensitivity\n * @param callback - Optional callback invoked with depth results for each operation\n * @returns A GraphQL validation rule function\n * @throws {Error} If `maxDepth` is not a non-negative integer\n *\n * @example\n * ```typescript\n * import { depthLimit } from \"graphql-query-depth-limit-esm\";\n * import { validate } from \"graphql\";\n *\n * const errors = validate(schema, document, [\n * depthLimit(5, {\n * ignore: [\"friends\", /.*Connection$/],\n * useDirective: true,\n * }),\n * ]);\n *\n * const withCallback = depthLimit(5, (depths) => {\n * console.log(depths);\n * });\n * ```\n */\nexport function depthLimit(maxDepth: number, callback?: DepthCallback): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions | DepthCallback,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 0) {\n\t\tthrow new Error(`Invalid maxDepth: ${maxDepth}. Must be a non-negative integer.`);\n\t}\n\n\tconst normalized = normalizeDepthLimitArgs(options, callback);\n\n\treturn createValidationRule(maxDepth, normalized.options, normalized.callback);\n}\n\n/** Compile-time check that the implementation satisfies the public type. */\ndepthLimit satisfies DepthLimitFunction;\n\n/**\n * Validates that a value is a boolean or undefined.\n */\nfunction assertBooleanOption(name: string, value: unknown): void {\n\tif (value !== undefined && typeof value !== \"boolean\") {\n\t\tthrow new TypeError(`Invalid ${name}: expected boolean, received ${typeof value}.`);\n\t}\n}\n\n/**\n * Creates the validation rule closure with validated parameters.\n */\nfunction createValidationRule(\n\tmaxDepth: number,\n\toptions?: NormalizedDepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tconst shortCircuit = options?.shortCircuit ?? callback == null;\n\n\treturn function depthLimitValidationRule(context: ValidationContext): ASTVisitor {\n\t\tlet anonymousCount = 0;\n\t\tconst caches = createTraversalCaches();\n\t\tconst document = context.getDocument();\n\t\tconst depths: Record<string, number> | undefined = callback ? {} : undefined;\n\t\tconst { fragments, operations } = extractDefinitions(document.definitions);\n\t\tconst schema = context.getSchema() ?? undefined;\n\t\t// By design: when useDirective is true but no schema is available,\n\t\t// directives silently fall back to the global maxDepth. This is not a\n\t\t// \"silent failure\" — directives cannot be resolved without type info,\n\t\t// so the global limit is the correct and safe default. Emitting an\n\t\t// error here would penalize valid schema-less contexts (e.g., custom\n\t\t// ValidationContext wrappers) where the user intentionally omits the\n\t\t// schema. See DepthLimitOptions.useDirective JSDoc for documentation.\n\t\tconst useDirective = Boolean(schema) && (options?.useDirective ?? false);\n\n\t\t// Pre-collect named operation names to avoid key collisions with\n\t\t// generated anonymous operation keys (e.g., \"anonymous\", \"anonymous_1\").\n\t\tconst namedOperationNames = new Set<string>();\n\t\tfor (const op of operations) {\n\t\t\tif (op.name?.value) {\n\t\t\t\tnamedOperationNames.add(op.name.value);\n\t\t\t}\n\t\t}\n\n\t\tconst config: TraversalConfig = {\n\t\t\tcaseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,\n\t\t\tdirectiveMode: options?.directiveMode ?? \"cap\",\n\t\t\tignore: options?.ignore,\n\t\t\tignoreMode: options?.ignoreMode ?? \"exclude\",\n\t\t\tintrospectionMode: options?.ignoreIntrospection ?? \"typename\",\n\t\t\tlimitIgnoredRecursion: options?.limitIgnoredRecursion ?? false,\n\t\t\tshortCircuit,\n\t\t\tuseDirective,\n\t\t};\n\n\t\tconst rootTypeMap = schema\n\t\t\t? {\n\t\t\t\t\tmutation: schema.getMutationType() ?? undefined,\n\t\t\t\t\tquery: schema.getQueryType() ?? undefined,\n\t\t\t\t\tsubscription: schema.getSubscriptionType() ?? undefined,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tmutation: undefined,\n\t\t\t\t\tquery: undefined,\n\t\t\t\t\tsubscription: undefined,\n\t\t\t\t};\n\n\t\tfor (const operation of operations) {\n\t\t\tlet operationName: string;\n\t\t\tif (operation.name?.value) {\n\t\t\t\toperationName = operation.name.value;\n\t\t\t} else {\n\t\t\t\tlet candidate = anonymousCount === 0 ? \"anonymous\" : `anonymous_${anonymousCount}`;\n\t\t\t\twhile (namedOperationNames.has(candidate)) {\n\t\t\t\t\tanonymousCount++;\n\t\t\t\t\tcandidate = `anonymous_${anonymousCount}`;\n\t\t\t\t}\n\t\t\t\toperationName = candidate;\n\t\t\t\tanonymousCount++;\n\t\t\t}\n\t\t\tconst rootType = rootTypeMap[operation.operation];\n\n\t\t\tlet result: ReturnType<typeof calculateDepth>;\n\t\t\ttry {\n\t\t\t\tresult = calculateDepth(caches, config, fragments, maxDepth, operation, rootType, schema);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && error.name === \"IgnoreRuleError\") {\n\t\t\t\t\tcontext.reportError(\n\t\t\t\t\t\tnew GraphQLError(error.message, {\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.IGNORE_RULE_ERROR,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: [operation],\n\t\t\t\t\t\t\toriginalError: error,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tif (depths) {\n\t\t\t\tsetDepthResult(depths, operationName, result.depth);\n\t\t\t}\n\n\t\t\tif (result.violation) {\n\t\t\t\tconst depthValue = result.violation.depth;\n\t\t\t\tconst depthLabel = shortCircuit ? `at least ${depthValue}` : `${depthValue}`;\n\t\t\t\tconst violationPath = result.violation.path;\n\t\t\t\tconst pathSuffix = violationPath.length > 0 ? ` (at ${violationPath.join(\".\")})` : \"\";\n\n\t\t\t\tcontext.reportError(\n\t\t\t\t\tnew GraphQLError(\n\t\t\t\t\t\t`'${operationName}' has depth ${depthLabel} which exceeds maximum allowed depth of ${result.violation.maxDepth}${pathSuffix}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.QUERY_TOO_DEEP,\n\t\t\t\t\t\t\t\tdepth: depthValue,\n\t\t\t\t\t\t\t\tmaxDepth: result.violation.maxDepth,\n\t\t\t\t\t\t\t\tpath: violationPath,\n\t\t\t\t\t\t\t\tshortCircuit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: result.violation.node ? [operation, result.violation.node] : [operation],\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (callback && depths) {\n\t\t\tcallback(depths);\n\t\t}\n\n\t\t// All depth validation is performed eagerly above because fragment\n\t\t// resolution requires the full document upfront. Nothing remains\n\t\t// for the visitor traversal phase.\n\t\treturn {};\n\t};\n}\n\n/**\n * Determines whether a value is a valid ignore rule.\n */\nfunction isIgnoreRule(rule: unknown): rule is IgnoreRule {\n\treturn typeof rule === \"function\" || rule instanceof RegExp || typeof rule === \"string\";\n}\n\n/**\n * Normalizes the optional depthLimit arguments.\n */\nfunction normalizeDepthLimitArgs(\n\toptions: DepthLimitOptions | DepthCallback | undefined,\n\tcallback: DepthCallback | undefined,\n): { callback?: DepthCallback; options?: NormalizedDepthLimitOptions } {\n\tif (callback !== undefined && typeof callback !== \"function\") {\n\t\tthrow new TypeError(\"Invalid callback: expected a function.\");\n\t}\n\n\tif (typeof options === \"function\") {\n\t\tif (callback) {\n\t\t\tthrow new TypeError(\"Invalid depthLimit arguments: callback provided twice.\");\n\t\t}\n\n\t\treturn { callback: options, options: undefined };\n\t}\n\n\tif (\n\t\toptions !== undefined &&\n\t\t(options === null || typeof options !== \"object\" || Array.isArray(options))\n\t) {\n\t\tconst receivedType = Array.isArray(options)\n\t\t\t? \"array\"\n\t\t\t: options === null\n\t\t\t\t? \"null\"\n\t\t\t\t: typeof options;\n\t\tthrow new TypeError(`Invalid options: expected an object, received ${receivedType}.`);\n\t}\n\n\treturn {\n\t\tcallback,\n\t\toptions: options ? normalizeDepthLimitOptions(options) : undefined,\n\t};\n}\n\n/**\n * Normalizes and validates depthLimit options.\n */\nfunction normalizeDepthLimitOptions(options: DepthLimitOptions): NormalizedDepthLimitOptions {\n\tassertBooleanOption(\"caseInsensitiveIgnore\", options.caseInsensitiveIgnore);\n\n\tif (options.directiveMode !== undefined && !DIRECTIVE_MODES.has(options.directiveMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid directiveMode: \"${options.directiveMode}\". Must be \"cap\" or \"override\".`,\n\t\t);\n\t}\n\n\tif (\n\t\toptions.ignoreIntrospection !== undefined &&\n\t\t!INTROSPECTION_MODES.has(options.ignoreIntrospection)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreIntrospection: \"${options.ignoreIntrospection}\". Must be \"all\", \"none\", or \"typename\".`,\n\t\t);\n\t}\n\n\tif (options.ignoreMode !== undefined && !IGNORE_MODES.has(options.ignoreMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreMode: \"${options.ignoreMode}\". Must be \"exclude\" or \"skip\".`,\n\t\t);\n\t}\n\n\tassertBooleanOption(\"limitIgnoredRecursion\", options.limitIgnoredRecursion);\n\tassertBooleanOption(\"shortCircuit\", options.shortCircuit);\n\tassertBooleanOption(\"useDirective\", options.useDirective);\n\n\tconst ignore = normalizeIgnoreRules(options.ignore);\n\treturn { ...options, ignore };\n}\n\n/**\n * Normalizes and validates ignore rules.\n */\nfunction normalizeIgnoreRules(ignore: DepthLimitOptions[\"ignore\"]): IgnoreRule[] | undefined {\n\tif (ignore == null) {\n\t\treturn undefined;\n\t}\n\n\tconst rules: unknown[] = Array.isArray(ignore) ? ignore : [ignore];\n\n\tfor (const [index, rule] of rules.entries()) {\n\t\tif (!isIgnoreRule(rule)) {\n\t\t\tconst receivedType = Array.isArray(rule) ? \"array\" : rule === null ? \"null\" : typeof rule;\n\t\t\tthrow new TypeError(\n\t\t\t\t`Invalid ignore rule at index ${index}: expected string, RegExp, or function, received ${receivedType}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn rules as IgnoreRule[];\n}\n\n/**\n * Safely assigns a depth entry without allowing prototype pollution.\n */\nfunction setDepthResult(target: Record<string, number>, key: string, value: number): void {\n\tObject.defineProperty(target, key, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tvalue,\n\t\twritable: true,\n\t});\n}\n","import {\n\ttype ASTNode,\n\ttype DefinitionNode,\n\ttype FragmentDefinitionNode,\n\ttype GraphQLField,\n\ttype GraphQLInterfaceType,\n\ttype GraphQLObjectType,\n\ttype GraphQLOutputType,\n\ttype GraphQLSchema,\n\tgetNamedType,\n\tisCompositeType,\n\tisInterfaceType,\n\tisObjectType,\n\tKind,\n\ttype OperationDefinitionNode,\n\ttype SelectionNode,\n} from \"graphql\";\n\nimport { getDepthFromDirective } from \"./directives.js\";\nimport { shouldIgnoreField } from \"./ignore.js\";\nimport type { DirectiveMode, IgnoreMode, IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Result returned by the depth calculation engine.\n */\ninterface DepthResult {\n\t/** Maximum depth found across all branches */\n\tdepth: number;\n\t/** Deepest violation found, or `null` if within limits */\n\tviolation: DepthViolation | null;\n}\n\n/**\n * Records a depth limit violation with the offending depth and its limit.\n */\ninterface DepthViolation {\n\t/** The actual depth that exceeded the limit */\n\tdepth: number;\n\t/** The maximum allowed depth that was exceeded */\n\tmaxDepth: number;\n\t/** The AST node that caused the violation, for precise error locations */\n\tnode?: ASTNode;\n\t/** Field path from the operation root to the violation point */\n\tpath: string[];\n}\n\n/**\n * Result of resolving a `@depth` directive on a field definition.\n * @internal\n */\ninterface DirectiveResolution {\n\t/** Whether a directive limit is now active on this path */\n\thasDirectiveLimit: boolean;\n\t/** The resolved maximum depth for this branch */\n\tmaxDepth: number;\n}\n\n/**\n * Lightweight linked-list node for building field paths without per-field\n * array allocations. Only materialized into `string[]` when reporting a\n * violation or populating callback results.\n * @internal\n */\ninterface PathNode {\n\t/** Parent node in the path, or `undefined` for the root */\n\tparent: PathNode | undefined;\n\t/** Field name or alias for this path segment */\n\tsegment: string;\n}\n\n/**\n * Single unit of work on the iterative DFS stack.\n * @internal\n */\ninterface StackFrame {\n\t/** Current depth at this point in traversal */\n\tcurrentDepth: number;\n\t/** Whether a `@depth` directive has already constrained this path */\n\thasDirectiveLimit: boolean;\n\t/** Ignored field names seen on the current path (for recursion guard) */\n\tignoredFieldsOnPath: Set<string>;\n\t/** Maximum allowed depth for this branch */\n\tmaxDepth: number;\n\t/** The AST node whose selectionSet should be processed */\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } };\n\t/** Parent type for field resolution */\n\tparentType: GraphQLOutputType | undefined;\n\t/** Linked-list path from the operation root to this node */\n\tpath: PathNode | undefined;\n\t/** Fragment names visited on the current path (for cycle detection) */\n\tvisitedFragments: Set<string>;\n}\n\n/**\n * Caches shared across all stack frames during a single validation run\n * to avoid repeated interface graph walks and directive AST lookups.\n * @internal\n */\ninterface TraversalCaches {\n\t/** Cached raw directive depth per `typeName:fieldName` */\n\tdirectiveDepths: Map<string, number | undefined>;\n\t/** Cached interface lists per type name */\n\tinterfaces: Map<string, GraphQLInterfaceType[]>;\n}\n\n/**\n * Immutable configuration shared across all stack frames during traversal.\n * @internal\n */\ninterface TraversalConfig {\n\t/** Whether to use case-insensitive matching for string ignore rules */\n\tcaseInsensitiveIgnore: boolean;\n\t/** Controls how `@depth` directives interact with the global `maxDepth` */\n\tdirectiveMode: DirectiveMode;\n\t/** Rules for fields to ignore in depth calculation */\n\tignore: IgnoreRule[] | undefined;\n\t/** Controls how ignored fields affect depth traversal */\n\tignoreMode: IgnoreMode;\n\t/** Controls which introspection fields are ignored */\n\tintrospectionMode: IntrospectionMode;\n\t/** Whether repeated ignored fields on a path should increment depth */\n\tlimitIgnoredRecursion: boolean;\n\t/** Whether to bail immediately on violation (when no callback needs true depth) */\n\tshortCircuit: boolean;\n\t/** Whether to check for `@depth` directives on fields */\n\tuseDirective: boolean;\n}\n\n/**\n * Calculates the depth of a GraphQL query AST node using iterative DFS.\n *\n * Handles three selection types:\n * - **Fields**: Increment depth by 1 for composite (non-scalar) fields\n * - **Fragment spreads**: Expand the fragment in-place (no depth increment)\n * - **Inline fragments**: Process in-place (no depth increment)\n *\n * Fragment cycle detection uses per-path visited sets so that the same\n * fragment reused at different depths is calculated correctly.\n *\n * When `shortCircuit` is enabled (no callback), the engine bails immediately\n * on the first violation instead of traversing the full subtree.\n *\n * Uses an explicit stack instead of recursion to prevent stack overflow\n * on deeply nested queries.\n *\n * @param caches - Shared caches for interface and directive lookups\n * @param config - Immutable traversal configuration\n * @param fragments - Map of all fragment definitions in the document\n * @param maxDepth - Maximum allowed depth for this branch\n * @param node - The AST node to calculate depth for\n * @param parentType - Root type for field resolution\n * @param schema - GraphQL schema for type resolution\n * @returns The maximum depth and the deepest violation found\n */\nfunction calculateDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tfragments: Map<string, FragmentDefinitionNode>,\n\tmaxDepth: number,\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } },\n\tparentType: GraphQLOutputType | undefined,\n\tschema: GraphQLSchema | undefined,\n): DepthResult {\n\tlet deepestViolation: DepthViolation | null = null;\n\tlet globalMaxDepth = 0;\n\n\tif (!node.selectionSet) {\n\t\treturn { depth: 0, violation: null };\n\t}\n\n\tconst stack: StackFrame[] = [\n\t\t{\n\t\t\tcurrentDepth: 0,\n\t\t\thasDirectiveLimit: false,\n\t\t\tignoredFieldsOnPath: new Set<string>(),\n\t\t\tmaxDepth,\n\t\t\tnode,\n\t\t\tparentType,\n\t\t\tpath: undefined,\n\t\t\tvisitedFragments: new Set<string>(),\n\t\t},\n\t];\n\n\tfor (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n\t\tif (!frame.node.selectionSet) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const selection of frame.node.selectionSet.selections) {\n\t\t\tswitch (selection.kind) {\n\t\t\t\tcase Kind.FIELD: {\n\t\t\t\t\tconst fieldName = selection.name.value;\n\t\t\t\t\tconst isIntrospectionField = fieldName.startsWith(\"__\");\n\n\t\t\t\t\t// When introspection is fully ignored, always skip the subtree\n\t\t\t\t\t// regardless of ignoreMode.\n\t\t\t\t\tif (config.introspectionMode === \"all\" && isIntrospectionField) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Leaf fields (no selectionSet) never contribute to depth,\n\t\t\t\t\t// so skip them before running ignore rules to avoid unnecessary\n\t\t\t\t\t// predicate evaluation and potential errors on irrelevant fields.\n\t\t\t\t\tif (!selection.selectionSet) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isIgnored = shouldIgnoreField(\n\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\tconfig.ignore,\n\t\t\t\t\t\tconfig.caseInsensitiveIgnore,\n\t\t\t\t\t\tconfig.introspectionMode,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (isIgnored && config.ignoreMode === \"skip\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Resolve field type and directive depth\n\t\t\t\t\tlet fieldMaxDepth = frame.maxDepth;\n\t\t\t\t\tlet fieldType: GraphQLOutputType | undefined;\n\t\t\t\t\tlet hasDirectiveLimit = frame.hasDirectiveLimit;\n\n\t\t\t\t\tif (schema && frame.parentType) {\n\t\t\t\t\t\tconst namedType = getNamedType(frame.parentType);\n\t\t\t\t\t\tif (isObjectType(namedType) || isInterfaceType(namedType)) {\n\t\t\t\t\t\t\tconst fieldDef = namedType.getFields()[fieldName];\n\t\t\t\t\t\t\tif (fieldDef) {\n\t\t\t\t\t\t\t\tfieldType = fieldDef.type;\n\n\t\t\t\t\t\t\t\t// Defensively skip non-composite fields that erroneously\n\t\t\t\t\t\t\t\t// have selections (normally caught by GraphQL's own\n\t\t\t\t\t\t\t\t// validation rules, but guards against miscounted depth\n\t\t\t\t\t\t\t\t// when this rule runs standalone or before other rules).\n\t\t\t\t\t\t\t\tconst resolvedType = getNamedType(fieldType);\n\t\t\t\t\t\t\t\tif (resolvedType && !isCompositeType(resolvedType)) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (config.useDirective) {\n\t\t\t\t\t\t\t\t\tconst resolved = resolveFieldDirectiveDepth(\n\t\t\t\t\t\t\t\t\t\tcaches,\n\t\t\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\t\t\tframe.currentDepth,\n\t\t\t\t\t\t\t\t\t\tframe.maxDepth,\n\t\t\t\t\t\t\t\t\t\tfieldDef,\n\t\t\t\t\t\t\t\t\t\tframe.hasDirectiveLimit,\n\t\t\t\t\t\t\t\t\t\tnamedType,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tfieldMaxDepth = resolved.maxDepth;\n\t\t\t\t\t\t\t\t\thasDirectiveLimit = resolved.hasDirectiveLimit;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Determine whether this ignored field should still increment\n\t\t\t\t\t// depth due to the recursion guard detecting repeated ignores\n\t\t\t\t\t// on the same path. The key is type-aware (`Type:field`) when\n\t\t\t\t\t// a schema is present so identically named fields on unrelated\n\t\t\t\t\t// types are tracked independently. Without a schema, the key\n\t\t\t\t\t// uses the field name alone, which may cause conservative\n\t\t\t\t\t// over-counting on paths with same-named fields on different types.\n\t\t\t\t\tlet ignoredFieldsOnPath = frame.ignoredFieldsOnPath;\n\t\t\t\t\tlet effectivelyIgnored = isIgnored;\n\n\t\t\t\t\tif (isIgnored && config.limitIgnoredRecursion) {\n\t\t\t\t\t\tconst parentName = frame.parentType ? getNamedType(frame.parentType)?.name : undefined;\n\t\t\t\t\t\tconst recursionKey = parentName ? `${parentName}:${fieldName}` : fieldName;\n\n\t\t\t\t\t\tif (ignoredFieldsOnPath.has(recursionKey)) {\n\t\t\t\t\t\t\t// Same type:field was already ignored on this path — increment depth\n\t\t\t\t\t\t\teffectivelyIgnored = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First occurrence — track it for subsequent path segments\n\t\t\t\t\t\t\tignoredFieldsOnPath = new Set(ignoredFieldsOnPath);\n\t\t\t\t\t\t\tignoredFieldsOnPath.add(recursionKey);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newDepth = effectivelyIgnored ? frame.currentDepth : frame.currentDepth + 1;\n\n\t\t\t\t\t// Use alias for path (matches the response shape clients see),\n\t\t\t\t\t// while fieldName is used for schema lookups and ignore rules.\n\t\t\t\t\tconst pathSegment = selection.alias?.value ?? fieldName;\n\t\t\t\t\tconst fieldPath = pathPush(frame.path, pathSegment);\n\n\t\t\t\t\t// Track maximum depth found\n\t\t\t\t\tif (newDepth > globalMaxDepth) {\n\t\t\t\t\t\tglobalMaxDepth = newDepth;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for violation\n\t\t\t\t\tif (newDepth > fieldMaxDepth) {\n\t\t\t\t\t\tconst violation: DepthViolation = {\n\t\t\t\t\t\t\tdepth: newDepth,\n\t\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\t\tpath: pathToArray(fieldPath),\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (config.shortCircuit) {\n\t\t\t\t\t\t\treturn { depth: newDepth, violation };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!deepestViolation || violation.depth > deepestViolation.depth) {\n\t\t\t\t\t\t\tdeepestViolation = violation;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Push children onto stack\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: newDepth,\n\t\t\t\t\t\thasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType: fieldType,\n\t\t\t\t\t\tpath: fieldPath,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.FRAGMENT_SPREAD: {\n\t\t\t\t\tconst fragmentName = selection.name.value;\n\n\t\t\t\t\t// Check membership before copying to avoid wasted allocations\n\t\t\t\t\t// when the fragment was already visited on this path.\n\t\t\t\t\tif (frame.visitedFragments.has(fragmentName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fragment = fragments.get(fragmentName);\n\t\t\t\t\tif (!fragment) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create independent copy for per-path cycle detection\n\t\t\t\t\tconst fragmentVisited = new Set(frame.visitedFragments);\n\t\t\t\t\tfragmentVisited.add(fragmentName);\n\n\t\t\t\t\tconst parentType = fragment.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: fragment,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: fragmentVisited,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.INLINE_FRAGMENT: {\n\t\t\t\t\tconst parentType = selection.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst exhaustiveCheck: never = selection;\n\t\t\t\t\tthrow new Error(`Unhandled selection kind: ${(exhaustiveCheck as SelectionNode).kind}`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { depth: globalMaxDepth, violation: deepestViolation };\n}\n\n/**\n * Collects all interfaces implemented by a type, including transitively\n * inherited interfaces (interface-implements-interface chains).\n *\n * @param type - The object or interface type to collect interfaces from\n * @returns All directly and transitively implemented interfaces\n */\nfunction collectInterfaces(type: GraphQLInterfaceType | GraphQLObjectType): GraphQLInterfaceType[] {\n\tconst interfaces: GraphQLInterfaceType[] = [];\n\tconst seen = new Set<string>();\n\tconst stack = [...type.getInterfaces()];\n\n\tfor (let iface = stack.pop(); iface !== undefined; iface = stack.pop()) {\n\t\tif (seen.has(iface.name)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tseen.add(iface.name);\n\t\tinterfaces.push(iface);\n\n\t\tfor (const parent of iface.getInterfaces()) {\n\t\t\tif (!seen.has(parent.name)) {\n\t\t\t\tstack.push(parent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn interfaces;\n}\n\n/**\n * Creates empty traversal caches for a new validation run.\n *\n * @returns Fresh caches for interface and directive lookups\n */\nfunction createTraversalCaches(): TraversalCaches {\n\treturn {\n\t\tdirectiveDepths: new Map<string, number | undefined>(),\n\t\tinterfaces: new Map<string, GraphQLInterfaceType[]>(),\n\t};\n}\n\n/**\n * Extracts all fragment and operation definitions from a GraphQL document\n * in a single pass.\n *\n * **By design:** Duplicate fragment names are silently overwritten (last wins)\n * rather than raising a validation error. This is intentional — detecting\n * duplicates is the responsibility of GraphQL's built-in `UniqueFragmentNamesRule`,\n * not a depth-limiting rule. When both rules run together (the normal case),\n * duplicates are already caught before this code executes. When used standalone,\n * last-wins is a safe, deterministic fallback that avoids coupling depth\n * validation to fragment uniqueness concerns.\n *\n * @param definitions - Array of definition nodes from the parsed document\n * @returns Fragment map and operation array\n */\nfunction extractDefinitions(definitions: readonly DefinitionNode[]): {\n\tfragments: Map<string, FragmentDefinitionNode>;\n\toperations: OperationDefinitionNode[];\n} {\n\tconst fragments = new Map<string, FragmentDefinitionNode>();\n\tconst operations: OperationDefinitionNode[] = [];\n\n\tfor (const definition of definitions) {\n\t\tif (definition.kind === Kind.FRAGMENT_DEFINITION) {\n\t\t\tfragments.set(definition.name.value, definition);\n\t\t} else if (definition.kind === Kind.OPERATION_DEFINITION) {\n\t\t\toperations.push(definition);\n\t\t}\n\t}\n\n\treturn { fragments, operations };\n}\n\n/**\n * Returns cached interfaces for a type, computing and caching on first access.\n *\n * @param caches - Traversal caches\n * @param type - The object or interface type to get interfaces for\n * @returns All directly and transitively implemented interfaces\n */\nfunction getCachedInterfaces(\n\tcaches: TraversalCaches,\n\ttype: GraphQLInterfaceType | GraphQLObjectType,\n): GraphQLInterfaceType[] {\n\tconst cached = caches.interfaces.get(type.name);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\tconst result = collectInterfaces(type);\n\tcaches.interfaces.set(type.name, result);\n\treturn result;\n}\n\n/**\n * Creates a new path node by appending a segment to the parent path.\n *\n * @param parent - Parent path node, or `undefined` for the root\n * @param segment - Field name or alias for this path segment\n * @returns New path node linked to the parent\n */\nfunction pathPush(parent: PathNode | undefined, segment: string): PathNode {\n\treturn { parent, segment };\n}\n\n/**\n * Materializes a linked-list path into a string array.\n *\n * @param node - Leaf path node to materialize from\n * @returns Array of path segments from root to leaf\n */\nfunction pathToArray(node: PathNode | undefined): string[] {\n\tconst result: string[] = [];\n\tlet current = node;\n\twhile (current) {\n\t\tresult.push(current.segment);\n\t\tcurrent = current.parent;\n\t}\n\tresult.reverse();\n\treturn result;\n}\n\n/**\n * Resolves a `@depth` directive on a field definition, falling back to\n * interface field directives when the concrete field has none.\n *\n * **Precedence:** The concrete field's directive takes priority. Interface\n * directives are only consulted when the concrete field has no `@depth`.\n * When multiple interfaces define `@depth` on the same field, the strictest\n * (minimum) value wins.\n *\n * Results are memoized per `typeName:fieldName` in the traversal caches\n * to avoid repeated interface graph walks on large schemas.\n *\n * @param caches - Traversal caches for memoizing lookups\n * @param config - Traversal configuration\n * @param currentDepth - Current depth in the query tree\n * @param currentMaxDepth - Current maximum depth for this branch\n * @param fieldDef - The field definition to inspect\n * @param hasDirectiveLimit - Whether a directive has already constrained this path\n * @param namedType - The named parent type owning this field (already unwrapped)\n * @returns Resolved maximum depth and directive limit state\n */\nfunction resolveFieldDirectiveDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tcurrentDepth: number,\n\tcurrentMaxDepth: number,\n\tfieldDef: GraphQLField<unknown, unknown>,\n\thasDirectiveLimit: boolean,\n\tnamedType: GraphQLInterfaceType | GraphQLObjectType,\n): DirectiveResolution {\n\tconst cacheKey = `${namedType.name}:${fieldDef.name}`;\n\n\tlet directiveDepth: number | undefined;\n\n\tif (caches.directiveDepths.has(cacheKey)) {\n\t\tdirectiveDepth = caches.directiveDepths.get(cacheKey);\n\t} else {\n\t\tdirectiveDepth = getDepthFromDirective(fieldDef);\n\n\t\tif (directiveDepth === undefined) {\n\t\t\tconst interfaces = getCachedInterfaces(caches, namedType);\n\t\t\tfor (const iface of interfaces) {\n\t\t\t\tconst ifaceField = iface.getFields()[fieldDef.name];\n\t\t\t\tconst ifaceDepth = getDepthFromDirective(ifaceField);\n\t\t\t\tif (ifaceDepth !== undefined) {\n\t\t\t\t\tdirectiveDepth =\n\t\t\t\t\t\tdirectiveDepth === undefined ? ifaceDepth : Math.min(directiveDepth, ifaceDepth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcaches.directiveDepths.set(cacheKey, directiveDepth);\n\t}\n\n\tif (directiveDepth !== undefined) {\n\t\tconst relativeMax = currentDepth + directiveDepth;\n\t\tconst maxDepth =\n\t\t\tconfig.directiveMode === \"cap\"\n\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t: hasDirectiveLimit\n\t\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t\t: relativeMax;\n\t\treturn { hasDirectiveLimit: true, maxDepth };\n\t}\n\n\treturn { hasDirectiveLimit, maxDepth: currentMaxDepth };\n}\n\n/**\n * Resolves the parent type from a type condition on a fragment or inline fragment.\n *\n * Falls back to `currentParentType` when the schema is unavailable or the\n * type condition resolves to a non-composite type (which GraphQL's own\n * validation would reject, but is handled defensively here).\n *\n * @param typeConditionName - The name of the type condition\n * @param schema - GraphQL schema for type lookup\n * @param currentParentType - Fallback parent type if resolution fails\n * @returns The resolved composite type or the current parent type\n */\nfunction resolveTypeCondition(\n\ttypeConditionName: string,\n\tschema: GraphQLSchema | undefined,\n\tcurrentParentType: GraphQLOutputType | undefined,\n): GraphQLOutputType | undefined {\n\tif (schema) {\n\t\tconst type = schema.getType(typeConditionName);\n\t\tif (type && isCompositeType(type)) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn currentParentType;\n}\n\nexport { calculateDepth, createTraversalCaches, extractDefinitions };\nexport type { DepthResult, TraversalCaches, TraversalConfig };\n","import { type GraphQLField, Kind } from \"graphql\";\n\n/**\n * GraphQL SDL type definition for the `@depth` directive.\n *\n * Include this in your schema definition to enable per-field depth\n * overrides when using `depthLimit` with `{ useDirective: true }`.\n *\n * @example\n * ```ts\n * import { makeExecutableSchema } from \"@graphql-tools/schema\";\n * import { depthDirectiveTypeDefs } from \"graphql-query-depth-limit-esm\";\n *\n * const schema = makeExecutableSchema({\n * typeDefs: [depthDirectiveTypeDefs, yourTypeDefs],\n * resolvers,\n * });\n * ```\n */\nexport const depthDirectiveTypeDefs = `\n directive @depth(max: Int!) on FIELD_DEFINITION\n`;\n\n/**\n * Extracts the maximum depth from a `@depth(max: Int!)` directive on a field definition.\n *\n * Only integer literals are supported. Variables (e.g., `@depth(max: $var)`) are not\n * supported because variable values are unavailable during the validation phase.\n * Fields with variable-based directives fall back to the global depth limit.\n *\n * @param field - The GraphQL field definition to inspect\n * @returns The maximum depth from the directive, or `undefined` if not found or invalid\n *\n * @example\n * ```graphql\n * type Query {\n * users: [User!]! @depth(max: 3)\n * }\n * ```\n */\nexport function getDepthFromDirective(\n\tfield: GraphQLField<unknown, unknown> | undefined,\n): number | undefined {\n\tif (!field?.astNode?.directives) {\n\t\treturn undefined;\n\t}\n\n\tconst depthDirective = field.astNode.directives.find((d) => d.name.value === \"depth\");\n\n\tif (!depthDirective?.arguments) {\n\t\treturn undefined;\n\t}\n\n\tconst maxArg = depthDirective.arguments.find((arg) => arg.name.value === \"max\");\n\n\tif (maxArg?.value.kind === Kind.INT) {\n\t\tconst directiveDepth = Number.parseInt(maxArg.value.value, 10);\n\t\tif (Number.isFinite(directiveDepth) && directiveDepth >= 0) {\n\t\t\treturn directiveDepth;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import type { IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Determines whether a field should be ignored during depth calculation.\n *\n * Introspection field handling is controlled by the `introspectionMode` parameter:\n * - `\"all\"` — ignore every `__`-prefixed field\n * - `\"typename\"` (default) — only ignore `__typename`\n * - `\"none\"` — count all introspection fields toward depth\n *\n * **Warning:** When `ignoreMode: \"skip\"` is set, an ignored field's **entire\n * subtree** is skipped — not just the depth increment. Ignoring a composite field\n * bypasses all depth protection for everything nested under it. Use\n * `ignoreMode: \"exclude\"` (default) to skip only the depth increment while still\n * traversing children.\n *\n * @param fieldName - The name of the field to check\n * @param ignore - Array of ignore rules (strings, RegExp, or functions)\n * @param caseInsensitive - Whether to use case-insensitive matching for string rules\n * @param introspectionMode - How to handle introspection fields\n * @returns `true` if the field should be skipped, `false` otherwise\n *\n * @example\n * ```typescript\n * shouldIgnoreField(\"__typename\", []); // true (introspection, default mode)\n * shouldIgnoreField(\"__schema\", [], false, \"typename\"); // false (only __typename ignored)\n * shouldIgnoreField(\"__schema\", [], false, \"all\"); // true (all __ fields ignored)\n * shouldIgnoreField(\"metadata\", [\"metadata\"]); // true (exact match)\n * shouldIgnoreField(\"Metadata\", [\"metadata\"], true); // true (case-insensitive)\n * shouldIgnoreField(\"posts\", [/^internal/]); // false (no match)\n * ```\n */\nexport function shouldIgnoreField(\n\tfieldName: string,\n\tignore?: IgnoreRule[],\n\tcaseInsensitive = false,\n\tintrospectionMode: IntrospectionMode = \"typename\",\n): boolean {\n\tif (introspectionMode === \"all\" && fieldName.startsWith(\"__\")) {\n\t\treturn true;\n\t}\n\n\tif (introspectionMode === \"typename\" && fieldName === \"__typename\") {\n\t\treturn true;\n\t}\n\n\tif (!ignore || ignore.length === 0) {\n\t\treturn false;\n\t}\n\n\t// Precompute lowercased field name once for all string rule comparisons\n\tconst normalizedFieldName = caseInsensitive ? fieldName.toLowerCase() : fieldName;\n\n\tfor (const rule of ignore) {\n\t\tif (typeof rule === \"string\") {\n\t\t\tconst normalizedRule = caseInsensitive ? rule.toLowerCase() : rule;\n\t\t\tif (normalizedRule === normalizedFieldName) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (rule instanceof RegExp) {\n\t\t\ttry {\n\t\t\t\t// Reset lastIndex for stateful regexes (/g, /y flags) to ensure\n\t\t\t\t// consistent results. This mutates the RegExp object, which is\n\t\t\t\t// intentional. Without the reset, repeated calls with the same\n\t\t\t\t// global or sticky regex produce inconsistent results.\n\t\t\t\tif (rule.global || rule.sticky) {\n\t\t\t\t\trule.lastIndex = 0;\n\t\t\t\t}\n\t\t\t\tif (rule.test(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Frozen or exotic RegExp objects throw on lastIndex mutation or\n\t\t\t\t// test(). Wrap as IgnoreRuleError so the caller can handle it\n\t\t\t\t// consistently with function rule errors.\n\t\t\t\tconst message = `Ignore rule RegExp threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tif (rule(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = `Ignore rule function threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"],"mappings":"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","names":["import_graphql","import_graphql","parentType"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/depth-limit.ts","../src/depth-engine.ts","../src/directives.ts","../src/ignore.ts"],"sourcesContent":["// Exports are sorted by module path (biome's organizeImports rule), not by\n// export name. This is enforced by the linter and is intentional.\nexport { ERROR_CODES } from \"./constants.js\";\nexport { depthLimit } from \"./depth-limit.js\";\nexport { depthDirectiveTypeDefs } from \"./directives.js\";\nexport type {\n\tDepthCallback,\n\tDepthLimitFunction,\n\tDepthLimitOptions,\n\tDirectiveMode,\n\tIgnoreMode,\n\tIgnoreRule,\n\tIntrospectionMode,\n} from \"./types.js\";\n","/**\n * Error extension codes for depth limit violations.\n */\nexport const ERROR_CODES = Object.freeze({\n\tIGNORE_RULE_ERROR: \"IGNORE_RULE_ERROR\",\n\tQUERY_TOO_DEEP: \"QUERY_TOO_DEEP\",\n} as const);\n","import {\n\ttype ASTVisitor,\n\tGraphQLError,\n\ttype OperationDefinitionNode,\n\ttype ValidationContext,\n\ttype ValidationRule,\n} from \"graphql\";\n\nimport { ERROR_CODES } from \"./constants.js\";\nimport {\n\tcalculateDepth,\n\tcreateTraversalCaches,\n\textractDefinitions,\n\ttype TraversalConfig,\n} from \"./depth-engine.js\";\nimport type { DepthCallback, DepthLimitFunction, DepthLimitOptions, IgnoreRule } from \"./types.js\";\n\n/** Valid values for the `directiveMode` option. */\nconst DIRECTIVE_MODES = new Set<string>([\"cap\", \"override\"]);\n\n/** Valid values for the `ignoreMode` option. */\nconst IGNORE_MODES = new Set<string>([\"exclude\", \"skip\"]);\n\n/** Valid values for the `ignoreIntrospection` option. */\nconst INTROSPECTION_MODES = new Set<string>([\"all\", \"none\", \"typename\"]);\n\n/**\n * Normalized depthLimit options with validated ignore rules.\n */\ntype NormalizedDepthLimitOptions = Omit<DepthLimitOptions, \"ignore\"> & {\n\tignore?: IgnoreRule[];\n};\n\n/**\n * Creates a GraphQL validation rule that limits query depth.\n *\n * Prevents DoS attacks and resource exhaustion from excessively deep queries\n * by enforcing a maximum depth on operations. Supports per-field overrides\n * via the `@depth` directive, customizable ignore rules, and an optional\n * callback for monitoring.\n *\n * Security considerations:\n * - The global `maxDepth` is a hard ceiling by default (`directiveMode: \"cap\"`)\n * - Correctly handles fragments reused at different depths\n * - Circular fragment references are detected per-path\n * - Only `__typename` is ignored by default (`ignoreIntrospection: \"typename\"`)\n * - Short-circuits on first violation when no callback is provided\n *\n * Limitations:\n * - Variables in `@depth` directives are not supported (falls back to global limit)\n * - Field names are case-sensitive by default (use `caseInsensitiveIgnore` if needed)\n * - `useDirective: true` requires a schema in the validation context; without one\n * it silently falls back to the global limit (directives cannot be resolved)\n * - RegExp ignore rules are executed against field names; patterns with catastrophic\n * backtracking (e.g., `/^(a+)+$/`) may cause performance issues\n *\n * @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)\n * @param options - Optional configuration for ignore rules, directives, and case sensitivity\n * @param callback - Optional callback invoked with per-operation depths as a plain object payload\n * @returns A GraphQL validation rule function\n * @throws {Error} If `maxDepth` is not a non-negative integer\n *\n * @example\n * ```typescript\n * import { depthLimit } from \"graphql-query-depth-limit-esm\";\n * import { validate } from \"graphql\";\n *\n * const errors = validate(schema, document, [\n * depthLimit(5, {\n * ignore: [\"friends\", /.*Connection$/],\n * useDirective: true,\n * }),\n * ]);\n *\n * const withCallback = depthLimit(5, (depths) => {\n * console.log(depths);\n * });\n * ```\n */\nexport function depthLimit(maxDepth: number, callback?: DepthCallback): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions | DepthCallback,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 0) {\n\t\tthrow new Error(`Invalid maxDepth: ${maxDepth}. Must be a non-negative integer.`);\n\t}\n\n\tconst normalized = normalizeDepthLimitArgs(options, callback);\n\n\treturn createValidationRule(maxDepth, normalized.options, normalized.callback);\n}\n\n/** Compile-time check that the implementation satisfies the public type. */\ndepthLimit satisfies DepthLimitFunction;\n\n/**\n * Validates that a value is a boolean or undefined.\n */\nfunction assertBooleanOption(name: string, value: unknown): void {\n\tif (value !== undefined && typeof value !== \"boolean\") {\n\t\tthrow new TypeError(`Invalid ${name}: expected boolean, received ${typeof value}.`);\n\t}\n}\n\n/**\n * Creates the validation rule closure with validated parameters.\n */\nfunction createValidationRule(\n\tmaxDepth: number,\n\toptions?: NormalizedDepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tconst shortCircuit = options?.shortCircuit ?? callback == null;\n\n\treturn function depthLimitValidationRule(context: ValidationContext): ASTVisitor {\n\t\tconst caches = createTraversalCaches();\n\t\tconst document = context.getDocument();\n\t\tconst depths: Record<string, number> | undefined = callback\n\t\t\t? createDepthResultRecord()\n\t\t\t: undefined;\n\t\tconst { fragments, operations } = extractDefinitions(document.definitions);\n\t\tconst schema = context.getSchema() ?? undefined;\n\t\t// By design: when useDirective is true but no schema is available,\n\t\t// directives silently fall back to the global maxDepth. This is not a\n\t\t// \"silent failure\" — directives cannot be resolved without type info,\n\t\t// so the global limit is the correct and safe default. Emitting an\n\t\t// error here would penalize valid schema-less contexts (e.g., custom\n\t\t// ValidationContext wrappers) where the user intentionally omits the\n\t\t// schema. See DepthLimitOptions.useDirective JSDoc for documentation.\n\t\tconst useDirective = Boolean(schema) && (options?.useDirective ?? false);\n\n\t\tconst nextOperationName = createOperationNameAllocator(operations);\n\n\t\tconst config: TraversalConfig = {\n\t\t\tcaseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,\n\t\t\tdirectiveMode: options?.directiveMode ?? \"cap\",\n\t\t\tignore: options?.ignore,\n\t\t\tignoreMode: options?.ignoreMode ?? \"exclude\",\n\t\t\tintrospectionMode: options?.ignoreIntrospection ?? \"typename\",\n\t\t\tlimitIgnoredRecursion: options?.limitIgnoredRecursion ?? false,\n\t\t\tshortCircuit,\n\t\t\tuseDirective,\n\t\t};\n\n\t\tconst rootTypeMap = schema\n\t\t\t? {\n\t\t\t\t\tmutation: schema.getMutationType() ?? undefined,\n\t\t\t\t\tquery: schema.getQueryType() ?? undefined,\n\t\t\t\t\tsubscription: schema.getSubscriptionType() ?? undefined,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tmutation: undefined,\n\t\t\t\t\tquery: undefined,\n\t\t\t\t\tsubscription: undefined,\n\t\t\t\t};\n\n\t\tfor (const operation of operations) {\n\t\t\tconst operationName = nextOperationName(operation);\n\t\t\tconst rootType = rootTypeMap[operation.operation];\n\n\t\t\tlet result: ReturnType<typeof calculateDepth>;\n\t\t\ttry {\n\t\t\t\tresult = calculateDepth(caches, config, fragments, maxDepth, operation, rootType, schema);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && error.name === \"IgnoreRuleError\") {\n\t\t\t\t\tcontext.reportError(\n\t\t\t\t\t\tnew GraphQLError(error.message, {\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.IGNORE_RULE_ERROR,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: [operation],\n\t\t\t\t\t\t\toriginalError: error,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tif (depths) {\n\t\t\t\tsetDepthResult(depths, operationName, result.depth);\n\t\t\t}\n\n\t\t\tif (result.violation) {\n\t\t\t\tconst depthValue = result.violation.depth;\n\t\t\t\tconst depthLabel = shortCircuit ? `at least ${depthValue}` : `${depthValue}`;\n\t\t\t\tconst violationPath = result.violation.path;\n\t\t\t\tconst pathSuffix = violationPath.length > 0 ? ` (at ${violationPath.join(\".\")})` : \"\";\n\n\t\t\t\tcontext.reportError(\n\t\t\t\t\tnew GraphQLError(\n\t\t\t\t\t\t`'${operationName}' has depth ${depthLabel} which exceeds maximum allowed depth of ${result.violation.maxDepth}${pathSuffix}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.QUERY_TOO_DEEP,\n\t\t\t\t\t\t\t\tdepth: depthValue,\n\t\t\t\t\t\t\t\tmaxDepth: result.violation.maxDepth,\n\t\t\t\t\t\t\t\tpath: violationPath,\n\t\t\t\t\t\t\t\tshortCircuit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: result.violation.node ? [operation, result.violation.node] : [operation],\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (callback && depths) {\n\t\t\t// Keep callback payload as a plain object for compatibility.\n\t\t\tcallback({ ...depths });\n\t\t}\n\n\t\t// All depth validation is performed eagerly above because fragment\n\t\t// resolution requires the full document upfront. Nothing remains\n\t\t// for the visitor traversal phase.\n\t\treturn {};\n\t};\n}\n\n/**\n * Determines whether a value is a valid ignore rule.\n */\nfunction isIgnoreRule(rule: unknown): rule is IgnoreRule {\n\treturn typeof rule === \"function\" || rule instanceof RegExp || typeof rule === \"string\";\n}\n\n/**\n * Normalizes the optional depthLimit arguments.\n */\nfunction normalizeDepthLimitArgs(\n\toptions: DepthLimitOptions | DepthCallback | undefined,\n\tcallback: DepthCallback | undefined,\n): { callback?: DepthCallback; options?: NormalizedDepthLimitOptions } {\n\tif (callback !== undefined && typeof callback !== \"function\") {\n\t\tthrow new TypeError(\"Invalid callback: expected a function.\");\n\t}\n\n\tif (typeof options === \"function\") {\n\t\tif (callback) {\n\t\t\tthrow new TypeError(\"Invalid depthLimit arguments: callback provided twice.\");\n\t\t}\n\n\t\treturn { callback: options, options: undefined };\n\t}\n\n\tif (\n\t\toptions !== undefined &&\n\t\t(options === null || typeof options !== \"object\" || Array.isArray(options))\n\t) {\n\t\tconst receivedType = Array.isArray(options)\n\t\t\t? \"array\"\n\t\t\t: options === null\n\t\t\t\t? \"null\"\n\t\t\t\t: typeof options;\n\t\tthrow new TypeError(`Invalid options: expected an object, received ${receivedType}.`);\n\t}\n\n\treturn {\n\t\tcallback,\n\t\toptions: options ? normalizeDepthLimitOptions(options) : undefined,\n\t};\n}\n\n/**\n * Normalizes and validates depthLimit options.\n */\nfunction normalizeDepthLimitOptions(options: DepthLimitOptions): NormalizedDepthLimitOptions {\n\tassertBooleanOption(\"caseInsensitiveIgnore\", options.caseInsensitiveIgnore);\n\n\tif (options.directiveMode !== undefined && !DIRECTIVE_MODES.has(options.directiveMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid directiveMode: \"${options.directiveMode}\". Must be \"cap\" or \"override\".`,\n\t\t);\n\t}\n\n\tif (\n\t\toptions.ignoreIntrospection !== undefined &&\n\t\t!INTROSPECTION_MODES.has(options.ignoreIntrospection)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreIntrospection: \"${options.ignoreIntrospection}\". Must be \"all\", \"none\", or \"typename\".`,\n\t\t);\n\t}\n\n\tif (options.ignoreMode !== undefined && !IGNORE_MODES.has(options.ignoreMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreMode: \"${options.ignoreMode}\". Must be \"exclude\" or \"skip\".`,\n\t\t);\n\t}\n\n\tassertBooleanOption(\"limitIgnoredRecursion\", options.limitIgnoredRecursion);\n\tassertBooleanOption(\"shortCircuit\", options.shortCircuit);\n\tassertBooleanOption(\"useDirective\", options.useDirective);\n\n\tconst ignore = normalizeIgnoreRules(options.ignore);\n\treturn { ...options, ignore };\n}\n\n/**\n * Normalizes and validates ignore rules.\n */\nfunction normalizeIgnoreRules(ignore: DepthLimitOptions[\"ignore\"]): IgnoreRule[] | undefined {\n\tif (ignore == null) {\n\t\treturn undefined;\n\t}\n\n\tconst rules: unknown[] = Array.isArray(ignore) ? ignore : [ignore];\n\n\tfor (const [index, rule] of rules.entries()) {\n\t\tif (!isIgnoreRule(rule)) {\n\t\t\tconst receivedType = Array.isArray(rule) ? \"array\" : rule === null ? \"null\" : typeof rule;\n\t\t\tthrow new TypeError(\n\t\t\t\t`Invalid ignore rule at index ${index}: expected string, RegExp, or function, received ${receivedType}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn rules as IgnoreRule[];\n}\n\n/**\n * Creates a null-prototype record for internal depth result accumulation.\n */\nfunction createDepthResultRecord(): Record<string, number> {\n\treturn Object.create(null) as Record<string, number>;\n}\n\n/**\n * Creates a stable allocator for callback operation names.\n *\n * Ensures:\n * - Anonymous names never collide with explicit operation names\n * - Duplicate named operations receive deterministic suffixes\n * - Generated names do not overwrite previous callback entries\n */\nfunction createOperationNameAllocator(\n\toperations: readonly OperationDefinitionNode[],\n): (operation: OperationDefinitionNode) => string {\n\tconst explicitNames = new Set<string>();\n\tfor (const operation of operations) {\n\t\tif (operation.name?.value) {\n\t\t\texplicitNames.add(operation.name.value);\n\t\t}\n\t}\n\n\tconst usedNames = new Set<string>();\n\tconst namedCounts = new Map<string, number>();\n\tlet anonymousCount = 0;\n\n\treturn (operation: OperationDefinitionNode): string => {\n\t\tconst explicitName = operation.name?.value;\n\t\tif (explicitName) {\n\t\t\tlet suffix = namedCounts.get(explicitName) ?? 0;\n\t\t\tlet candidate = suffix === 0 ? explicitName : `${explicitName}_${suffix}`;\n\n\t\t\twhile (usedNames.has(candidate) || (suffix > 0 && explicitNames.has(candidate))) {\n\t\t\t\tsuffix++;\n\t\t\t\tcandidate = `${explicitName}_${suffix}`;\n\t\t\t}\n\n\t\t\tnamedCounts.set(explicitName, suffix + 1);\n\t\t\tusedNames.add(candidate);\n\t\t\treturn candidate;\n\t\t}\n\n\t\tlet suffix = anonymousCount;\n\t\tlet candidate = suffix === 0 ? \"anonymous\" : `anonymous_${suffix}`;\n\t\twhile (usedNames.has(candidate) || explicitNames.has(candidate)) {\n\t\t\tsuffix++;\n\t\t\tcandidate = `anonymous_${suffix}`;\n\t\t}\n\n\t\tanonymousCount = suffix + 1;\n\t\tusedNames.add(candidate);\n\t\treturn candidate;\n\t};\n}\n\n/**\n * Safely assigns a depth entry without allowing prototype pollution.\n */\nfunction setDepthResult(target: Record<string, number>, key: string, value: number): void {\n\tObject.defineProperty(target, key, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tvalue,\n\t\twritable: true,\n\t});\n}\n","import {\n\ttype ASTNode,\n\ttype DefinitionNode,\n\ttype FragmentDefinitionNode,\n\ttype GraphQLField,\n\ttype GraphQLInterfaceType,\n\ttype GraphQLObjectType,\n\ttype GraphQLOutputType,\n\ttype GraphQLSchema,\n\tgetNamedType,\n\tisCompositeType,\n\tisInterfaceType,\n\tisObjectType,\n\tKind,\n\ttype OperationDefinitionNode,\n\ttype SelectionNode,\n} from \"graphql\";\n\nimport { getDepthFromDirective } from \"./directives.js\";\nimport { shouldIgnoreField } from \"./ignore.js\";\nimport type { DirectiveMode, IgnoreMode, IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Result returned by the depth calculation engine.\n */\ninterface DepthResult {\n\t/** Maximum depth found across all branches */\n\tdepth: number;\n\t/** Deepest violation found, or `null` if within limits */\n\tviolation: DepthViolation | null;\n}\n\n/**\n * Records a depth limit violation with the offending depth and its limit.\n */\ninterface DepthViolation {\n\t/** The actual depth that exceeded the limit */\n\tdepth: number;\n\t/** The maximum allowed depth that was exceeded */\n\tmaxDepth: number;\n\t/** The AST node that caused the violation, for precise error locations */\n\tnode?: ASTNode;\n\t/** Field path from the operation root to the violation point */\n\tpath: string[];\n}\n\n/**\n * Result of resolving a `@depth` directive on a field definition.\n * @internal\n */\ninterface DirectiveResolution {\n\t/** Whether a directive limit is now active on this path */\n\thasDirectiveLimit: boolean;\n\t/** The resolved maximum depth for this branch */\n\tmaxDepth: number;\n}\n\n/**\n * Lightweight linked-list node for building field paths without per-field\n * array allocations. Only materialized into `string[]` when reporting a\n * violation or populating callback results.\n * @internal\n */\ninterface PathNode {\n\t/** Parent node in the path, or `undefined` for the root */\n\tparent: PathNode | undefined;\n\t/** Field name or alias for this path segment */\n\tsegment: string;\n}\n\n/**\n * Single unit of work on the iterative DFS stack.\n * @internal\n */\ninterface StackFrame {\n\t/** Current depth at this point in traversal */\n\tcurrentDepth: number;\n\t/** Whether a `@depth` directive has already constrained this path */\n\thasDirectiveLimit: boolean;\n\t/** Ignored field names seen on the current path (for recursion guard) */\n\tignoredFieldsOnPath: Set<string>;\n\t/** Maximum allowed depth for this branch */\n\tmaxDepth: number;\n\t/** The AST node whose selectionSet should be processed */\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } };\n\t/** Parent type for field resolution */\n\tparentType: GraphQLOutputType | undefined;\n\t/** Linked-list path from the operation root to this node */\n\tpath: PathNode | undefined;\n\t/** Fragment names visited on the current path (for cycle detection) */\n\tvisitedFragments: Set<string>;\n}\n\n/**\n * Caches shared across all stack frames during a single validation run\n * to avoid repeated interface graph walks and directive AST lookups.\n * @internal\n */\ninterface TraversalCaches {\n\t/** Cached raw directive depth per `typeName:fieldName` */\n\tdirectiveDepths: Map<string, number | undefined>;\n\t/** Cached interface lists per type name */\n\tinterfaces: Map<string, GraphQLInterfaceType[]>;\n}\n\n/**\n * Immutable configuration shared across all stack frames during traversal.\n * @internal\n */\ninterface TraversalConfig {\n\t/** Whether to use case-insensitive matching for string ignore rules */\n\tcaseInsensitiveIgnore: boolean;\n\t/** Controls how `@depth` directives interact with the global `maxDepth` */\n\tdirectiveMode: DirectiveMode;\n\t/** Rules for fields to ignore in depth calculation */\n\tignore: IgnoreRule[] | undefined;\n\t/** Controls how ignored fields affect depth traversal */\n\tignoreMode: IgnoreMode;\n\t/** Controls which introspection fields are ignored */\n\tintrospectionMode: IntrospectionMode;\n\t/** Whether repeated ignored fields on a path should increment depth */\n\tlimitIgnoredRecursion: boolean;\n\t/** Whether to bail immediately on violation (when no callback needs true depth) */\n\tshortCircuit: boolean;\n\t/** Whether to check for `@depth` directives on fields */\n\tuseDirective: boolean;\n}\n\n/**\n * Calculates the depth of a GraphQL query AST node using iterative DFS.\n *\n * Handles three selection types:\n * - **Fields**: Increment depth by 1 for composite (non-scalar) fields\n * - **Fragment spreads**: Expand the fragment in-place (no depth increment)\n * - **Inline fragments**: Process in-place (no depth increment)\n *\n * Fragment cycle detection uses per-path visited sets so that the same\n * fragment reused at different depths is calculated correctly.\n *\n * When `shortCircuit` is enabled (no callback), the engine bails immediately\n * on the first violation instead of traversing the full subtree.\n *\n * Uses an explicit stack instead of recursion to prevent stack overflow\n * on deeply nested queries.\n *\n * @param caches - Shared caches for interface and directive lookups\n * @param config - Immutable traversal configuration\n * @param fragments - Map of all fragment definitions in the document\n * @param maxDepth - Maximum allowed depth for this branch\n * @param node - The AST node to calculate depth for\n * @param parentType - Root type for field resolution\n * @param schema - GraphQL schema for type resolution\n * @returns The maximum depth and the deepest violation found\n */\nfunction calculateDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tfragments: Map<string, FragmentDefinitionNode>,\n\tmaxDepth: number,\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } },\n\tparentType: GraphQLOutputType | undefined,\n\tschema: GraphQLSchema | undefined,\n): DepthResult {\n\tlet deepestViolation: DepthViolation | null = null;\n\tlet globalMaxDepth = 0;\n\n\tif (!node.selectionSet) {\n\t\treturn { depth: 0, violation: null };\n\t}\n\n\tconst stack: StackFrame[] = [\n\t\t{\n\t\t\tcurrentDepth: 0,\n\t\t\thasDirectiveLimit: false,\n\t\t\tignoredFieldsOnPath: new Set<string>(),\n\t\t\tmaxDepth,\n\t\t\tnode,\n\t\t\tparentType,\n\t\t\tpath: undefined,\n\t\t\tvisitedFragments: new Set<string>(),\n\t\t},\n\t];\n\n\tfor (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n\t\tif (!frame.node.selectionSet) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nextFrames: StackFrame[] = [];\n\n\t\tfor (const selection of frame.node.selectionSet.selections) {\n\t\t\tswitch (selection.kind) {\n\t\t\t\tcase Kind.FIELD: {\n\t\t\t\t\tconst fieldName = selection.name.value;\n\t\t\t\t\tconst isIntrospectionField = fieldName.startsWith(\"__\");\n\n\t\t\t\t\t// When introspection is fully ignored, always skip the subtree\n\t\t\t\t\t// regardless of ignoreMode.\n\t\t\t\t\tif (config.introspectionMode === \"all\" && isIntrospectionField) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Leaf fields (no selectionSet) never contribute to depth,\n\t\t\t\t\t// so skip them before running ignore rules to avoid unnecessary\n\t\t\t\t\t// predicate evaluation and potential errors on irrelevant fields.\n\t\t\t\t\tif (!selection.selectionSet) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isIgnored = shouldIgnoreField(\n\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\tconfig.ignore,\n\t\t\t\t\t\tconfig.caseInsensitiveIgnore,\n\t\t\t\t\t\tconfig.introspectionMode,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (isIgnored && config.ignoreMode === \"skip\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Resolve field type and directive depth\n\t\t\t\t\tlet fieldMaxDepth = frame.maxDepth;\n\t\t\t\t\tlet fieldType: GraphQLOutputType | undefined;\n\t\t\t\t\tlet hasDirectiveLimit = frame.hasDirectiveLimit;\n\n\t\t\t\t\tif (schema && frame.parentType) {\n\t\t\t\t\t\tconst namedType = getNamedType(frame.parentType);\n\t\t\t\t\t\tif (isObjectType(namedType) || isInterfaceType(namedType)) {\n\t\t\t\t\t\t\tconst fieldDef = namedType.getFields()[fieldName];\n\t\t\t\t\t\t\tif (fieldDef) {\n\t\t\t\t\t\t\t\tfieldType = fieldDef.type;\n\n\t\t\t\t\t\t\t\t// Defensively skip non-composite fields that erroneously\n\t\t\t\t\t\t\t\t// have selections (normally caught by GraphQL's own\n\t\t\t\t\t\t\t\t// validation rules, but guards against miscounted depth\n\t\t\t\t\t\t\t\t// when this rule runs standalone or before other rules).\n\t\t\t\t\t\t\t\tconst resolvedType = getNamedType(fieldType);\n\t\t\t\t\t\t\t\tif (resolvedType && !isCompositeType(resolvedType)) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (config.useDirective) {\n\t\t\t\t\t\t\t\t\tconst resolved = resolveFieldDirectiveDepth(\n\t\t\t\t\t\t\t\t\t\tcaches,\n\t\t\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\t\t\tframe.currentDepth,\n\t\t\t\t\t\t\t\t\t\tframe.maxDepth,\n\t\t\t\t\t\t\t\t\t\tfieldDef,\n\t\t\t\t\t\t\t\t\t\tframe.hasDirectiveLimit,\n\t\t\t\t\t\t\t\t\t\tnamedType,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tfieldMaxDepth = resolved.maxDepth;\n\t\t\t\t\t\t\t\t\thasDirectiveLimit = resolved.hasDirectiveLimit;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Determine whether this ignored field should still increment\n\t\t\t\t\t// depth due to the recursion guard detecting repeated ignores\n\t\t\t\t\t// on the same path. The key is type-aware (`Type:field`) when\n\t\t\t\t\t// a schema is present so identically named fields on unrelated\n\t\t\t\t\t// types are tracked independently. Without a schema, the key\n\t\t\t\t\t// uses the field name alone, which may cause conservative\n\t\t\t\t\t// over-counting on paths with same-named fields on different types.\n\t\t\t\t\tlet ignoredFieldsOnPath = frame.ignoredFieldsOnPath;\n\t\t\t\t\tlet effectivelyIgnored = isIgnored;\n\n\t\t\t\t\tif (isIgnored && config.limitIgnoredRecursion) {\n\t\t\t\t\t\tconst parentName = frame.parentType ? getNamedType(frame.parentType)?.name : undefined;\n\t\t\t\t\t\tconst recursionKey = parentName ? `${parentName}:${fieldName}` : fieldName;\n\n\t\t\t\t\t\tif (ignoredFieldsOnPath.has(recursionKey)) {\n\t\t\t\t\t\t\t// Same type:field was already ignored on this path — increment depth\n\t\t\t\t\t\t\teffectivelyIgnored = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First occurrence — track it for subsequent path segments\n\t\t\t\t\t\t\tignoredFieldsOnPath = new Set(ignoredFieldsOnPath);\n\t\t\t\t\t\t\tignoredFieldsOnPath.add(recursionKey);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newDepth = effectivelyIgnored ? frame.currentDepth : frame.currentDepth + 1;\n\n\t\t\t\t\t// Use alias for path (matches the response shape clients see),\n\t\t\t\t\t// while fieldName is used for schema lookups and ignore rules.\n\t\t\t\t\tconst pathSegment = selection.alias?.value ?? fieldName;\n\t\t\t\t\tconst fieldPath = pathPush(frame.path, pathSegment);\n\n\t\t\t\t\t// Track maximum depth found\n\t\t\t\t\tif (newDepth > globalMaxDepth) {\n\t\t\t\t\t\tglobalMaxDepth = newDepth;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for violation\n\t\t\t\t\tif (newDepth > fieldMaxDepth) {\n\t\t\t\t\t\tconst violation: DepthViolation = {\n\t\t\t\t\t\t\tdepth: newDepth,\n\t\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\t\tpath: pathToArray(fieldPath),\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (config.shortCircuit) {\n\t\t\t\t\t\t\treturn { depth: newDepth, violation };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!deepestViolation || violation.depth > deepestViolation.depth) {\n\t\t\t\t\t\t\tdeepestViolation = violation;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Queue children and push in reverse after this selection set\n\t\t\t\t\t// so DFS traversal follows query order deterministically.\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: newDepth,\n\t\t\t\t\t\thasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType: fieldType,\n\t\t\t\t\t\tpath: fieldPath,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.FRAGMENT_SPREAD: {\n\t\t\t\t\tconst fragmentName = selection.name.value;\n\n\t\t\t\t\t// Check membership before copying to avoid wasted allocations\n\t\t\t\t\t// when the fragment was already visited on this path.\n\t\t\t\t\tif (frame.visitedFragments.has(fragmentName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fragment = fragments.get(fragmentName);\n\t\t\t\t\tif (!fragment) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create independent copy for per-path cycle detection\n\t\t\t\t\tconst fragmentVisited = new Set(frame.visitedFragments);\n\t\t\t\t\tfragmentVisited.add(fragmentName);\n\n\t\t\t\t\tconst parentType = fragment.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: fragment,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: fragmentVisited,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.INLINE_FRAGMENT: {\n\t\t\t\t\tconst parentType = selection.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst exhaustiveCheck: never = selection;\n\t\t\t\t\tthrowUnhandledSelectionKind(exhaustiveCheck);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (let index = nextFrames.length - 1; index >= 0; index--) {\n\t\t\tconst nextFrame = nextFrames[index];\n\t\t\tif (nextFrame) {\n\t\t\t\tstack.push(nextFrame);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { depth: globalMaxDepth, violation: deepestViolation };\n}\n\n/**\n * Throws for impossible selection kinds. This should only run when a\n * malformed or manually constructed AST bypasses GraphQL parser guarantees.\n */\nfunction throwUnhandledSelectionKind(selection: SelectionNode): never {\n\tthrow new Error(`Unhandled selection kind: ${selection.kind}`);\n}\n\n/**\n * Collects all interfaces implemented by a type, including transitively\n * inherited interfaces (interface-implements-interface chains).\n *\n * @param type - The object or interface type to collect interfaces from\n * @returns All directly and transitively implemented interfaces\n */\nfunction collectInterfaces(type: GraphQLInterfaceType | GraphQLObjectType): GraphQLInterfaceType[] {\n\tconst interfaces: GraphQLInterfaceType[] = [];\n\tconst seen = new Set<string>();\n\tconst stack = [...type.getInterfaces()];\n\n\tfor (let iface = stack.pop(); iface !== undefined; iface = stack.pop()) {\n\t\tif (seen.has(iface.name)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tseen.add(iface.name);\n\t\tinterfaces.push(iface);\n\n\t\tfor (const parent of iface.getInterfaces()) {\n\t\t\tif (!seen.has(parent.name)) {\n\t\t\t\tstack.push(parent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn interfaces;\n}\n\n/**\n * Creates empty traversal caches for a new validation run.\n *\n * @returns Fresh caches for interface and directive lookups\n */\nfunction createTraversalCaches(): TraversalCaches {\n\treturn {\n\t\tdirectiveDepths: new Map<string, number | undefined>(),\n\t\tinterfaces: new Map<string, GraphQLInterfaceType[]>(),\n\t};\n}\n\n/**\n * Extracts all fragment and operation definitions from a GraphQL document\n * in a single pass.\n *\n * **By design:** Duplicate fragment names are silently overwritten (last wins)\n * rather than raising a validation error. This is intentional — detecting\n * duplicates is the responsibility of GraphQL's built-in `UniqueFragmentNamesRule`,\n * not a depth-limiting rule. When both rules run together (the normal case),\n * duplicates are already caught before this code executes. When used standalone,\n * last-wins is a safe, deterministic fallback that avoids coupling depth\n * validation to fragment uniqueness concerns.\n *\n * @param definitions - Array of definition nodes from the parsed document\n * @returns Fragment map and operation array\n */\nfunction extractDefinitions(definitions: readonly DefinitionNode[]): {\n\tfragments: Map<string, FragmentDefinitionNode>;\n\toperations: OperationDefinitionNode[];\n} {\n\tconst fragments = new Map<string, FragmentDefinitionNode>();\n\tconst operations: OperationDefinitionNode[] = [];\n\n\tfor (const definition of definitions) {\n\t\tif (definition.kind === Kind.FRAGMENT_DEFINITION) {\n\t\t\tfragments.set(definition.name.value, definition);\n\t\t} else if (definition.kind === Kind.OPERATION_DEFINITION) {\n\t\t\toperations.push(definition);\n\t\t}\n\t}\n\n\treturn { fragments, operations };\n}\n\n/**\n * Returns cached interfaces for a type, computing and caching on first access.\n *\n * @param caches - Traversal caches\n * @param type - The object or interface type to get interfaces for\n * @returns All directly and transitively implemented interfaces\n */\nfunction getCachedInterfaces(\n\tcaches: TraversalCaches,\n\ttype: GraphQLInterfaceType | GraphQLObjectType,\n): GraphQLInterfaceType[] {\n\tconst cached = caches.interfaces.get(type.name);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\tconst result = collectInterfaces(type);\n\tcaches.interfaces.set(type.name, result);\n\treturn result;\n}\n\n/**\n * Creates a new path node by appending a segment to the parent path.\n *\n * @param parent - Parent path node, or `undefined` for the root\n * @param segment - Field name or alias for this path segment\n * @returns New path node linked to the parent\n */\nfunction pathPush(parent: PathNode | undefined, segment: string): PathNode {\n\treturn { parent, segment };\n}\n\n/**\n * Materializes a linked-list path into a string array.\n *\n * @param node - Leaf path node to materialize from\n * @returns Array of path segments from root to leaf\n */\nfunction pathToArray(node: PathNode | undefined): string[] {\n\tconst result: string[] = [];\n\tlet current = node;\n\twhile (current) {\n\t\tresult.push(current.segment);\n\t\tcurrent = current.parent;\n\t}\n\tresult.reverse();\n\treturn result;\n}\n\n/**\n * Resolves a `@depth` directive on a field definition, falling back to\n * interface field directives when the concrete field has none.\n *\n * **Precedence:** The concrete field's directive takes priority. Interface\n * directives are only consulted when the concrete field has no `@depth`.\n * When multiple interfaces define `@depth` on the same field, the strictest\n * (minimum) value wins.\n *\n * Results are memoized per `typeName:fieldName` in the traversal caches\n * to avoid repeated interface graph walks on large schemas.\n *\n * @param caches - Traversal caches for memoizing lookups\n * @param config - Traversal configuration\n * @param currentDepth - Current depth in the query tree\n * @param currentMaxDepth - Current maximum depth for this branch\n * @param fieldDef - The field definition to inspect\n * @param hasDirectiveLimit - Whether a directive has already constrained this path\n * @param namedType - The named parent type owning this field (already unwrapped)\n * @returns Resolved maximum depth and directive limit state\n */\nfunction resolveFieldDirectiveDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tcurrentDepth: number,\n\tcurrentMaxDepth: number,\n\tfieldDef: GraphQLField<unknown, unknown>,\n\thasDirectiveLimit: boolean,\n\tnamedType: GraphQLInterfaceType | GraphQLObjectType,\n): DirectiveResolution {\n\tconst cacheKey = `${namedType.name}:${fieldDef.name}`;\n\n\tlet directiveDepth: number | undefined;\n\n\tif (caches.directiveDepths.has(cacheKey)) {\n\t\tdirectiveDepth = caches.directiveDepths.get(cacheKey);\n\t} else {\n\t\tdirectiveDepth = getDepthFromDirective(fieldDef);\n\n\t\tif (directiveDepth === undefined) {\n\t\t\tconst interfaces = getCachedInterfaces(caches, namedType);\n\t\t\tfor (const iface of interfaces) {\n\t\t\t\tconst ifaceField = iface.getFields()[fieldDef.name];\n\t\t\t\tconst ifaceDepth = getDepthFromDirective(ifaceField);\n\t\t\t\tif (ifaceDepth !== undefined) {\n\t\t\t\t\tdirectiveDepth =\n\t\t\t\t\t\tdirectiveDepth === undefined ? ifaceDepth : Math.min(directiveDepth, ifaceDepth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcaches.directiveDepths.set(cacheKey, directiveDepth);\n\t}\n\n\tif (directiveDepth !== undefined) {\n\t\tconst relativeMax = currentDepth + directiveDepth;\n\t\tconst maxDepth =\n\t\t\tconfig.directiveMode === \"cap\"\n\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t: hasDirectiveLimit\n\t\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t\t: relativeMax;\n\t\treturn { hasDirectiveLimit: true, maxDepth };\n\t}\n\n\treturn { hasDirectiveLimit, maxDepth: currentMaxDepth };\n}\n\n/**\n * Resolves the parent type from a type condition on a fragment or inline fragment.\n *\n * Falls back to `currentParentType` when the schema is unavailable or the\n * type condition resolves to a non-composite type (which GraphQL's own\n * validation would reject, but is handled defensively here).\n *\n * @param typeConditionName - The name of the type condition\n * @param schema - GraphQL schema for type lookup\n * @param currentParentType - Fallback parent type if resolution fails\n * @returns The resolved composite type or the current parent type\n */\nfunction resolveTypeCondition(\n\ttypeConditionName: string,\n\tschema: GraphQLSchema | undefined,\n\tcurrentParentType: GraphQLOutputType | undefined,\n): GraphQLOutputType | undefined {\n\tif (schema) {\n\t\tconst type = schema.getType(typeConditionName);\n\t\tif (type && isCompositeType(type)) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn currentParentType;\n}\n\nexport { calculateDepth, createTraversalCaches, extractDefinitions };\nexport type { DepthResult, TraversalCaches, TraversalConfig };\n","import { type GraphQLField, Kind } from \"graphql\";\n\n/**\n * GraphQL SDL type definition for the `@depth` directive.\n *\n * Include this in your schema definition to enable per-field depth\n * overrides when using `depthLimit` with `{ useDirective: true }`.\n *\n * @example\n * ```ts\n * import { makeExecutableSchema } from \"@graphql-tools/schema\";\n * import { depthDirectiveTypeDefs } from \"graphql-query-depth-limit-esm\";\n *\n * const schema = makeExecutableSchema({\n * typeDefs: [depthDirectiveTypeDefs, yourTypeDefs],\n * resolvers,\n * });\n * ```\n */\nexport const depthDirectiveTypeDefs = `\n directive @depth(max: Int!) on FIELD_DEFINITION\n`;\n\n/**\n * Extracts the maximum depth from a `@depth(max: Int!)` directive on a field definition.\n *\n * Only integer literals are supported. Variables (e.g., `@depth(max: $var)`) are not\n * supported because variable values are unavailable during the validation phase.\n * Fields with variable-based directives fall back to the global depth limit.\n *\n * @param field - The GraphQL field definition to inspect\n * @returns The maximum depth from the directive, or `undefined` if not found or invalid\n *\n * @example\n * ```graphql\n * type Query {\n * users: [User!]! @depth(max: 3)\n * }\n * ```\n */\nexport function getDepthFromDirective(\n\tfield: GraphQLField<unknown, unknown> | undefined,\n): number | undefined {\n\tif (!field?.astNode?.directives) {\n\t\treturn undefined;\n\t}\n\n\tconst depthDirective = field.astNode.directives.find((d) => d.name.value === \"depth\");\n\n\tif (!depthDirective?.arguments) {\n\t\treturn undefined;\n\t}\n\n\tconst maxArg = depthDirective.arguments.find((arg) => arg.name.value === \"max\");\n\n\tif (maxArg?.value.kind === Kind.INT) {\n\t\tconst directiveDepth = Number.parseInt(maxArg.value.value, 10);\n\t\tif (Number.isFinite(directiveDepth) && directiveDepth >= 0) {\n\t\t\treturn directiveDepth;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import type { IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Determines whether a field should be ignored during depth calculation.\n *\n * Introspection field handling is controlled by the `introspectionMode` parameter:\n * - `\"all\"` — ignore every `__`-prefixed field\n * - `\"typename\"` (default) — only ignore `__typename`\n * - `\"none\"` — count all introspection fields toward depth\n *\n * **Warning:** When `ignoreMode: \"skip\"` is set, an ignored field's **entire\n * subtree** is skipped — not just the depth increment. Ignoring a composite field\n * bypasses all depth protection for everything nested under it. Use\n * `ignoreMode: \"exclude\"` (default) to skip only the depth increment while still\n * traversing children.\n *\n * @param fieldName - The name of the field to check\n * @param ignore - Array of ignore rules (strings, RegExp, or functions)\n * @param caseInsensitive - Whether to use case-insensitive matching for string rules\n * @param introspectionMode - How to handle introspection fields\n * @returns `true` if the field should be skipped, `false` otherwise\n *\n * @example\n * ```typescript\n * shouldIgnoreField(\"__typename\", []); // true (introspection, default mode)\n * shouldIgnoreField(\"__schema\", [], false, \"typename\"); // false (only __typename ignored)\n * shouldIgnoreField(\"__schema\", [], false, \"all\"); // true (all __ fields ignored)\n * shouldIgnoreField(\"metadata\", [\"metadata\"]); // true (exact match)\n * shouldIgnoreField(\"Metadata\", [\"metadata\"], true); // true (case-insensitive)\n * shouldIgnoreField(\"posts\", [/^internal/]); // false (no match)\n * ```\n */\nexport function shouldIgnoreField(\n\tfieldName: string,\n\tignore?: IgnoreRule[],\n\tcaseInsensitive = false,\n\tintrospectionMode: IntrospectionMode = \"typename\",\n): boolean {\n\tif (introspectionMode === \"all\" && fieldName.startsWith(\"__\")) {\n\t\treturn true;\n\t}\n\n\tif (introspectionMode === \"typename\" && fieldName === \"__typename\") {\n\t\treturn true;\n\t}\n\n\tif (!ignore || ignore.length === 0) {\n\t\treturn false;\n\t}\n\n\t// Precompute lowercased field name once for all string rule comparisons\n\tconst normalizedFieldName = caseInsensitive ? fieldName.toLowerCase() : fieldName;\n\n\tfor (const rule of ignore) {\n\t\tif (typeof rule === \"string\") {\n\t\t\tconst normalizedRule = caseInsensitive ? rule.toLowerCase() : rule;\n\t\t\tif (normalizedRule === normalizedFieldName) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (rule instanceof RegExp) {\n\t\t\ttry {\n\t\t\t\t// Reset lastIndex for stateful regexes (/g, /y flags) to ensure\n\t\t\t\t// consistent results. This mutates the RegExp object, which is\n\t\t\t\t// intentional. Without the reset, repeated calls with the same\n\t\t\t\t// global or sticky regex produce inconsistent results.\n\t\t\t\tif (rule.global || rule.sticky) {\n\t\t\t\t\trule.lastIndex = 0;\n\t\t\t\t}\n\t\t\t\tif (rule.test(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Frozen or exotic RegExp objects throw on lastIndex mutation or\n\t\t\t\t// test(). Wrap as IgnoreRuleError so the caller can handle it\n\t\t\t\t// consistently with function rule errors.\n\t\t\t\tconst message = `Ignore rule RegExp threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tif (rule(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = `Ignore rule function threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"],"mappings":"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","names":["import_graphql","import_graphql","parentType","suffix","candidate"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -4,14 +4,17 @@ import { ValidationRule } from 'graphql';
|
|
|
4
4
|
/**
|
|
5
5
|
* Error extension codes for depth limit violations.
|
|
6
6
|
*/
|
|
7
|
-
declare const ERROR_CODES: {
|
|
7
|
+
declare const ERROR_CODES: Readonly<{
|
|
8
8
|
readonly IGNORE_RULE_ERROR: "IGNORE_RULE_ERROR";
|
|
9
9
|
readonly QUERY_TOO_DEEP: "QUERY_TOO_DEEP";
|
|
10
|
-
}
|
|
10
|
+
}>;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Callback invoked after depth validation with per-operation depth results.
|
|
14
14
|
*
|
|
15
|
+
* The `depths` argument is always a plain object (Object.prototype) for
|
|
16
|
+
* compatibility with common object utilities and `hasOwnProperty` access.
|
|
17
|
+
*
|
|
15
18
|
* @example
|
|
16
19
|
* ```typescript
|
|
17
20
|
* const callback: DepthCallback = (depths) => {
|
|
@@ -227,7 +230,7 @@ type IntrospectionMode = "all" | "none" | "typename";
|
|
|
227
230
|
*
|
|
228
231
|
* @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)
|
|
229
232
|
* @param options - Optional configuration for ignore rules, directives, and case sensitivity
|
|
230
|
-
* @param callback - Optional callback invoked with
|
|
233
|
+
* @param callback - Optional callback invoked with per-operation depths as a plain object payload
|
|
231
234
|
* @returns A GraphQL validation rule function
|
|
232
235
|
* @throws {Error} If `maxDepth` is not a non-negative integer
|
|
233
236
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -4,14 +4,17 @@ import { ValidationRule } from 'graphql';
|
|
|
4
4
|
/**
|
|
5
5
|
* Error extension codes for depth limit violations.
|
|
6
6
|
*/
|
|
7
|
-
declare const ERROR_CODES: {
|
|
7
|
+
declare const ERROR_CODES: Readonly<{
|
|
8
8
|
readonly IGNORE_RULE_ERROR: "IGNORE_RULE_ERROR";
|
|
9
9
|
readonly QUERY_TOO_DEEP: "QUERY_TOO_DEEP";
|
|
10
|
-
}
|
|
10
|
+
}>;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Callback invoked after depth validation with per-operation depth results.
|
|
14
14
|
*
|
|
15
|
+
* The `depths` argument is always a plain object (Object.prototype) for
|
|
16
|
+
* compatibility with common object utilities and `hasOwnProperty` access.
|
|
17
|
+
*
|
|
15
18
|
* @example
|
|
16
19
|
* ```typescript
|
|
17
20
|
* const callback: DepthCallback = (depths) => {
|
|
@@ -227,7 +230,7 @@ type IntrospectionMode = "all" | "none" | "typename";
|
|
|
227
230
|
*
|
|
228
231
|
* @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)
|
|
229
232
|
* @param options - Optional configuration for ignore rules, directives, and case sensitivity
|
|
230
|
-
* @param callback - Optional callback invoked with
|
|
233
|
+
* @param callback - Optional callback invoked with per-operation depths as a plain object payload
|
|
231
234
|
* @returns A GraphQL validation rule function
|
|
232
235
|
* @throws {Error} If `maxDepth` is not a non-negative integer
|
|
233
236
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/constants.ts
|
|
2
|
-
var ERROR_CODES = {
|
|
2
|
+
var ERROR_CODES = Object.freeze({
|
|
3
3
|
IGNORE_RULE_ERROR: "IGNORE_RULE_ERROR",
|
|
4
4
|
QUERY_TOO_DEEP: "QUERY_TOO_DEEP"
|
|
5
|
-
};
|
|
5
|
+
});
|
|
6
6
|
|
|
7
7
|
// src/depth-limit.ts
|
|
8
8
|
import {
|
|
@@ -112,6 +112,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
112
112
|
if (!frame.node.selectionSet) {
|
|
113
113
|
continue;
|
|
114
114
|
}
|
|
115
|
+
const nextFrames = [];
|
|
115
116
|
for (const selection of frame.node.selectionSet.selections) {
|
|
116
117
|
switch (selection.kind) {
|
|
117
118
|
case Kind2.FIELD: {
|
|
@@ -193,7 +194,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
193
194
|
deepestViolation = violation;
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
nextFrames.push({
|
|
197
198
|
currentDepth: newDepth,
|
|
198
199
|
hasDirectiveLimit,
|
|
199
200
|
ignoredFieldsOnPath,
|
|
@@ -217,7 +218,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
217
218
|
const fragmentVisited = new Set(frame.visitedFragments);
|
|
218
219
|
fragmentVisited.add(fragmentName);
|
|
219
220
|
const parentType2 = fragment.typeCondition ? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
220
|
-
|
|
221
|
+
nextFrames.push({
|
|
221
222
|
currentDepth: frame.currentDepth,
|
|
222
223
|
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
223
224
|
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
@@ -231,7 +232,7 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
231
232
|
}
|
|
232
233
|
case Kind2.INLINE_FRAGMENT: {
|
|
233
234
|
const parentType2 = selection.typeCondition ? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
234
|
-
|
|
235
|
+
nextFrames.push({
|
|
235
236
|
currentDepth: frame.currentDepth,
|
|
236
237
|
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
237
238
|
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
@@ -245,13 +246,22 @@ function calculateDepth(caches, config, fragments, maxDepth, node, parentType, s
|
|
|
245
246
|
}
|
|
246
247
|
default: {
|
|
247
248
|
const exhaustiveCheck = selection;
|
|
248
|
-
|
|
249
|
+
throwUnhandledSelectionKind(exhaustiveCheck);
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
}
|
|
253
|
+
for (let index = nextFrames.length - 1; index >= 0; index--) {
|
|
254
|
+
const nextFrame = nextFrames[index];
|
|
255
|
+
if (nextFrame) {
|
|
256
|
+
stack.push(nextFrame);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
252
259
|
}
|
|
253
260
|
return { depth: globalMaxDepth, violation: deepestViolation };
|
|
254
261
|
}
|
|
262
|
+
function throwUnhandledSelectionKind(selection) {
|
|
263
|
+
throw new Error(`Unhandled selection kind: ${selection.kind}`);
|
|
264
|
+
}
|
|
255
265
|
function collectInterfaces(type) {
|
|
256
266
|
const interfaces = [];
|
|
257
267
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -365,19 +375,13 @@ function assertBooleanOption(name, value) {
|
|
|
365
375
|
function createValidationRule(maxDepth, options, callback) {
|
|
366
376
|
const shortCircuit = options?.shortCircuit ?? callback == null;
|
|
367
377
|
return function depthLimitValidationRule(context) {
|
|
368
|
-
let anonymousCount = 0;
|
|
369
378
|
const caches = createTraversalCaches();
|
|
370
379
|
const document = context.getDocument();
|
|
371
|
-
const depths = callback ?
|
|
380
|
+
const depths = callback ? createDepthResultRecord() : void 0;
|
|
372
381
|
const { fragments, operations } = extractDefinitions(document.definitions);
|
|
373
382
|
const schema = context.getSchema() ?? void 0;
|
|
374
383
|
const useDirective = Boolean(schema) && (options?.useDirective ?? false);
|
|
375
|
-
const
|
|
376
|
-
for (const op of operations) {
|
|
377
|
-
if (op.name?.value) {
|
|
378
|
-
namedOperationNames.add(op.name.value);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
384
|
+
const nextOperationName = createOperationNameAllocator(operations);
|
|
381
385
|
const config = {
|
|
382
386
|
caseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,
|
|
383
387
|
directiveMode: options?.directiveMode ?? "cap",
|
|
@@ -398,18 +402,7 @@ function createValidationRule(maxDepth, options, callback) {
|
|
|
398
402
|
subscription: void 0
|
|
399
403
|
};
|
|
400
404
|
for (const operation of operations) {
|
|
401
|
-
|
|
402
|
-
if (operation.name?.value) {
|
|
403
|
-
operationName = operation.name.value;
|
|
404
|
-
} else {
|
|
405
|
-
let candidate = anonymousCount === 0 ? "anonymous" : `anonymous_${anonymousCount}`;
|
|
406
|
-
while (namedOperationNames.has(candidate)) {
|
|
407
|
-
anonymousCount++;
|
|
408
|
-
candidate = `anonymous_${anonymousCount}`;
|
|
409
|
-
}
|
|
410
|
-
operationName = candidate;
|
|
411
|
-
anonymousCount++;
|
|
412
|
-
}
|
|
405
|
+
const operationName = nextOperationName(operation);
|
|
413
406
|
const rootType = rootTypeMap[operation.operation];
|
|
414
407
|
let result;
|
|
415
408
|
try {
|
|
@@ -455,7 +448,7 @@ function createValidationRule(maxDepth, options, callback) {
|
|
|
455
448
|
}
|
|
456
449
|
}
|
|
457
450
|
if (callback && depths) {
|
|
458
|
-
callback(depths);
|
|
451
|
+
callback({ ...depths });
|
|
459
452
|
}
|
|
460
453
|
return {};
|
|
461
454
|
};
|
|
@@ -520,6 +513,43 @@ function normalizeIgnoreRules(ignore) {
|
|
|
520
513
|
}
|
|
521
514
|
return rules;
|
|
522
515
|
}
|
|
516
|
+
function createDepthResultRecord() {
|
|
517
|
+
return /* @__PURE__ */ Object.create(null);
|
|
518
|
+
}
|
|
519
|
+
function createOperationNameAllocator(operations) {
|
|
520
|
+
const explicitNames = /* @__PURE__ */ new Set();
|
|
521
|
+
for (const operation of operations) {
|
|
522
|
+
if (operation.name?.value) {
|
|
523
|
+
explicitNames.add(operation.name.value);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
527
|
+
const namedCounts = /* @__PURE__ */ new Map();
|
|
528
|
+
let anonymousCount = 0;
|
|
529
|
+
return (operation) => {
|
|
530
|
+
const explicitName = operation.name?.value;
|
|
531
|
+
if (explicitName) {
|
|
532
|
+
let suffix2 = namedCounts.get(explicitName) ?? 0;
|
|
533
|
+
let candidate2 = suffix2 === 0 ? explicitName : `${explicitName}_${suffix2}`;
|
|
534
|
+
while (usedNames.has(candidate2) || suffix2 > 0 && explicitNames.has(candidate2)) {
|
|
535
|
+
suffix2++;
|
|
536
|
+
candidate2 = `${explicitName}_${suffix2}`;
|
|
537
|
+
}
|
|
538
|
+
namedCounts.set(explicitName, suffix2 + 1);
|
|
539
|
+
usedNames.add(candidate2);
|
|
540
|
+
return candidate2;
|
|
541
|
+
}
|
|
542
|
+
let suffix = anonymousCount;
|
|
543
|
+
let candidate = suffix === 0 ? "anonymous" : `anonymous_${suffix}`;
|
|
544
|
+
while (usedNames.has(candidate) || explicitNames.has(candidate)) {
|
|
545
|
+
suffix++;
|
|
546
|
+
candidate = `anonymous_${suffix}`;
|
|
547
|
+
}
|
|
548
|
+
anonymousCount = suffix + 1;
|
|
549
|
+
usedNames.add(candidate);
|
|
550
|
+
return candidate;
|
|
551
|
+
};
|
|
552
|
+
}
|
|
523
553
|
function setDepthResult(target, key, value) {
|
|
524
554
|
Object.defineProperty(target, key, {
|
|
525
555
|
configurable: true,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/depth-limit.ts","../src/depth-engine.ts","../src/directives.ts","../src/ignore.ts"],"sourcesContent":["/**\n * Error extension codes for depth limit violations.\n */\nexport const ERROR_CODES = {\n\tIGNORE_RULE_ERROR: \"IGNORE_RULE_ERROR\",\n\tQUERY_TOO_DEEP: \"QUERY_TOO_DEEP\",\n} as const;\n","import {\n\ttype ASTVisitor,\n\tGraphQLError,\n\ttype ValidationContext,\n\ttype ValidationRule,\n} from \"graphql\";\n\nimport { ERROR_CODES } from \"./constants.js\";\nimport {\n\tcalculateDepth,\n\tcreateTraversalCaches,\n\textractDefinitions,\n\ttype TraversalConfig,\n} from \"./depth-engine.js\";\nimport type { DepthCallback, DepthLimitFunction, DepthLimitOptions, IgnoreRule } from \"./types.js\";\n\n/** Valid values for the `directiveMode` option. */\nconst DIRECTIVE_MODES = new Set<string>([\"cap\", \"override\"]);\n\n/** Valid values for the `ignoreMode` option. */\nconst IGNORE_MODES = new Set<string>([\"exclude\", \"skip\"]);\n\n/** Valid values for the `ignoreIntrospection` option. */\nconst INTROSPECTION_MODES = new Set<string>([\"all\", \"none\", \"typename\"]);\n\n/**\n * Normalized depthLimit options with validated ignore rules.\n */\ntype NormalizedDepthLimitOptions = Omit<DepthLimitOptions, \"ignore\"> & {\n\tignore?: IgnoreRule[];\n};\n\n/**\n * Creates a GraphQL validation rule that limits query depth.\n *\n * Prevents DoS attacks and resource exhaustion from excessively deep queries\n * by enforcing a maximum depth on operations. Supports per-field overrides\n * via the `@depth` directive, customizable ignore rules, and an optional\n * callback for monitoring.\n *\n * Security considerations:\n * - The global `maxDepth` is a hard ceiling by default (`directiveMode: \"cap\"`)\n * - Correctly handles fragments reused at different depths\n * - Circular fragment references are detected per-path\n * - Only `__typename` is ignored by default (`ignoreIntrospection: \"typename\"`)\n * - Short-circuits on first violation when no callback is provided\n *\n * Limitations:\n * - Variables in `@depth` directives are not supported (falls back to global limit)\n * - Field names are case-sensitive by default (use `caseInsensitiveIgnore` if needed)\n * - `useDirective: true` requires a schema in the validation context; without one\n * it silently falls back to the global limit (directives cannot be resolved)\n * - RegExp ignore rules are executed against field names; patterns with catastrophic\n * backtracking (e.g., `/^(a+)+$/`) may cause performance issues\n *\n * @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)\n * @param options - Optional configuration for ignore rules, directives, and case sensitivity\n * @param callback - Optional callback invoked with depth results for each operation\n * @returns A GraphQL validation rule function\n * @throws {Error} If `maxDepth` is not a non-negative integer\n *\n * @example\n * ```typescript\n * import { depthLimit } from \"graphql-query-depth-limit-esm\";\n * import { validate } from \"graphql\";\n *\n * const errors = validate(schema, document, [\n * depthLimit(5, {\n * ignore: [\"friends\", /.*Connection$/],\n * useDirective: true,\n * }),\n * ]);\n *\n * const withCallback = depthLimit(5, (depths) => {\n * console.log(depths);\n * });\n * ```\n */\nexport function depthLimit(maxDepth: number, callback?: DepthCallback): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions | DepthCallback,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 0) {\n\t\tthrow new Error(`Invalid maxDepth: ${maxDepth}. Must be a non-negative integer.`);\n\t}\n\n\tconst normalized = normalizeDepthLimitArgs(options, callback);\n\n\treturn createValidationRule(maxDepth, normalized.options, normalized.callback);\n}\n\n/** Compile-time check that the implementation satisfies the public type. */\ndepthLimit satisfies DepthLimitFunction;\n\n/**\n * Validates that a value is a boolean or undefined.\n */\nfunction assertBooleanOption(name: string, value: unknown): void {\n\tif (value !== undefined && typeof value !== \"boolean\") {\n\t\tthrow new TypeError(`Invalid ${name}: expected boolean, received ${typeof value}.`);\n\t}\n}\n\n/**\n * Creates the validation rule closure with validated parameters.\n */\nfunction createValidationRule(\n\tmaxDepth: number,\n\toptions?: NormalizedDepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tconst shortCircuit = options?.shortCircuit ?? callback == null;\n\n\treturn function depthLimitValidationRule(context: ValidationContext): ASTVisitor {\n\t\tlet anonymousCount = 0;\n\t\tconst caches = createTraversalCaches();\n\t\tconst document = context.getDocument();\n\t\tconst depths: Record<string, number> | undefined = callback ? {} : undefined;\n\t\tconst { fragments, operations } = extractDefinitions(document.definitions);\n\t\tconst schema = context.getSchema() ?? undefined;\n\t\t// By design: when useDirective is true but no schema is available,\n\t\t// directives silently fall back to the global maxDepth. This is not a\n\t\t// \"silent failure\" — directives cannot be resolved without type info,\n\t\t// so the global limit is the correct and safe default. Emitting an\n\t\t// error here would penalize valid schema-less contexts (e.g., custom\n\t\t// ValidationContext wrappers) where the user intentionally omits the\n\t\t// schema. See DepthLimitOptions.useDirective JSDoc for documentation.\n\t\tconst useDirective = Boolean(schema) && (options?.useDirective ?? false);\n\n\t\t// Pre-collect named operation names to avoid key collisions with\n\t\t// generated anonymous operation keys (e.g., \"anonymous\", \"anonymous_1\").\n\t\tconst namedOperationNames = new Set<string>();\n\t\tfor (const op of operations) {\n\t\t\tif (op.name?.value) {\n\t\t\t\tnamedOperationNames.add(op.name.value);\n\t\t\t}\n\t\t}\n\n\t\tconst config: TraversalConfig = {\n\t\t\tcaseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,\n\t\t\tdirectiveMode: options?.directiveMode ?? \"cap\",\n\t\t\tignore: options?.ignore,\n\t\t\tignoreMode: options?.ignoreMode ?? \"exclude\",\n\t\t\tintrospectionMode: options?.ignoreIntrospection ?? \"typename\",\n\t\t\tlimitIgnoredRecursion: options?.limitIgnoredRecursion ?? false,\n\t\t\tshortCircuit,\n\t\t\tuseDirective,\n\t\t};\n\n\t\tconst rootTypeMap = schema\n\t\t\t? {\n\t\t\t\t\tmutation: schema.getMutationType() ?? undefined,\n\t\t\t\t\tquery: schema.getQueryType() ?? undefined,\n\t\t\t\t\tsubscription: schema.getSubscriptionType() ?? undefined,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tmutation: undefined,\n\t\t\t\t\tquery: undefined,\n\t\t\t\t\tsubscription: undefined,\n\t\t\t\t};\n\n\t\tfor (const operation of operations) {\n\t\t\tlet operationName: string;\n\t\t\tif (operation.name?.value) {\n\t\t\t\toperationName = operation.name.value;\n\t\t\t} else {\n\t\t\t\tlet candidate = anonymousCount === 0 ? \"anonymous\" : `anonymous_${anonymousCount}`;\n\t\t\t\twhile (namedOperationNames.has(candidate)) {\n\t\t\t\t\tanonymousCount++;\n\t\t\t\t\tcandidate = `anonymous_${anonymousCount}`;\n\t\t\t\t}\n\t\t\t\toperationName = candidate;\n\t\t\t\tanonymousCount++;\n\t\t\t}\n\t\t\tconst rootType = rootTypeMap[operation.operation];\n\n\t\t\tlet result: ReturnType<typeof calculateDepth>;\n\t\t\ttry {\n\t\t\t\tresult = calculateDepth(caches, config, fragments, maxDepth, operation, rootType, schema);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && error.name === \"IgnoreRuleError\") {\n\t\t\t\t\tcontext.reportError(\n\t\t\t\t\t\tnew GraphQLError(error.message, {\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.IGNORE_RULE_ERROR,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: [operation],\n\t\t\t\t\t\t\toriginalError: error,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tif (depths) {\n\t\t\t\tsetDepthResult(depths, operationName, result.depth);\n\t\t\t}\n\n\t\t\tif (result.violation) {\n\t\t\t\tconst depthValue = result.violation.depth;\n\t\t\t\tconst depthLabel = shortCircuit ? `at least ${depthValue}` : `${depthValue}`;\n\t\t\t\tconst violationPath = result.violation.path;\n\t\t\t\tconst pathSuffix = violationPath.length > 0 ? ` (at ${violationPath.join(\".\")})` : \"\";\n\n\t\t\t\tcontext.reportError(\n\t\t\t\t\tnew GraphQLError(\n\t\t\t\t\t\t`'${operationName}' has depth ${depthLabel} which exceeds maximum allowed depth of ${result.violation.maxDepth}${pathSuffix}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.QUERY_TOO_DEEP,\n\t\t\t\t\t\t\t\tdepth: depthValue,\n\t\t\t\t\t\t\t\tmaxDepth: result.violation.maxDepth,\n\t\t\t\t\t\t\t\tpath: violationPath,\n\t\t\t\t\t\t\t\tshortCircuit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: result.violation.node ? [operation, result.violation.node] : [operation],\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (callback && depths) {\n\t\t\tcallback(depths);\n\t\t}\n\n\t\t// All depth validation is performed eagerly above because fragment\n\t\t// resolution requires the full document upfront. Nothing remains\n\t\t// for the visitor traversal phase.\n\t\treturn {};\n\t};\n}\n\n/**\n * Determines whether a value is a valid ignore rule.\n */\nfunction isIgnoreRule(rule: unknown): rule is IgnoreRule {\n\treturn typeof rule === \"function\" || rule instanceof RegExp || typeof rule === \"string\";\n}\n\n/**\n * Normalizes the optional depthLimit arguments.\n */\nfunction normalizeDepthLimitArgs(\n\toptions: DepthLimitOptions | DepthCallback | undefined,\n\tcallback: DepthCallback | undefined,\n): { callback?: DepthCallback; options?: NormalizedDepthLimitOptions } {\n\tif (callback !== undefined && typeof callback !== \"function\") {\n\t\tthrow new TypeError(\"Invalid callback: expected a function.\");\n\t}\n\n\tif (typeof options === \"function\") {\n\t\tif (callback) {\n\t\t\tthrow new TypeError(\"Invalid depthLimit arguments: callback provided twice.\");\n\t\t}\n\n\t\treturn { callback: options, options: undefined };\n\t}\n\n\tif (\n\t\toptions !== undefined &&\n\t\t(options === null || typeof options !== \"object\" || Array.isArray(options))\n\t) {\n\t\tconst receivedType = Array.isArray(options)\n\t\t\t? \"array\"\n\t\t\t: options === null\n\t\t\t\t? \"null\"\n\t\t\t\t: typeof options;\n\t\tthrow new TypeError(`Invalid options: expected an object, received ${receivedType}.`);\n\t}\n\n\treturn {\n\t\tcallback,\n\t\toptions: options ? normalizeDepthLimitOptions(options) : undefined,\n\t};\n}\n\n/**\n * Normalizes and validates depthLimit options.\n */\nfunction normalizeDepthLimitOptions(options: DepthLimitOptions): NormalizedDepthLimitOptions {\n\tassertBooleanOption(\"caseInsensitiveIgnore\", options.caseInsensitiveIgnore);\n\n\tif (options.directiveMode !== undefined && !DIRECTIVE_MODES.has(options.directiveMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid directiveMode: \"${options.directiveMode}\". Must be \"cap\" or \"override\".`,\n\t\t);\n\t}\n\n\tif (\n\t\toptions.ignoreIntrospection !== undefined &&\n\t\t!INTROSPECTION_MODES.has(options.ignoreIntrospection)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreIntrospection: \"${options.ignoreIntrospection}\". Must be \"all\", \"none\", or \"typename\".`,\n\t\t);\n\t}\n\n\tif (options.ignoreMode !== undefined && !IGNORE_MODES.has(options.ignoreMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreMode: \"${options.ignoreMode}\". Must be \"exclude\" or \"skip\".`,\n\t\t);\n\t}\n\n\tassertBooleanOption(\"limitIgnoredRecursion\", options.limitIgnoredRecursion);\n\tassertBooleanOption(\"shortCircuit\", options.shortCircuit);\n\tassertBooleanOption(\"useDirective\", options.useDirective);\n\n\tconst ignore = normalizeIgnoreRules(options.ignore);\n\treturn { ...options, ignore };\n}\n\n/**\n * Normalizes and validates ignore rules.\n */\nfunction normalizeIgnoreRules(ignore: DepthLimitOptions[\"ignore\"]): IgnoreRule[] | undefined {\n\tif (ignore == null) {\n\t\treturn undefined;\n\t}\n\n\tconst rules: unknown[] = Array.isArray(ignore) ? ignore : [ignore];\n\n\tfor (const [index, rule] of rules.entries()) {\n\t\tif (!isIgnoreRule(rule)) {\n\t\t\tconst receivedType = Array.isArray(rule) ? \"array\" : rule === null ? \"null\" : typeof rule;\n\t\t\tthrow new TypeError(\n\t\t\t\t`Invalid ignore rule at index ${index}: expected string, RegExp, or function, received ${receivedType}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn rules as IgnoreRule[];\n}\n\n/**\n * Safely assigns a depth entry without allowing prototype pollution.\n */\nfunction setDepthResult(target: Record<string, number>, key: string, value: number): void {\n\tObject.defineProperty(target, key, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tvalue,\n\t\twritable: true,\n\t});\n}\n","import {\n\ttype ASTNode,\n\ttype DefinitionNode,\n\ttype FragmentDefinitionNode,\n\ttype GraphQLField,\n\ttype GraphQLInterfaceType,\n\ttype GraphQLObjectType,\n\ttype GraphQLOutputType,\n\ttype GraphQLSchema,\n\tgetNamedType,\n\tisCompositeType,\n\tisInterfaceType,\n\tisObjectType,\n\tKind,\n\ttype OperationDefinitionNode,\n\ttype SelectionNode,\n} from \"graphql\";\n\nimport { getDepthFromDirective } from \"./directives.js\";\nimport { shouldIgnoreField } from \"./ignore.js\";\nimport type { DirectiveMode, IgnoreMode, IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Result returned by the depth calculation engine.\n */\ninterface DepthResult {\n\t/** Maximum depth found across all branches */\n\tdepth: number;\n\t/** Deepest violation found, or `null` if within limits */\n\tviolation: DepthViolation | null;\n}\n\n/**\n * Records a depth limit violation with the offending depth and its limit.\n */\ninterface DepthViolation {\n\t/** The actual depth that exceeded the limit */\n\tdepth: number;\n\t/** The maximum allowed depth that was exceeded */\n\tmaxDepth: number;\n\t/** The AST node that caused the violation, for precise error locations */\n\tnode?: ASTNode;\n\t/** Field path from the operation root to the violation point */\n\tpath: string[];\n}\n\n/**\n * Result of resolving a `@depth` directive on a field definition.\n * @internal\n */\ninterface DirectiveResolution {\n\t/** Whether a directive limit is now active on this path */\n\thasDirectiveLimit: boolean;\n\t/** The resolved maximum depth for this branch */\n\tmaxDepth: number;\n}\n\n/**\n * Lightweight linked-list node for building field paths without per-field\n * array allocations. Only materialized into `string[]` when reporting a\n * violation or populating callback results.\n * @internal\n */\ninterface PathNode {\n\t/** Parent node in the path, or `undefined` for the root */\n\tparent: PathNode | undefined;\n\t/** Field name or alias for this path segment */\n\tsegment: string;\n}\n\n/**\n * Single unit of work on the iterative DFS stack.\n * @internal\n */\ninterface StackFrame {\n\t/** Current depth at this point in traversal */\n\tcurrentDepth: number;\n\t/** Whether a `@depth` directive has already constrained this path */\n\thasDirectiveLimit: boolean;\n\t/** Ignored field names seen on the current path (for recursion guard) */\n\tignoredFieldsOnPath: Set<string>;\n\t/** Maximum allowed depth for this branch */\n\tmaxDepth: number;\n\t/** The AST node whose selectionSet should be processed */\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } };\n\t/** Parent type for field resolution */\n\tparentType: GraphQLOutputType | undefined;\n\t/** Linked-list path from the operation root to this node */\n\tpath: PathNode | undefined;\n\t/** Fragment names visited on the current path (for cycle detection) */\n\tvisitedFragments: Set<string>;\n}\n\n/**\n * Caches shared across all stack frames during a single validation run\n * to avoid repeated interface graph walks and directive AST lookups.\n * @internal\n */\ninterface TraversalCaches {\n\t/** Cached raw directive depth per `typeName:fieldName` */\n\tdirectiveDepths: Map<string, number | undefined>;\n\t/** Cached interface lists per type name */\n\tinterfaces: Map<string, GraphQLInterfaceType[]>;\n}\n\n/**\n * Immutable configuration shared across all stack frames during traversal.\n * @internal\n */\ninterface TraversalConfig {\n\t/** Whether to use case-insensitive matching for string ignore rules */\n\tcaseInsensitiveIgnore: boolean;\n\t/** Controls how `@depth` directives interact with the global `maxDepth` */\n\tdirectiveMode: DirectiveMode;\n\t/** Rules for fields to ignore in depth calculation */\n\tignore: IgnoreRule[] | undefined;\n\t/** Controls how ignored fields affect depth traversal */\n\tignoreMode: IgnoreMode;\n\t/** Controls which introspection fields are ignored */\n\tintrospectionMode: IntrospectionMode;\n\t/** Whether repeated ignored fields on a path should increment depth */\n\tlimitIgnoredRecursion: boolean;\n\t/** Whether to bail immediately on violation (when no callback needs true depth) */\n\tshortCircuit: boolean;\n\t/** Whether to check for `@depth` directives on fields */\n\tuseDirective: boolean;\n}\n\n/**\n * Calculates the depth of a GraphQL query AST node using iterative DFS.\n *\n * Handles three selection types:\n * - **Fields**: Increment depth by 1 for composite (non-scalar) fields\n * - **Fragment spreads**: Expand the fragment in-place (no depth increment)\n * - **Inline fragments**: Process in-place (no depth increment)\n *\n * Fragment cycle detection uses per-path visited sets so that the same\n * fragment reused at different depths is calculated correctly.\n *\n * When `shortCircuit` is enabled (no callback), the engine bails immediately\n * on the first violation instead of traversing the full subtree.\n *\n * Uses an explicit stack instead of recursion to prevent stack overflow\n * on deeply nested queries.\n *\n * @param caches - Shared caches for interface and directive lookups\n * @param config - Immutable traversal configuration\n * @param fragments - Map of all fragment definitions in the document\n * @param maxDepth - Maximum allowed depth for this branch\n * @param node - The AST node to calculate depth for\n * @param parentType - Root type for field resolution\n * @param schema - GraphQL schema for type resolution\n * @returns The maximum depth and the deepest violation found\n */\nfunction calculateDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tfragments: Map<string, FragmentDefinitionNode>,\n\tmaxDepth: number,\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } },\n\tparentType: GraphQLOutputType | undefined,\n\tschema: GraphQLSchema | undefined,\n): DepthResult {\n\tlet deepestViolation: DepthViolation | null = null;\n\tlet globalMaxDepth = 0;\n\n\tif (!node.selectionSet) {\n\t\treturn { depth: 0, violation: null };\n\t}\n\n\tconst stack: StackFrame[] = [\n\t\t{\n\t\t\tcurrentDepth: 0,\n\t\t\thasDirectiveLimit: false,\n\t\t\tignoredFieldsOnPath: new Set<string>(),\n\t\t\tmaxDepth,\n\t\t\tnode,\n\t\t\tparentType,\n\t\t\tpath: undefined,\n\t\t\tvisitedFragments: new Set<string>(),\n\t\t},\n\t];\n\n\tfor (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n\t\tif (!frame.node.selectionSet) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const selection of frame.node.selectionSet.selections) {\n\t\t\tswitch (selection.kind) {\n\t\t\t\tcase Kind.FIELD: {\n\t\t\t\t\tconst fieldName = selection.name.value;\n\t\t\t\t\tconst isIntrospectionField = fieldName.startsWith(\"__\");\n\n\t\t\t\t\t// When introspection is fully ignored, always skip the subtree\n\t\t\t\t\t// regardless of ignoreMode.\n\t\t\t\t\tif (config.introspectionMode === \"all\" && isIntrospectionField) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Leaf fields (no selectionSet) never contribute to depth,\n\t\t\t\t\t// so skip them before running ignore rules to avoid unnecessary\n\t\t\t\t\t// predicate evaluation and potential errors on irrelevant fields.\n\t\t\t\t\tif (!selection.selectionSet) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isIgnored = shouldIgnoreField(\n\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\tconfig.ignore,\n\t\t\t\t\t\tconfig.caseInsensitiveIgnore,\n\t\t\t\t\t\tconfig.introspectionMode,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (isIgnored && config.ignoreMode === \"skip\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Resolve field type and directive depth\n\t\t\t\t\tlet fieldMaxDepth = frame.maxDepth;\n\t\t\t\t\tlet fieldType: GraphQLOutputType | undefined;\n\t\t\t\t\tlet hasDirectiveLimit = frame.hasDirectiveLimit;\n\n\t\t\t\t\tif (schema && frame.parentType) {\n\t\t\t\t\t\tconst namedType = getNamedType(frame.parentType);\n\t\t\t\t\t\tif (isObjectType(namedType) || isInterfaceType(namedType)) {\n\t\t\t\t\t\t\tconst fieldDef = namedType.getFields()[fieldName];\n\t\t\t\t\t\t\tif (fieldDef) {\n\t\t\t\t\t\t\t\tfieldType = fieldDef.type;\n\n\t\t\t\t\t\t\t\t// Defensively skip non-composite fields that erroneously\n\t\t\t\t\t\t\t\t// have selections (normally caught by GraphQL's own\n\t\t\t\t\t\t\t\t// validation rules, but guards against miscounted depth\n\t\t\t\t\t\t\t\t// when this rule runs standalone or before other rules).\n\t\t\t\t\t\t\t\tconst resolvedType = getNamedType(fieldType);\n\t\t\t\t\t\t\t\tif (resolvedType && !isCompositeType(resolvedType)) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (config.useDirective) {\n\t\t\t\t\t\t\t\t\tconst resolved = resolveFieldDirectiveDepth(\n\t\t\t\t\t\t\t\t\t\tcaches,\n\t\t\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\t\t\tframe.currentDepth,\n\t\t\t\t\t\t\t\t\t\tframe.maxDepth,\n\t\t\t\t\t\t\t\t\t\tfieldDef,\n\t\t\t\t\t\t\t\t\t\tframe.hasDirectiveLimit,\n\t\t\t\t\t\t\t\t\t\tnamedType,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tfieldMaxDepth = resolved.maxDepth;\n\t\t\t\t\t\t\t\t\thasDirectiveLimit = resolved.hasDirectiveLimit;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Determine whether this ignored field should still increment\n\t\t\t\t\t// depth due to the recursion guard detecting repeated ignores\n\t\t\t\t\t// on the same path. The key is type-aware (`Type:field`) when\n\t\t\t\t\t// a schema is present so identically named fields on unrelated\n\t\t\t\t\t// types are tracked independently. Without a schema, the key\n\t\t\t\t\t// uses the field name alone, which may cause conservative\n\t\t\t\t\t// over-counting on paths with same-named fields on different types.\n\t\t\t\t\tlet ignoredFieldsOnPath = frame.ignoredFieldsOnPath;\n\t\t\t\t\tlet effectivelyIgnored = isIgnored;\n\n\t\t\t\t\tif (isIgnored && config.limitIgnoredRecursion) {\n\t\t\t\t\t\tconst parentName = frame.parentType ? getNamedType(frame.parentType)?.name : undefined;\n\t\t\t\t\t\tconst recursionKey = parentName ? `${parentName}:${fieldName}` : fieldName;\n\n\t\t\t\t\t\tif (ignoredFieldsOnPath.has(recursionKey)) {\n\t\t\t\t\t\t\t// Same type:field was already ignored on this path — increment depth\n\t\t\t\t\t\t\teffectivelyIgnored = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First occurrence — track it for subsequent path segments\n\t\t\t\t\t\t\tignoredFieldsOnPath = new Set(ignoredFieldsOnPath);\n\t\t\t\t\t\t\tignoredFieldsOnPath.add(recursionKey);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newDepth = effectivelyIgnored ? frame.currentDepth : frame.currentDepth + 1;\n\n\t\t\t\t\t// Use alias for path (matches the response shape clients see),\n\t\t\t\t\t// while fieldName is used for schema lookups and ignore rules.\n\t\t\t\t\tconst pathSegment = selection.alias?.value ?? fieldName;\n\t\t\t\t\tconst fieldPath = pathPush(frame.path, pathSegment);\n\n\t\t\t\t\t// Track maximum depth found\n\t\t\t\t\tif (newDepth > globalMaxDepth) {\n\t\t\t\t\t\tglobalMaxDepth = newDepth;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for violation\n\t\t\t\t\tif (newDepth > fieldMaxDepth) {\n\t\t\t\t\t\tconst violation: DepthViolation = {\n\t\t\t\t\t\t\tdepth: newDepth,\n\t\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\t\tpath: pathToArray(fieldPath),\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (config.shortCircuit) {\n\t\t\t\t\t\t\treturn { depth: newDepth, violation };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!deepestViolation || violation.depth > deepestViolation.depth) {\n\t\t\t\t\t\t\tdeepestViolation = violation;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Push children onto stack\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: newDepth,\n\t\t\t\t\t\thasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType: fieldType,\n\t\t\t\t\t\tpath: fieldPath,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.FRAGMENT_SPREAD: {\n\t\t\t\t\tconst fragmentName = selection.name.value;\n\n\t\t\t\t\t// Check membership before copying to avoid wasted allocations\n\t\t\t\t\t// when the fragment was already visited on this path.\n\t\t\t\t\tif (frame.visitedFragments.has(fragmentName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fragment = fragments.get(fragmentName);\n\t\t\t\t\tif (!fragment) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create independent copy for per-path cycle detection\n\t\t\t\t\tconst fragmentVisited = new Set(frame.visitedFragments);\n\t\t\t\t\tfragmentVisited.add(fragmentName);\n\n\t\t\t\t\tconst parentType = fragment.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: fragment,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: fragmentVisited,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.INLINE_FRAGMENT: {\n\t\t\t\t\tconst parentType = selection.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tstack.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst exhaustiveCheck: never = selection;\n\t\t\t\t\tthrow new Error(`Unhandled selection kind: ${(exhaustiveCheck as SelectionNode).kind}`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { depth: globalMaxDepth, violation: deepestViolation };\n}\n\n/**\n * Collects all interfaces implemented by a type, including transitively\n * inherited interfaces (interface-implements-interface chains).\n *\n * @param type - The object or interface type to collect interfaces from\n * @returns All directly and transitively implemented interfaces\n */\nfunction collectInterfaces(type: GraphQLInterfaceType | GraphQLObjectType): GraphQLInterfaceType[] {\n\tconst interfaces: GraphQLInterfaceType[] = [];\n\tconst seen = new Set<string>();\n\tconst stack = [...type.getInterfaces()];\n\n\tfor (let iface = stack.pop(); iface !== undefined; iface = stack.pop()) {\n\t\tif (seen.has(iface.name)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tseen.add(iface.name);\n\t\tinterfaces.push(iface);\n\n\t\tfor (const parent of iface.getInterfaces()) {\n\t\t\tif (!seen.has(parent.name)) {\n\t\t\t\tstack.push(parent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn interfaces;\n}\n\n/**\n * Creates empty traversal caches for a new validation run.\n *\n * @returns Fresh caches for interface and directive lookups\n */\nfunction createTraversalCaches(): TraversalCaches {\n\treturn {\n\t\tdirectiveDepths: new Map<string, number | undefined>(),\n\t\tinterfaces: new Map<string, GraphQLInterfaceType[]>(),\n\t};\n}\n\n/**\n * Extracts all fragment and operation definitions from a GraphQL document\n * in a single pass.\n *\n * **By design:** Duplicate fragment names are silently overwritten (last wins)\n * rather than raising a validation error. This is intentional — detecting\n * duplicates is the responsibility of GraphQL's built-in `UniqueFragmentNamesRule`,\n * not a depth-limiting rule. When both rules run together (the normal case),\n * duplicates are already caught before this code executes. When used standalone,\n * last-wins is a safe, deterministic fallback that avoids coupling depth\n * validation to fragment uniqueness concerns.\n *\n * @param definitions - Array of definition nodes from the parsed document\n * @returns Fragment map and operation array\n */\nfunction extractDefinitions(definitions: readonly DefinitionNode[]): {\n\tfragments: Map<string, FragmentDefinitionNode>;\n\toperations: OperationDefinitionNode[];\n} {\n\tconst fragments = new Map<string, FragmentDefinitionNode>();\n\tconst operations: OperationDefinitionNode[] = [];\n\n\tfor (const definition of definitions) {\n\t\tif (definition.kind === Kind.FRAGMENT_DEFINITION) {\n\t\t\tfragments.set(definition.name.value, definition);\n\t\t} else if (definition.kind === Kind.OPERATION_DEFINITION) {\n\t\t\toperations.push(definition);\n\t\t}\n\t}\n\n\treturn { fragments, operations };\n}\n\n/**\n * Returns cached interfaces for a type, computing and caching on first access.\n *\n * @param caches - Traversal caches\n * @param type - The object or interface type to get interfaces for\n * @returns All directly and transitively implemented interfaces\n */\nfunction getCachedInterfaces(\n\tcaches: TraversalCaches,\n\ttype: GraphQLInterfaceType | GraphQLObjectType,\n): GraphQLInterfaceType[] {\n\tconst cached = caches.interfaces.get(type.name);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\tconst result = collectInterfaces(type);\n\tcaches.interfaces.set(type.name, result);\n\treturn result;\n}\n\n/**\n * Creates a new path node by appending a segment to the parent path.\n *\n * @param parent - Parent path node, or `undefined` for the root\n * @param segment - Field name or alias for this path segment\n * @returns New path node linked to the parent\n */\nfunction pathPush(parent: PathNode | undefined, segment: string): PathNode {\n\treturn { parent, segment };\n}\n\n/**\n * Materializes a linked-list path into a string array.\n *\n * @param node - Leaf path node to materialize from\n * @returns Array of path segments from root to leaf\n */\nfunction pathToArray(node: PathNode | undefined): string[] {\n\tconst result: string[] = [];\n\tlet current = node;\n\twhile (current) {\n\t\tresult.push(current.segment);\n\t\tcurrent = current.parent;\n\t}\n\tresult.reverse();\n\treturn result;\n}\n\n/**\n * Resolves a `@depth` directive on a field definition, falling back to\n * interface field directives when the concrete field has none.\n *\n * **Precedence:** The concrete field's directive takes priority. Interface\n * directives are only consulted when the concrete field has no `@depth`.\n * When multiple interfaces define `@depth` on the same field, the strictest\n * (minimum) value wins.\n *\n * Results are memoized per `typeName:fieldName` in the traversal caches\n * to avoid repeated interface graph walks on large schemas.\n *\n * @param caches - Traversal caches for memoizing lookups\n * @param config - Traversal configuration\n * @param currentDepth - Current depth in the query tree\n * @param currentMaxDepth - Current maximum depth for this branch\n * @param fieldDef - The field definition to inspect\n * @param hasDirectiveLimit - Whether a directive has already constrained this path\n * @param namedType - The named parent type owning this field (already unwrapped)\n * @returns Resolved maximum depth and directive limit state\n */\nfunction resolveFieldDirectiveDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tcurrentDepth: number,\n\tcurrentMaxDepth: number,\n\tfieldDef: GraphQLField<unknown, unknown>,\n\thasDirectiveLimit: boolean,\n\tnamedType: GraphQLInterfaceType | GraphQLObjectType,\n): DirectiveResolution {\n\tconst cacheKey = `${namedType.name}:${fieldDef.name}`;\n\n\tlet directiveDepth: number | undefined;\n\n\tif (caches.directiveDepths.has(cacheKey)) {\n\t\tdirectiveDepth = caches.directiveDepths.get(cacheKey);\n\t} else {\n\t\tdirectiveDepth = getDepthFromDirective(fieldDef);\n\n\t\tif (directiveDepth === undefined) {\n\t\t\tconst interfaces = getCachedInterfaces(caches, namedType);\n\t\t\tfor (const iface of interfaces) {\n\t\t\t\tconst ifaceField = iface.getFields()[fieldDef.name];\n\t\t\t\tconst ifaceDepth = getDepthFromDirective(ifaceField);\n\t\t\t\tif (ifaceDepth !== undefined) {\n\t\t\t\t\tdirectiveDepth =\n\t\t\t\t\t\tdirectiveDepth === undefined ? ifaceDepth : Math.min(directiveDepth, ifaceDepth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcaches.directiveDepths.set(cacheKey, directiveDepth);\n\t}\n\n\tif (directiveDepth !== undefined) {\n\t\tconst relativeMax = currentDepth + directiveDepth;\n\t\tconst maxDepth =\n\t\t\tconfig.directiveMode === \"cap\"\n\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t: hasDirectiveLimit\n\t\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t\t: relativeMax;\n\t\treturn { hasDirectiveLimit: true, maxDepth };\n\t}\n\n\treturn { hasDirectiveLimit, maxDepth: currentMaxDepth };\n}\n\n/**\n * Resolves the parent type from a type condition on a fragment or inline fragment.\n *\n * Falls back to `currentParentType` when the schema is unavailable or the\n * type condition resolves to a non-composite type (which GraphQL's own\n * validation would reject, but is handled defensively here).\n *\n * @param typeConditionName - The name of the type condition\n * @param schema - GraphQL schema for type lookup\n * @param currentParentType - Fallback parent type if resolution fails\n * @returns The resolved composite type or the current parent type\n */\nfunction resolveTypeCondition(\n\ttypeConditionName: string,\n\tschema: GraphQLSchema | undefined,\n\tcurrentParentType: GraphQLOutputType | undefined,\n): GraphQLOutputType | undefined {\n\tif (schema) {\n\t\tconst type = schema.getType(typeConditionName);\n\t\tif (type && isCompositeType(type)) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn currentParentType;\n}\n\nexport { calculateDepth, createTraversalCaches, extractDefinitions };\nexport type { DepthResult, TraversalCaches, TraversalConfig };\n","import { type GraphQLField, Kind } from \"graphql\";\n\n/**\n * GraphQL SDL type definition for the `@depth` directive.\n *\n * Include this in your schema definition to enable per-field depth\n * overrides when using `depthLimit` with `{ useDirective: true }`.\n *\n * @example\n * ```ts\n * import { makeExecutableSchema } from \"@graphql-tools/schema\";\n * import { depthDirectiveTypeDefs } from \"graphql-query-depth-limit-esm\";\n *\n * const schema = makeExecutableSchema({\n * typeDefs: [depthDirectiveTypeDefs, yourTypeDefs],\n * resolvers,\n * });\n * ```\n */\nexport const depthDirectiveTypeDefs = `\n directive @depth(max: Int!) on FIELD_DEFINITION\n`;\n\n/**\n * Extracts the maximum depth from a `@depth(max: Int!)` directive on a field definition.\n *\n * Only integer literals are supported. Variables (e.g., `@depth(max: $var)`) are not\n * supported because variable values are unavailable during the validation phase.\n * Fields with variable-based directives fall back to the global depth limit.\n *\n * @param field - The GraphQL field definition to inspect\n * @returns The maximum depth from the directive, or `undefined` if not found or invalid\n *\n * @example\n * ```graphql\n * type Query {\n * users: [User!]! @depth(max: 3)\n * }\n * ```\n */\nexport function getDepthFromDirective(\n\tfield: GraphQLField<unknown, unknown> | undefined,\n): number | undefined {\n\tif (!field?.astNode?.directives) {\n\t\treturn undefined;\n\t}\n\n\tconst depthDirective = field.astNode.directives.find((d) => d.name.value === \"depth\");\n\n\tif (!depthDirective?.arguments) {\n\t\treturn undefined;\n\t}\n\n\tconst maxArg = depthDirective.arguments.find((arg) => arg.name.value === \"max\");\n\n\tif (maxArg?.value.kind === Kind.INT) {\n\t\tconst directiveDepth = Number.parseInt(maxArg.value.value, 10);\n\t\tif (Number.isFinite(directiveDepth) && directiveDepth >= 0) {\n\t\t\treturn directiveDepth;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import type { IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Determines whether a field should be ignored during depth calculation.\n *\n * Introspection field handling is controlled by the `introspectionMode` parameter:\n * - `\"all\"` — ignore every `__`-prefixed field\n * - `\"typename\"` (default) — only ignore `__typename`\n * - `\"none\"` — count all introspection fields toward depth\n *\n * **Warning:** When `ignoreMode: \"skip\"` is set, an ignored field's **entire\n * subtree** is skipped — not just the depth increment. Ignoring a composite field\n * bypasses all depth protection for everything nested under it. Use\n * `ignoreMode: \"exclude\"` (default) to skip only the depth increment while still\n * traversing children.\n *\n * @param fieldName - The name of the field to check\n * @param ignore - Array of ignore rules (strings, RegExp, or functions)\n * @param caseInsensitive - Whether to use case-insensitive matching for string rules\n * @param introspectionMode - How to handle introspection fields\n * @returns `true` if the field should be skipped, `false` otherwise\n *\n * @example\n * ```typescript\n * shouldIgnoreField(\"__typename\", []); // true (introspection, default mode)\n * shouldIgnoreField(\"__schema\", [], false, \"typename\"); // false (only __typename ignored)\n * shouldIgnoreField(\"__schema\", [], false, \"all\"); // true (all __ fields ignored)\n * shouldIgnoreField(\"metadata\", [\"metadata\"]); // true (exact match)\n * shouldIgnoreField(\"Metadata\", [\"metadata\"], true); // true (case-insensitive)\n * shouldIgnoreField(\"posts\", [/^internal/]); // false (no match)\n * ```\n */\nexport function shouldIgnoreField(\n\tfieldName: string,\n\tignore?: IgnoreRule[],\n\tcaseInsensitive = false,\n\tintrospectionMode: IntrospectionMode = \"typename\",\n): boolean {\n\tif (introspectionMode === \"all\" && fieldName.startsWith(\"__\")) {\n\t\treturn true;\n\t}\n\n\tif (introspectionMode === \"typename\" && fieldName === \"__typename\") {\n\t\treturn true;\n\t}\n\n\tif (!ignore || ignore.length === 0) {\n\t\treturn false;\n\t}\n\n\t// Precompute lowercased field name once for all string rule comparisons\n\tconst normalizedFieldName = caseInsensitive ? fieldName.toLowerCase() : fieldName;\n\n\tfor (const rule of ignore) {\n\t\tif (typeof rule === \"string\") {\n\t\t\tconst normalizedRule = caseInsensitive ? rule.toLowerCase() : rule;\n\t\t\tif (normalizedRule === normalizedFieldName) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (rule instanceof RegExp) {\n\t\t\ttry {\n\t\t\t\t// Reset lastIndex for stateful regexes (/g, /y flags) to ensure\n\t\t\t\t// consistent results. This mutates the RegExp object, which is\n\t\t\t\t// intentional. Without the reset, repeated calls with the same\n\t\t\t\t// global or sticky regex produce inconsistent results.\n\t\t\t\tif (rule.global || rule.sticky) {\n\t\t\t\t\trule.lastIndex = 0;\n\t\t\t\t}\n\t\t\t\tif (rule.test(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Frozen or exotic RegExp objects throw on lastIndex mutation or\n\t\t\t\t// test(). Wrap as IgnoreRuleError so the caller can handle it\n\t\t\t\t// consistently with function rule errors.\n\t\t\t\tconst message = `Ignore rule RegExp threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tif (rule(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = `Ignore rule function threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"],"mappings":"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","names":["Kind","Kind","parentType"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/depth-limit.ts","../src/depth-engine.ts","../src/directives.ts","../src/ignore.ts"],"sourcesContent":["/**\n * Error extension codes for depth limit violations.\n */\nexport const ERROR_CODES = Object.freeze({\n\tIGNORE_RULE_ERROR: \"IGNORE_RULE_ERROR\",\n\tQUERY_TOO_DEEP: \"QUERY_TOO_DEEP\",\n} as const);\n","import {\n\ttype ASTVisitor,\n\tGraphQLError,\n\ttype OperationDefinitionNode,\n\ttype ValidationContext,\n\ttype ValidationRule,\n} from \"graphql\";\n\nimport { ERROR_CODES } from \"./constants.js\";\nimport {\n\tcalculateDepth,\n\tcreateTraversalCaches,\n\textractDefinitions,\n\ttype TraversalConfig,\n} from \"./depth-engine.js\";\nimport type { DepthCallback, DepthLimitFunction, DepthLimitOptions, IgnoreRule } from \"./types.js\";\n\n/** Valid values for the `directiveMode` option. */\nconst DIRECTIVE_MODES = new Set<string>([\"cap\", \"override\"]);\n\n/** Valid values for the `ignoreMode` option. */\nconst IGNORE_MODES = new Set<string>([\"exclude\", \"skip\"]);\n\n/** Valid values for the `ignoreIntrospection` option. */\nconst INTROSPECTION_MODES = new Set<string>([\"all\", \"none\", \"typename\"]);\n\n/**\n * Normalized depthLimit options with validated ignore rules.\n */\ntype NormalizedDepthLimitOptions = Omit<DepthLimitOptions, \"ignore\"> & {\n\tignore?: IgnoreRule[];\n};\n\n/**\n * Creates a GraphQL validation rule that limits query depth.\n *\n * Prevents DoS attacks and resource exhaustion from excessively deep queries\n * by enforcing a maximum depth on operations. Supports per-field overrides\n * via the `@depth` directive, customizable ignore rules, and an optional\n * callback for monitoring.\n *\n * Security considerations:\n * - The global `maxDepth` is a hard ceiling by default (`directiveMode: \"cap\"`)\n * - Correctly handles fragments reused at different depths\n * - Circular fragment references are detected per-path\n * - Only `__typename` is ignored by default (`ignoreIntrospection: \"typename\"`)\n * - Short-circuits on first violation when no callback is provided\n *\n * Limitations:\n * - Variables in `@depth` directives are not supported (falls back to global limit)\n * - Field names are case-sensitive by default (use `caseInsensitiveIgnore` if needed)\n * - `useDirective: true` requires a schema in the validation context; without one\n * it silently falls back to the global limit (directives cannot be resolved)\n * - RegExp ignore rules are executed against field names; patterns with catastrophic\n * backtracking (e.g., `/^(a+)+$/`) may cause performance issues\n *\n * @param maxDepth - Maximum allowed depth for queries (must be a non-negative integer)\n * @param options - Optional configuration for ignore rules, directives, and case sensitivity\n * @param callback - Optional callback invoked with per-operation depths as a plain object payload\n * @returns A GraphQL validation rule function\n * @throws {Error} If `maxDepth` is not a non-negative integer\n *\n * @example\n * ```typescript\n * import { depthLimit } from \"graphql-query-depth-limit-esm\";\n * import { validate } from \"graphql\";\n *\n * const errors = validate(schema, document, [\n * depthLimit(5, {\n * ignore: [\"friends\", /.*Connection$/],\n * useDirective: true,\n * }),\n * ]);\n *\n * const withCallback = depthLimit(5, (depths) => {\n * console.log(depths);\n * });\n * ```\n */\nexport function depthLimit(maxDepth: number, callback?: DepthCallback): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule;\nexport function depthLimit(\n\tmaxDepth: number,\n\toptions?: DepthLimitOptions | DepthCallback,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 0) {\n\t\tthrow new Error(`Invalid maxDepth: ${maxDepth}. Must be a non-negative integer.`);\n\t}\n\n\tconst normalized = normalizeDepthLimitArgs(options, callback);\n\n\treturn createValidationRule(maxDepth, normalized.options, normalized.callback);\n}\n\n/** Compile-time check that the implementation satisfies the public type. */\ndepthLimit satisfies DepthLimitFunction;\n\n/**\n * Validates that a value is a boolean or undefined.\n */\nfunction assertBooleanOption(name: string, value: unknown): void {\n\tif (value !== undefined && typeof value !== \"boolean\") {\n\t\tthrow new TypeError(`Invalid ${name}: expected boolean, received ${typeof value}.`);\n\t}\n}\n\n/**\n * Creates the validation rule closure with validated parameters.\n */\nfunction createValidationRule(\n\tmaxDepth: number,\n\toptions?: NormalizedDepthLimitOptions,\n\tcallback?: DepthCallback,\n): ValidationRule {\n\tconst shortCircuit = options?.shortCircuit ?? callback == null;\n\n\treturn function depthLimitValidationRule(context: ValidationContext): ASTVisitor {\n\t\tconst caches = createTraversalCaches();\n\t\tconst document = context.getDocument();\n\t\tconst depths: Record<string, number> | undefined = callback\n\t\t\t? createDepthResultRecord()\n\t\t\t: undefined;\n\t\tconst { fragments, operations } = extractDefinitions(document.definitions);\n\t\tconst schema = context.getSchema() ?? undefined;\n\t\t// By design: when useDirective is true but no schema is available,\n\t\t// directives silently fall back to the global maxDepth. This is not a\n\t\t// \"silent failure\" — directives cannot be resolved without type info,\n\t\t// so the global limit is the correct and safe default. Emitting an\n\t\t// error here would penalize valid schema-less contexts (e.g., custom\n\t\t// ValidationContext wrappers) where the user intentionally omits the\n\t\t// schema. See DepthLimitOptions.useDirective JSDoc for documentation.\n\t\tconst useDirective = Boolean(schema) && (options?.useDirective ?? false);\n\n\t\tconst nextOperationName = createOperationNameAllocator(operations);\n\n\t\tconst config: TraversalConfig = {\n\t\t\tcaseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,\n\t\t\tdirectiveMode: options?.directiveMode ?? \"cap\",\n\t\t\tignore: options?.ignore,\n\t\t\tignoreMode: options?.ignoreMode ?? \"exclude\",\n\t\t\tintrospectionMode: options?.ignoreIntrospection ?? \"typename\",\n\t\t\tlimitIgnoredRecursion: options?.limitIgnoredRecursion ?? false,\n\t\t\tshortCircuit,\n\t\t\tuseDirective,\n\t\t};\n\n\t\tconst rootTypeMap = schema\n\t\t\t? {\n\t\t\t\t\tmutation: schema.getMutationType() ?? undefined,\n\t\t\t\t\tquery: schema.getQueryType() ?? undefined,\n\t\t\t\t\tsubscription: schema.getSubscriptionType() ?? undefined,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tmutation: undefined,\n\t\t\t\t\tquery: undefined,\n\t\t\t\t\tsubscription: undefined,\n\t\t\t\t};\n\n\t\tfor (const operation of operations) {\n\t\t\tconst operationName = nextOperationName(operation);\n\t\t\tconst rootType = rootTypeMap[operation.operation];\n\n\t\t\tlet result: ReturnType<typeof calculateDepth>;\n\t\t\ttry {\n\t\t\t\tresult = calculateDepth(caches, config, fragments, maxDepth, operation, rootType, schema);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof Error && error.name === \"IgnoreRuleError\") {\n\t\t\t\t\tcontext.reportError(\n\t\t\t\t\t\tnew GraphQLError(error.message, {\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.IGNORE_RULE_ERROR,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: [operation],\n\t\t\t\t\t\t\toriginalError: error,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tif (depths) {\n\t\t\t\tsetDepthResult(depths, operationName, result.depth);\n\t\t\t}\n\n\t\t\tif (result.violation) {\n\t\t\t\tconst depthValue = result.violation.depth;\n\t\t\t\tconst depthLabel = shortCircuit ? `at least ${depthValue}` : `${depthValue}`;\n\t\t\t\tconst violationPath = result.violation.path;\n\t\t\t\tconst pathSuffix = violationPath.length > 0 ? ` (at ${violationPath.join(\".\")})` : \"\";\n\n\t\t\t\tcontext.reportError(\n\t\t\t\t\tnew GraphQLError(\n\t\t\t\t\t\t`'${operationName}' has depth ${depthLabel} which exceeds maximum allowed depth of ${result.violation.maxDepth}${pathSuffix}`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\textensions: {\n\t\t\t\t\t\t\t\tcode: ERROR_CODES.QUERY_TOO_DEEP,\n\t\t\t\t\t\t\t\tdepth: depthValue,\n\t\t\t\t\t\t\t\tmaxDepth: result.violation.maxDepth,\n\t\t\t\t\t\t\t\tpath: violationPath,\n\t\t\t\t\t\t\t\tshortCircuit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnodes: result.violation.node ? [operation, result.violation.node] : [operation],\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (callback && depths) {\n\t\t\t// Keep callback payload as a plain object for compatibility.\n\t\t\tcallback({ ...depths });\n\t\t}\n\n\t\t// All depth validation is performed eagerly above because fragment\n\t\t// resolution requires the full document upfront. Nothing remains\n\t\t// for the visitor traversal phase.\n\t\treturn {};\n\t};\n}\n\n/**\n * Determines whether a value is a valid ignore rule.\n */\nfunction isIgnoreRule(rule: unknown): rule is IgnoreRule {\n\treturn typeof rule === \"function\" || rule instanceof RegExp || typeof rule === \"string\";\n}\n\n/**\n * Normalizes the optional depthLimit arguments.\n */\nfunction normalizeDepthLimitArgs(\n\toptions: DepthLimitOptions | DepthCallback | undefined,\n\tcallback: DepthCallback | undefined,\n): { callback?: DepthCallback; options?: NormalizedDepthLimitOptions } {\n\tif (callback !== undefined && typeof callback !== \"function\") {\n\t\tthrow new TypeError(\"Invalid callback: expected a function.\");\n\t}\n\n\tif (typeof options === \"function\") {\n\t\tif (callback) {\n\t\t\tthrow new TypeError(\"Invalid depthLimit arguments: callback provided twice.\");\n\t\t}\n\n\t\treturn { callback: options, options: undefined };\n\t}\n\n\tif (\n\t\toptions !== undefined &&\n\t\t(options === null || typeof options !== \"object\" || Array.isArray(options))\n\t) {\n\t\tconst receivedType = Array.isArray(options)\n\t\t\t? \"array\"\n\t\t\t: options === null\n\t\t\t\t? \"null\"\n\t\t\t\t: typeof options;\n\t\tthrow new TypeError(`Invalid options: expected an object, received ${receivedType}.`);\n\t}\n\n\treturn {\n\t\tcallback,\n\t\toptions: options ? normalizeDepthLimitOptions(options) : undefined,\n\t};\n}\n\n/**\n * Normalizes and validates depthLimit options.\n */\nfunction normalizeDepthLimitOptions(options: DepthLimitOptions): NormalizedDepthLimitOptions {\n\tassertBooleanOption(\"caseInsensitiveIgnore\", options.caseInsensitiveIgnore);\n\n\tif (options.directiveMode !== undefined && !DIRECTIVE_MODES.has(options.directiveMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid directiveMode: \"${options.directiveMode}\". Must be \"cap\" or \"override\".`,\n\t\t);\n\t}\n\n\tif (\n\t\toptions.ignoreIntrospection !== undefined &&\n\t\t!INTROSPECTION_MODES.has(options.ignoreIntrospection)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreIntrospection: \"${options.ignoreIntrospection}\". Must be \"all\", \"none\", or \"typename\".`,\n\t\t);\n\t}\n\n\tif (options.ignoreMode !== undefined && !IGNORE_MODES.has(options.ignoreMode)) {\n\t\tthrow new TypeError(\n\t\t\t`Invalid ignoreMode: \"${options.ignoreMode}\". Must be \"exclude\" or \"skip\".`,\n\t\t);\n\t}\n\n\tassertBooleanOption(\"limitIgnoredRecursion\", options.limitIgnoredRecursion);\n\tassertBooleanOption(\"shortCircuit\", options.shortCircuit);\n\tassertBooleanOption(\"useDirective\", options.useDirective);\n\n\tconst ignore = normalizeIgnoreRules(options.ignore);\n\treturn { ...options, ignore };\n}\n\n/**\n * Normalizes and validates ignore rules.\n */\nfunction normalizeIgnoreRules(ignore: DepthLimitOptions[\"ignore\"]): IgnoreRule[] | undefined {\n\tif (ignore == null) {\n\t\treturn undefined;\n\t}\n\n\tconst rules: unknown[] = Array.isArray(ignore) ? ignore : [ignore];\n\n\tfor (const [index, rule] of rules.entries()) {\n\t\tif (!isIgnoreRule(rule)) {\n\t\t\tconst receivedType = Array.isArray(rule) ? \"array\" : rule === null ? \"null\" : typeof rule;\n\t\t\tthrow new TypeError(\n\t\t\t\t`Invalid ignore rule at index ${index}: expected string, RegExp, or function, received ${receivedType}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn rules as IgnoreRule[];\n}\n\n/**\n * Creates a null-prototype record for internal depth result accumulation.\n */\nfunction createDepthResultRecord(): Record<string, number> {\n\treturn Object.create(null) as Record<string, number>;\n}\n\n/**\n * Creates a stable allocator for callback operation names.\n *\n * Ensures:\n * - Anonymous names never collide with explicit operation names\n * - Duplicate named operations receive deterministic suffixes\n * - Generated names do not overwrite previous callback entries\n */\nfunction createOperationNameAllocator(\n\toperations: readonly OperationDefinitionNode[],\n): (operation: OperationDefinitionNode) => string {\n\tconst explicitNames = new Set<string>();\n\tfor (const operation of operations) {\n\t\tif (operation.name?.value) {\n\t\t\texplicitNames.add(operation.name.value);\n\t\t}\n\t}\n\n\tconst usedNames = new Set<string>();\n\tconst namedCounts = new Map<string, number>();\n\tlet anonymousCount = 0;\n\n\treturn (operation: OperationDefinitionNode): string => {\n\t\tconst explicitName = operation.name?.value;\n\t\tif (explicitName) {\n\t\t\tlet suffix = namedCounts.get(explicitName) ?? 0;\n\t\t\tlet candidate = suffix === 0 ? explicitName : `${explicitName}_${suffix}`;\n\n\t\t\twhile (usedNames.has(candidate) || (suffix > 0 && explicitNames.has(candidate))) {\n\t\t\t\tsuffix++;\n\t\t\t\tcandidate = `${explicitName}_${suffix}`;\n\t\t\t}\n\n\t\t\tnamedCounts.set(explicitName, suffix + 1);\n\t\t\tusedNames.add(candidate);\n\t\t\treturn candidate;\n\t\t}\n\n\t\tlet suffix = anonymousCount;\n\t\tlet candidate = suffix === 0 ? \"anonymous\" : `anonymous_${suffix}`;\n\t\twhile (usedNames.has(candidate) || explicitNames.has(candidate)) {\n\t\t\tsuffix++;\n\t\t\tcandidate = `anonymous_${suffix}`;\n\t\t}\n\n\t\tanonymousCount = suffix + 1;\n\t\tusedNames.add(candidate);\n\t\treturn candidate;\n\t};\n}\n\n/**\n * Safely assigns a depth entry without allowing prototype pollution.\n */\nfunction setDepthResult(target: Record<string, number>, key: string, value: number): void {\n\tObject.defineProperty(target, key, {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\tvalue,\n\t\twritable: true,\n\t});\n}\n","import {\n\ttype ASTNode,\n\ttype DefinitionNode,\n\ttype FragmentDefinitionNode,\n\ttype GraphQLField,\n\ttype GraphQLInterfaceType,\n\ttype GraphQLObjectType,\n\ttype GraphQLOutputType,\n\ttype GraphQLSchema,\n\tgetNamedType,\n\tisCompositeType,\n\tisInterfaceType,\n\tisObjectType,\n\tKind,\n\ttype OperationDefinitionNode,\n\ttype SelectionNode,\n} from \"graphql\";\n\nimport { getDepthFromDirective } from \"./directives.js\";\nimport { shouldIgnoreField } from \"./ignore.js\";\nimport type { DirectiveMode, IgnoreMode, IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Result returned by the depth calculation engine.\n */\ninterface DepthResult {\n\t/** Maximum depth found across all branches */\n\tdepth: number;\n\t/** Deepest violation found, or `null` if within limits */\n\tviolation: DepthViolation | null;\n}\n\n/**\n * Records a depth limit violation with the offending depth and its limit.\n */\ninterface DepthViolation {\n\t/** The actual depth that exceeded the limit */\n\tdepth: number;\n\t/** The maximum allowed depth that was exceeded */\n\tmaxDepth: number;\n\t/** The AST node that caused the violation, for precise error locations */\n\tnode?: ASTNode;\n\t/** Field path from the operation root to the violation point */\n\tpath: string[];\n}\n\n/**\n * Result of resolving a `@depth` directive on a field definition.\n * @internal\n */\ninterface DirectiveResolution {\n\t/** Whether a directive limit is now active on this path */\n\thasDirectiveLimit: boolean;\n\t/** The resolved maximum depth for this branch */\n\tmaxDepth: number;\n}\n\n/**\n * Lightweight linked-list node for building field paths without per-field\n * array allocations. Only materialized into `string[]` when reporting a\n * violation or populating callback results.\n * @internal\n */\ninterface PathNode {\n\t/** Parent node in the path, or `undefined` for the root */\n\tparent: PathNode | undefined;\n\t/** Field name or alias for this path segment */\n\tsegment: string;\n}\n\n/**\n * Single unit of work on the iterative DFS stack.\n * @internal\n */\ninterface StackFrame {\n\t/** Current depth at this point in traversal */\n\tcurrentDepth: number;\n\t/** Whether a `@depth` directive has already constrained this path */\n\thasDirectiveLimit: boolean;\n\t/** Ignored field names seen on the current path (for recursion guard) */\n\tignoredFieldsOnPath: Set<string>;\n\t/** Maximum allowed depth for this branch */\n\tmaxDepth: number;\n\t/** The AST node whose selectionSet should be processed */\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } };\n\t/** Parent type for field resolution */\n\tparentType: GraphQLOutputType | undefined;\n\t/** Linked-list path from the operation root to this node */\n\tpath: PathNode | undefined;\n\t/** Fragment names visited on the current path (for cycle detection) */\n\tvisitedFragments: Set<string>;\n}\n\n/**\n * Caches shared across all stack frames during a single validation run\n * to avoid repeated interface graph walks and directive AST lookups.\n * @internal\n */\ninterface TraversalCaches {\n\t/** Cached raw directive depth per `typeName:fieldName` */\n\tdirectiveDepths: Map<string, number | undefined>;\n\t/** Cached interface lists per type name */\n\tinterfaces: Map<string, GraphQLInterfaceType[]>;\n}\n\n/**\n * Immutable configuration shared across all stack frames during traversal.\n * @internal\n */\ninterface TraversalConfig {\n\t/** Whether to use case-insensitive matching for string ignore rules */\n\tcaseInsensitiveIgnore: boolean;\n\t/** Controls how `@depth` directives interact with the global `maxDepth` */\n\tdirectiveMode: DirectiveMode;\n\t/** Rules for fields to ignore in depth calculation */\n\tignore: IgnoreRule[] | undefined;\n\t/** Controls how ignored fields affect depth traversal */\n\tignoreMode: IgnoreMode;\n\t/** Controls which introspection fields are ignored */\n\tintrospectionMode: IntrospectionMode;\n\t/** Whether repeated ignored fields on a path should increment depth */\n\tlimitIgnoredRecursion: boolean;\n\t/** Whether to bail immediately on violation (when no callback needs true depth) */\n\tshortCircuit: boolean;\n\t/** Whether to check for `@depth` directives on fields */\n\tuseDirective: boolean;\n}\n\n/**\n * Calculates the depth of a GraphQL query AST node using iterative DFS.\n *\n * Handles three selection types:\n * - **Fields**: Increment depth by 1 for composite (non-scalar) fields\n * - **Fragment spreads**: Expand the fragment in-place (no depth increment)\n * - **Inline fragments**: Process in-place (no depth increment)\n *\n * Fragment cycle detection uses per-path visited sets so that the same\n * fragment reused at different depths is calculated correctly.\n *\n * When `shortCircuit` is enabled (no callback), the engine bails immediately\n * on the first violation instead of traversing the full subtree.\n *\n * Uses an explicit stack instead of recursion to prevent stack overflow\n * on deeply nested queries.\n *\n * @param caches - Shared caches for interface and directive lookups\n * @param config - Immutable traversal configuration\n * @param fragments - Map of all fragment definitions in the document\n * @param maxDepth - Maximum allowed depth for this branch\n * @param node - The AST node to calculate depth for\n * @param parentType - Root type for field resolution\n * @param schema - GraphQL schema for type resolution\n * @returns The maximum depth and the deepest violation found\n */\nfunction calculateDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tfragments: Map<string, FragmentDefinitionNode>,\n\tmaxDepth: number,\n\tnode: ASTNode & { selectionSet?: { selections: readonly SelectionNode[] } },\n\tparentType: GraphQLOutputType | undefined,\n\tschema: GraphQLSchema | undefined,\n): DepthResult {\n\tlet deepestViolation: DepthViolation | null = null;\n\tlet globalMaxDepth = 0;\n\n\tif (!node.selectionSet) {\n\t\treturn { depth: 0, violation: null };\n\t}\n\n\tconst stack: StackFrame[] = [\n\t\t{\n\t\t\tcurrentDepth: 0,\n\t\t\thasDirectiveLimit: false,\n\t\t\tignoredFieldsOnPath: new Set<string>(),\n\t\t\tmaxDepth,\n\t\t\tnode,\n\t\t\tparentType,\n\t\t\tpath: undefined,\n\t\t\tvisitedFragments: new Set<string>(),\n\t\t},\n\t];\n\n\tfor (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n\t\tif (!frame.node.selectionSet) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst nextFrames: StackFrame[] = [];\n\n\t\tfor (const selection of frame.node.selectionSet.selections) {\n\t\t\tswitch (selection.kind) {\n\t\t\t\tcase Kind.FIELD: {\n\t\t\t\t\tconst fieldName = selection.name.value;\n\t\t\t\t\tconst isIntrospectionField = fieldName.startsWith(\"__\");\n\n\t\t\t\t\t// When introspection is fully ignored, always skip the subtree\n\t\t\t\t\t// regardless of ignoreMode.\n\t\t\t\t\tif (config.introspectionMode === \"all\" && isIntrospectionField) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Leaf fields (no selectionSet) never contribute to depth,\n\t\t\t\t\t// so skip them before running ignore rules to avoid unnecessary\n\t\t\t\t\t// predicate evaluation and potential errors on irrelevant fields.\n\t\t\t\t\tif (!selection.selectionSet) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isIgnored = shouldIgnoreField(\n\t\t\t\t\t\tfieldName,\n\t\t\t\t\t\tconfig.ignore,\n\t\t\t\t\t\tconfig.caseInsensitiveIgnore,\n\t\t\t\t\t\tconfig.introspectionMode,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (isIgnored && config.ignoreMode === \"skip\") {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Resolve field type and directive depth\n\t\t\t\t\tlet fieldMaxDepth = frame.maxDepth;\n\t\t\t\t\tlet fieldType: GraphQLOutputType | undefined;\n\t\t\t\t\tlet hasDirectiveLimit = frame.hasDirectiveLimit;\n\n\t\t\t\t\tif (schema && frame.parentType) {\n\t\t\t\t\t\tconst namedType = getNamedType(frame.parentType);\n\t\t\t\t\t\tif (isObjectType(namedType) || isInterfaceType(namedType)) {\n\t\t\t\t\t\t\tconst fieldDef = namedType.getFields()[fieldName];\n\t\t\t\t\t\t\tif (fieldDef) {\n\t\t\t\t\t\t\t\tfieldType = fieldDef.type;\n\n\t\t\t\t\t\t\t\t// Defensively skip non-composite fields that erroneously\n\t\t\t\t\t\t\t\t// have selections (normally caught by GraphQL's own\n\t\t\t\t\t\t\t\t// validation rules, but guards against miscounted depth\n\t\t\t\t\t\t\t\t// when this rule runs standalone or before other rules).\n\t\t\t\t\t\t\t\tconst resolvedType = getNamedType(fieldType);\n\t\t\t\t\t\t\t\tif (resolvedType && !isCompositeType(resolvedType)) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (config.useDirective) {\n\t\t\t\t\t\t\t\t\tconst resolved = resolveFieldDirectiveDepth(\n\t\t\t\t\t\t\t\t\t\tcaches,\n\t\t\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\t\t\tframe.currentDepth,\n\t\t\t\t\t\t\t\t\t\tframe.maxDepth,\n\t\t\t\t\t\t\t\t\t\tfieldDef,\n\t\t\t\t\t\t\t\t\t\tframe.hasDirectiveLimit,\n\t\t\t\t\t\t\t\t\t\tnamedType,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tfieldMaxDepth = resolved.maxDepth;\n\t\t\t\t\t\t\t\t\thasDirectiveLimit = resolved.hasDirectiveLimit;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Determine whether this ignored field should still increment\n\t\t\t\t\t// depth due to the recursion guard detecting repeated ignores\n\t\t\t\t\t// on the same path. The key is type-aware (`Type:field`) when\n\t\t\t\t\t// a schema is present so identically named fields on unrelated\n\t\t\t\t\t// types are tracked independently. Without a schema, the key\n\t\t\t\t\t// uses the field name alone, which may cause conservative\n\t\t\t\t\t// over-counting on paths with same-named fields on different types.\n\t\t\t\t\tlet ignoredFieldsOnPath = frame.ignoredFieldsOnPath;\n\t\t\t\t\tlet effectivelyIgnored = isIgnored;\n\n\t\t\t\t\tif (isIgnored && config.limitIgnoredRecursion) {\n\t\t\t\t\t\tconst parentName = frame.parentType ? getNamedType(frame.parentType)?.name : undefined;\n\t\t\t\t\t\tconst recursionKey = parentName ? `${parentName}:${fieldName}` : fieldName;\n\n\t\t\t\t\t\tif (ignoredFieldsOnPath.has(recursionKey)) {\n\t\t\t\t\t\t\t// Same type:field was already ignored on this path — increment depth\n\t\t\t\t\t\t\teffectivelyIgnored = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First occurrence — track it for subsequent path segments\n\t\t\t\t\t\t\tignoredFieldsOnPath = new Set(ignoredFieldsOnPath);\n\t\t\t\t\t\t\tignoredFieldsOnPath.add(recursionKey);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newDepth = effectivelyIgnored ? frame.currentDepth : frame.currentDepth + 1;\n\n\t\t\t\t\t// Use alias for path (matches the response shape clients see),\n\t\t\t\t\t// while fieldName is used for schema lookups and ignore rules.\n\t\t\t\t\tconst pathSegment = selection.alias?.value ?? fieldName;\n\t\t\t\t\tconst fieldPath = pathPush(frame.path, pathSegment);\n\n\t\t\t\t\t// Track maximum depth found\n\t\t\t\t\tif (newDepth > globalMaxDepth) {\n\t\t\t\t\t\tglobalMaxDepth = newDepth;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for violation\n\t\t\t\t\tif (newDepth > fieldMaxDepth) {\n\t\t\t\t\t\tconst violation: DepthViolation = {\n\t\t\t\t\t\t\tdepth: newDepth,\n\t\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\t\tpath: pathToArray(fieldPath),\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (config.shortCircuit) {\n\t\t\t\t\t\t\treturn { depth: newDepth, violation };\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!deepestViolation || violation.depth > deepestViolation.depth) {\n\t\t\t\t\t\t\tdeepestViolation = violation;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Queue children and push in reverse after this selection set\n\t\t\t\t\t// so DFS traversal follows query order deterministically.\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: newDepth,\n\t\t\t\t\t\thasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: fieldMaxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType: fieldType,\n\t\t\t\t\t\tpath: fieldPath,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.FRAGMENT_SPREAD: {\n\t\t\t\t\tconst fragmentName = selection.name.value;\n\n\t\t\t\t\t// Check membership before copying to avoid wasted allocations\n\t\t\t\t\t// when the fragment was already visited on this path.\n\t\t\t\t\tif (frame.visitedFragments.has(fragmentName)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst fragment = fragments.get(fragmentName);\n\t\t\t\t\tif (!fragment) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create independent copy for per-path cycle detection\n\t\t\t\t\tconst fragmentVisited = new Set(frame.visitedFragments);\n\t\t\t\t\tfragmentVisited.add(fragmentName);\n\n\t\t\t\t\tconst parentType = fragment.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: fragment,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: fragmentVisited,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase Kind.INLINE_FRAGMENT: {\n\t\t\t\t\tconst parentType = selection.typeCondition\n\t\t\t\t\t\t? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType)\n\t\t\t\t\t\t: frame.parentType;\n\n\t\t\t\t\tnextFrames.push({\n\t\t\t\t\t\tcurrentDepth: frame.currentDepth,\n\t\t\t\t\t\thasDirectiveLimit: frame.hasDirectiveLimit,\n\t\t\t\t\t\tignoredFieldsOnPath: frame.ignoredFieldsOnPath,\n\t\t\t\t\t\tmaxDepth: frame.maxDepth,\n\t\t\t\t\t\tnode: selection,\n\t\t\t\t\t\tparentType,\n\t\t\t\t\t\tpath: frame.path,\n\t\t\t\t\t\tvisitedFragments: frame.visitedFragments,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault: {\n\t\t\t\t\tconst exhaustiveCheck: never = selection;\n\t\t\t\t\tthrowUnhandledSelectionKind(exhaustiveCheck);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (let index = nextFrames.length - 1; index >= 0; index--) {\n\t\t\tconst nextFrame = nextFrames[index];\n\t\t\tif (nextFrame) {\n\t\t\t\tstack.push(nextFrame);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { depth: globalMaxDepth, violation: deepestViolation };\n}\n\n/**\n * Throws for impossible selection kinds. This should only run when a\n * malformed or manually constructed AST bypasses GraphQL parser guarantees.\n */\nfunction throwUnhandledSelectionKind(selection: SelectionNode): never {\n\tthrow new Error(`Unhandled selection kind: ${selection.kind}`);\n}\n\n/**\n * Collects all interfaces implemented by a type, including transitively\n * inherited interfaces (interface-implements-interface chains).\n *\n * @param type - The object or interface type to collect interfaces from\n * @returns All directly and transitively implemented interfaces\n */\nfunction collectInterfaces(type: GraphQLInterfaceType | GraphQLObjectType): GraphQLInterfaceType[] {\n\tconst interfaces: GraphQLInterfaceType[] = [];\n\tconst seen = new Set<string>();\n\tconst stack = [...type.getInterfaces()];\n\n\tfor (let iface = stack.pop(); iface !== undefined; iface = stack.pop()) {\n\t\tif (seen.has(iface.name)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tseen.add(iface.name);\n\t\tinterfaces.push(iface);\n\n\t\tfor (const parent of iface.getInterfaces()) {\n\t\t\tif (!seen.has(parent.name)) {\n\t\t\t\tstack.push(parent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn interfaces;\n}\n\n/**\n * Creates empty traversal caches for a new validation run.\n *\n * @returns Fresh caches for interface and directive lookups\n */\nfunction createTraversalCaches(): TraversalCaches {\n\treturn {\n\t\tdirectiveDepths: new Map<string, number | undefined>(),\n\t\tinterfaces: new Map<string, GraphQLInterfaceType[]>(),\n\t};\n}\n\n/**\n * Extracts all fragment and operation definitions from a GraphQL document\n * in a single pass.\n *\n * **By design:** Duplicate fragment names are silently overwritten (last wins)\n * rather than raising a validation error. This is intentional — detecting\n * duplicates is the responsibility of GraphQL's built-in `UniqueFragmentNamesRule`,\n * not a depth-limiting rule. When both rules run together (the normal case),\n * duplicates are already caught before this code executes. When used standalone,\n * last-wins is a safe, deterministic fallback that avoids coupling depth\n * validation to fragment uniqueness concerns.\n *\n * @param definitions - Array of definition nodes from the parsed document\n * @returns Fragment map and operation array\n */\nfunction extractDefinitions(definitions: readonly DefinitionNode[]): {\n\tfragments: Map<string, FragmentDefinitionNode>;\n\toperations: OperationDefinitionNode[];\n} {\n\tconst fragments = new Map<string, FragmentDefinitionNode>();\n\tconst operations: OperationDefinitionNode[] = [];\n\n\tfor (const definition of definitions) {\n\t\tif (definition.kind === Kind.FRAGMENT_DEFINITION) {\n\t\t\tfragments.set(definition.name.value, definition);\n\t\t} else if (definition.kind === Kind.OPERATION_DEFINITION) {\n\t\t\toperations.push(definition);\n\t\t}\n\t}\n\n\treturn { fragments, operations };\n}\n\n/**\n * Returns cached interfaces for a type, computing and caching on first access.\n *\n * @param caches - Traversal caches\n * @param type - The object or interface type to get interfaces for\n * @returns All directly and transitively implemented interfaces\n */\nfunction getCachedInterfaces(\n\tcaches: TraversalCaches,\n\ttype: GraphQLInterfaceType | GraphQLObjectType,\n): GraphQLInterfaceType[] {\n\tconst cached = caches.interfaces.get(type.name);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\tconst result = collectInterfaces(type);\n\tcaches.interfaces.set(type.name, result);\n\treturn result;\n}\n\n/**\n * Creates a new path node by appending a segment to the parent path.\n *\n * @param parent - Parent path node, or `undefined` for the root\n * @param segment - Field name or alias for this path segment\n * @returns New path node linked to the parent\n */\nfunction pathPush(parent: PathNode | undefined, segment: string): PathNode {\n\treturn { parent, segment };\n}\n\n/**\n * Materializes a linked-list path into a string array.\n *\n * @param node - Leaf path node to materialize from\n * @returns Array of path segments from root to leaf\n */\nfunction pathToArray(node: PathNode | undefined): string[] {\n\tconst result: string[] = [];\n\tlet current = node;\n\twhile (current) {\n\t\tresult.push(current.segment);\n\t\tcurrent = current.parent;\n\t}\n\tresult.reverse();\n\treturn result;\n}\n\n/**\n * Resolves a `@depth` directive on a field definition, falling back to\n * interface field directives when the concrete field has none.\n *\n * **Precedence:** The concrete field's directive takes priority. Interface\n * directives are only consulted when the concrete field has no `@depth`.\n * When multiple interfaces define `@depth` on the same field, the strictest\n * (minimum) value wins.\n *\n * Results are memoized per `typeName:fieldName` in the traversal caches\n * to avoid repeated interface graph walks on large schemas.\n *\n * @param caches - Traversal caches for memoizing lookups\n * @param config - Traversal configuration\n * @param currentDepth - Current depth in the query tree\n * @param currentMaxDepth - Current maximum depth for this branch\n * @param fieldDef - The field definition to inspect\n * @param hasDirectiveLimit - Whether a directive has already constrained this path\n * @param namedType - The named parent type owning this field (already unwrapped)\n * @returns Resolved maximum depth and directive limit state\n */\nfunction resolveFieldDirectiveDepth(\n\tcaches: TraversalCaches,\n\tconfig: TraversalConfig,\n\tcurrentDepth: number,\n\tcurrentMaxDepth: number,\n\tfieldDef: GraphQLField<unknown, unknown>,\n\thasDirectiveLimit: boolean,\n\tnamedType: GraphQLInterfaceType | GraphQLObjectType,\n): DirectiveResolution {\n\tconst cacheKey = `${namedType.name}:${fieldDef.name}`;\n\n\tlet directiveDepth: number | undefined;\n\n\tif (caches.directiveDepths.has(cacheKey)) {\n\t\tdirectiveDepth = caches.directiveDepths.get(cacheKey);\n\t} else {\n\t\tdirectiveDepth = getDepthFromDirective(fieldDef);\n\n\t\tif (directiveDepth === undefined) {\n\t\t\tconst interfaces = getCachedInterfaces(caches, namedType);\n\t\t\tfor (const iface of interfaces) {\n\t\t\t\tconst ifaceField = iface.getFields()[fieldDef.name];\n\t\t\t\tconst ifaceDepth = getDepthFromDirective(ifaceField);\n\t\t\t\tif (ifaceDepth !== undefined) {\n\t\t\t\t\tdirectiveDepth =\n\t\t\t\t\t\tdirectiveDepth === undefined ? ifaceDepth : Math.min(directiveDepth, ifaceDepth);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcaches.directiveDepths.set(cacheKey, directiveDepth);\n\t}\n\n\tif (directiveDepth !== undefined) {\n\t\tconst relativeMax = currentDepth + directiveDepth;\n\t\tconst maxDepth =\n\t\t\tconfig.directiveMode === \"cap\"\n\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t: hasDirectiveLimit\n\t\t\t\t\t? Math.min(currentMaxDepth, relativeMax)\n\t\t\t\t\t: relativeMax;\n\t\treturn { hasDirectiveLimit: true, maxDepth };\n\t}\n\n\treturn { hasDirectiveLimit, maxDepth: currentMaxDepth };\n}\n\n/**\n * Resolves the parent type from a type condition on a fragment or inline fragment.\n *\n * Falls back to `currentParentType` when the schema is unavailable or the\n * type condition resolves to a non-composite type (which GraphQL's own\n * validation would reject, but is handled defensively here).\n *\n * @param typeConditionName - The name of the type condition\n * @param schema - GraphQL schema for type lookup\n * @param currentParentType - Fallback parent type if resolution fails\n * @returns The resolved composite type or the current parent type\n */\nfunction resolveTypeCondition(\n\ttypeConditionName: string,\n\tschema: GraphQLSchema | undefined,\n\tcurrentParentType: GraphQLOutputType | undefined,\n): GraphQLOutputType | undefined {\n\tif (schema) {\n\t\tconst type = schema.getType(typeConditionName);\n\t\tif (type && isCompositeType(type)) {\n\t\t\treturn type;\n\t\t}\n\t}\n\treturn currentParentType;\n}\n\nexport { calculateDepth, createTraversalCaches, extractDefinitions };\nexport type { DepthResult, TraversalCaches, TraversalConfig };\n","import { type GraphQLField, Kind } from \"graphql\";\n\n/**\n * GraphQL SDL type definition for the `@depth` directive.\n *\n * Include this in your schema definition to enable per-field depth\n * overrides when using `depthLimit` with `{ useDirective: true }`.\n *\n * @example\n * ```ts\n * import { makeExecutableSchema } from \"@graphql-tools/schema\";\n * import { depthDirectiveTypeDefs } from \"graphql-query-depth-limit-esm\";\n *\n * const schema = makeExecutableSchema({\n * typeDefs: [depthDirectiveTypeDefs, yourTypeDefs],\n * resolvers,\n * });\n * ```\n */\nexport const depthDirectiveTypeDefs = `\n directive @depth(max: Int!) on FIELD_DEFINITION\n`;\n\n/**\n * Extracts the maximum depth from a `@depth(max: Int!)` directive on a field definition.\n *\n * Only integer literals are supported. Variables (e.g., `@depth(max: $var)`) are not\n * supported because variable values are unavailable during the validation phase.\n * Fields with variable-based directives fall back to the global depth limit.\n *\n * @param field - The GraphQL field definition to inspect\n * @returns The maximum depth from the directive, or `undefined` if not found or invalid\n *\n * @example\n * ```graphql\n * type Query {\n * users: [User!]! @depth(max: 3)\n * }\n * ```\n */\nexport function getDepthFromDirective(\n\tfield: GraphQLField<unknown, unknown> | undefined,\n): number | undefined {\n\tif (!field?.astNode?.directives) {\n\t\treturn undefined;\n\t}\n\n\tconst depthDirective = field.astNode.directives.find((d) => d.name.value === \"depth\");\n\n\tif (!depthDirective?.arguments) {\n\t\treturn undefined;\n\t}\n\n\tconst maxArg = depthDirective.arguments.find((arg) => arg.name.value === \"max\");\n\n\tif (maxArg?.value.kind === Kind.INT) {\n\t\tconst directiveDepth = Number.parseInt(maxArg.value.value, 10);\n\t\tif (Number.isFinite(directiveDepth) && directiveDepth >= 0) {\n\t\t\treturn directiveDepth;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import type { IgnoreRule, IntrospectionMode } from \"./types.js\";\n\n/**\n * Determines whether a field should be ignored during depth calculation.\n *\n * Introspection field handling is controlled by the `introspectionMode` parameter:\n * - `\"all\"` — ignore every `__`-prefixed field\n * - `\"typename\"` (default) — only ignore `__typename`\n * - `\"none\"` — count all introspection fields toward depth\n *\n * **Warning:** When `ignoreMode: \"skip\"` is set, an ignored field's **entire\n * subtree** is skipped — not just the depth increment. Ignoring a composite field\n * bypasses all depth protection for everything nested under it. Use\n * `ignoreMode: \"exclude\"` (default) to skip only the depth increment while still\n * traversing children.\n *\n * @param fieldName - The name of the field to check\n * @param ignore - Array of ignore rules (strings, RegExp, or functions)\n * @param caseInsensitive - Whether to use case-insensitive matching for string rules\n * @param introspectionMode - How to handle introspection fields\n * @returns `true` if the field should be skipped, `false` otherwise\n *\n * @example\n * ```typescript\n * shouldIgnoreField(\"__typename\", []); // true (introspection, default mode)\n * shouldIgnoreField(\"__schema\", [], false, \"typename\"); // false (only __typename ignored)\n * shouldIgnoreField(\"__schema\", [], false, \"all\"); // true (all __ fields ignored)\n * shouldIgnoreField(\"metadata\", [\"metadata\"]); // true (exact match)\n * shouldIgnoreField(\"Metadata\", [\"metadata\"], true); // true (case-insensitive)\n * shouldIgnoreField(\"posts\", [/^internal/]); // false (no match)\n * ```\n */\nexport function shouldIgnoreField(\n\tfieldName: string,\n\tignore?: IgnoreRule[],\n\tcaseInsensitive = false,\n\tintrospectionMode: IntrospectionMode = \"typename\",\n): boolean {\n\tif (introspectionMode === \"all\" && fieldName.startsWith(\"__\")) {\n\t\treturn true;\n\t}\n\n\tif (introspectionMode === \"typename\" && fieldName === \"__typename\") {\n\t\treturn true;\n\t}\n\n\tif (!ignore || ignore.length === 0) {\n\t\treturn false;\n\t}\n\n\t// Precompute lowercased field name once for all string rule comparisons\n\tconst normalizedFieldName = caseInsensitive ? fieldName.toLowerCase() : fieldName;\n\n\tfor (const rule of ignore) {\n\t\tif (typeof rule === \"string\") {\n\t\t\tconst normalizedRule = caseInsensitive ? rule.toLowerCase() : rule;\n\t\t\tif (normalizedRule === normalizedFieldName) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (rule instanceof RegExp) {\n\t\t\ttry {\n\t\t\t\t// Reset lastIndex for stateful regexes (/g, /y flags) to ensure\n\t\t\t\t// consistent results. This mutates the RegExp object, which is\n\t\t\t\t// intentional. Without the reset, repeated calls with the same\n\t\t\t\t// global or sticky regex produce inconsistent results.\n\t\t\t\tif (rule.global || rule.sticky) {\n\t\t\t\t\trule.lastIndex = 0;\n\t\t\t\t}\n\t\t\t\tif (rule.test(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Frozen or exotic RegExp objects throw on lastIndex mutation or\n\t\t\t\t// test(). Wrap as IgnoreRuleError so the caller can handle it\n\t\t\t\t// consistently with function rule errors.\n\t\t\t\tconst message = `Ignore rule RegExp threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tif (rule(fieldName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = `Ignore rule function threw for field \"${fieldName}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`;\n\t\t\t\tconst wrapped = new Error(message, { cause: error });\n\t\t\t\twrapped.name = \"IgnoreRuleError\";\n\t\t\t\tthrow wrapped;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"],"mappings":"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","names":["Kind","Kind","parentType","suffix","candidate"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-query-depth-limit-esm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "GraphQL query depth limiting validation rule with directive support, ignore rules, and fragment cycle detection",
|
|
5
5
|
"author": "Lafitte Mehdy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"import": "./dist/index.js",
|
|
12
|
-
"require": "./dist/index.cjs"
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"default": "./dist/index.js"
|
|
13
14
|
}
|
|
14
15
|
},
|
|
15
16
|
"main": "./dist/index.cjs",
|
|
@@ -49,8 +50,8 @@
|
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@apollo/server": "^5.4.0",
|
|
52
|
-
"@biomejs/biome": "^2.4.
|
|
53
|
-
"@types/node": "^25.
|
|
53
|
+
"@biomejs/biome": "^2.4.4",
|
|
54
|
+
"@types/node": "^25.3.0",
|
|
54
55
|
"@vitest/coverage-v8": "^4.0.18",
|
|
55
56
|
"@vitest/ui": "^4.0.18",
|
|
56
57
|
"graphql": "^16.12.0",
|