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 CHANGED
@@ -7,6 +7,7 @@
7
7
  [![npm downloads](https://img.shields.io/npm/dm/graphql-query-depth-limit-esm?logo=npm)](https://www.npmjs.com/package/graphql-query-depth-limit-esm)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
9
  [![Node >=20](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
10
+ [![Provenance](https://img.shields.io/badge/provenance-verified-brightgreen?logo=npm)](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, [depthLimit(7, { useDirective: true })]);
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
- stack.push({
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
- stack.push({
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
- stack.push({
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
- throw new Error(`Unhandled selection kind: ${exhaustiveCheck.kind}`);
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 ? {} : void 0;
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 namedOperationNames = /* @__PURE__ */ new Set();
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
- let operationName;
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,
@@ -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 depth results for each operation
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 depth results for each operation
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
- stack.push({
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
- stack.push({
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
- stack.push({
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
- throw new Error(`Unhandled selection kind: ${exhaustiveCheck.kind}`);
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 ? {} : void 0;
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 namedOperationNames = /* @__PURE__ */ new Set();
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
- let operationName;
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.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.0",
53
- "@types/node": "^25.2.3",
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",