graphql-query-depth-limit-esm 0.1.0 → 2.0.0
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/LICENSE +1 -1
- package/README.md +369 -348
- package/dist/index.cjs +557 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +273 -0
- package/dist/index.d.ts +269 -7
- package/dist/index.js +536 -9
- package/dist/index.js.map +1 -0
- package/package.json +74 -65
- package/dist/depthLimit.d.ts +0 -41
- package/dist/depthLimit.d.ts.map +0 -1
- package/dist/depthLimit.js +0 -273
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts +0 -25
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ERROR_CODES: () => ERROR_CODES,
|
|
24
|
+
depthDirectiveTypeDefs: () => depthDirectiveTypeDefs,
|
|
25
|
+
depthLimit: () => depthLimit
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/constants.ts
|
|
30
|
+
var ERROR_CODES = {
|
|
31
|
+
IGNORE_RULE_ERROR: "IGNORE_RULE_ERROR",
|
|
32
|
+
QUERY_TOO_DEEP: "QUERY_TOO_DEEP"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/depth-limit.ts
|
|
36
|
+
var import_graphql3 = require("graphql");
|
|
37
|
+
|
|
38
|
+
// src/depth-engine.ts
|
|
39
|
+
var import_graphql2 = require("graphql");
|
|
40
|
+
|
|
41
|
+
// src/directives.ts
|
|
42
|
+
var import_graphql = require("graphql");
|
|
43
|
+
var depthDirectiveTypeDefs = `
|
|
44
|
+
directive @depth(max: Int!) on FIELD_DEFINITION
|
|
45
|
+
`;
|
|
46
|
+
function getDepthFromDirective(field) {
|
|
47
|
+
if (!field?.astNode?.directives) {
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
const depthDirective = field.astNode.directives.find((d) => d.name.value === "depth");
|
|
51
|
+
if (!depthDirective?.arguments) {
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
const maxArg = depthDirective.arguments.find((arg) => arg.name.value === "max");
|
|
55
|
+
if (maxArg?.value.kind === import_graphql.Kind.INT) {
|
|
56
|
+
const directiveDepth = Number.parseInt(maxArg.value.value, 10);
|
|
57
|
+
if (Number.isFinite(directiveDepth) && directiveDepth >= 0) {
|
|
58
|
+
return directiveDepth;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/ignore.ts
|
|
65
|
+
function shouldIgnoreField(fieldName, ignore, caseInsensitive = false, introspectionMode = "typename") {
|
|
66
|
+
if (introspectionMode === "all" && fieldName.startsWith("__")) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (introspectionMode === "typename" && fieldName === "__typename") {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (!ignore || ignore.length === 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const normalizedFieldName = caseInsensitive ? fieldName.toLowerCase() : fieldName;
|
|
76
|
+
for (const rule of ignore) {
|
|
77
|
+
if (typeof rule === "string") {
|
|
78
|
+
const normalizedRule = caseInsensitive ? rule.toLowerCase() : rule;
|
|
79
|
+
if (normalizedRule === normalizedFieldName) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
} else if (rule instanceof RegExp) {
|
|
83
|
+
try {
|
|
84
|
+
if (rule.global || rule.sticky) {
|
|
85
|
+
rule.lastIndex = 0;
|
|
86
|
+
}
|
|
87
|
+
if (rule.test(fieldName)) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = `Ignore rule RegExp threw for field "${fieldName}": ${error instanceof Error ? error.message : String(error)}`;
|
|
92
|
+
const wrapped = new Error(message, { cause: error });
|
|
93
|
+
wrapped.name = "IgnoreRuleError";
|
|
94
|
+
throw wrapped;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
try {
|
|
98
|
+
if (rule(fieldName)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const message = `Ignore rule function threw for field "${fieldName}": ${error instanceof Error ? error.message : String(error)}`;
|
|
103
|
+
const wrapped = new Error(message, { cause: error });
|
|
104
|
+
wrapped.name = "IgnoreRuleError";
|
|
105
|
+
throw wrapped;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/depth-engine.ts
|
|
113
|
+
function calculateDepth(caches, config, fragments, maxDepth, node, parentType, schema) {
|
|
114
|
+
let deepestViolation = null;
|
|
115
|
+
let globalMaxDepth = 0;
|
|
116
|
+
if (!node.selectionSet) {
|
|
117
|
+
return { depth: 0, violation: null };
|
|
118
|
+
}
|
|
119
|
+
const stack = [
|
|
120
|
+
{
|
|
121
|
+
currentDepth: 0,
|
|
122
|
+
hasDirectiveLimit: false,
|
|
123
|
+
ignoredFieldsOnPath: /* @__PURE__ */ new Set(),
|
|
124
|
+
maxDepth,
|
|
125
|
+
node,
|
|
126
|
+
parentType,
|
|
127
|
+
path: void 0,
|
|
128
|
+
visitedFragments: /* @__PURE__ */ new Set()
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
for (let frame = stack.pop(); frame !== void 0; frame = stack.pop()) {
|
|
132
|
+
if (!frame.node.selectionSet) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
for (const selection of frame.node.selectionSet.selections) {
|
|
136
|
+
switch (selection.kind) {
|
|
137
|
+
case import_graphql2.Kind.FIELD: {
|
|
138
|
+
const fieldName = selection.name.value;
|
|
139
|
+
const isIntrospectionField = fieldName.startsWith("__");
|
|
140
|
+
if (config.introspectionMode === "all" && isIntrospectionField) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!selection.selectionSet) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const isIgnored = shouldIgnoreField(
|
|
147
|
+
fieldName,
|
|
148
|
+
config.ignore,
|
|
149
|
+
config.caseInsensitiveIgnore,
|
|
150
|
+
config.introspectionMode
|
|
151
|
+
);
|
|
152
|
+
if (isIgnored && config.ignoreMode === "skip") {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let fieldMaxDepth = frame.maxDepth;
|
|
156
|
+
let fieldType;
|
|
157
|
+
let hasDirectiveLimit = frame.hasDirectiveLimit;
|
|
158
|
+
if (schema && frame.parentType) {
|
|
159
|
+
const namedType = (0, import_graphql2.getNamedType)(frame.parentType);
|
|
160
|
+
if ((0, import_graphql2.isObjectType)(namedType) || (0, import_graphql2.isInterfaceType)(namedType)) {
|
|
161
|
+
const fieldDef = namedType.getFields()[fieldName];
|
|
162
|
+
if (fieldDef) {
|
|
163
|
+
fieldType = fieldDef.type;
|
|
164
|
+
const resolvedType = (0, import_graphql2.getNamedType)(fieldType);
|
|
165
|
+
if (resolvedType && !(0, import_graphql2.isCompositeType)(resolvedType)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (config.useDirective) {
|
|
169
|
+
const resolved = resolveFieldDirectiveDepth(
|
|
170
|
+
caches,
|
|
171
|
+
config,
|
|
172
|
+
frame.currentDepth,
|
|
173
|
+
frame.maxDepth,
|
|
174
|
+
fieldDef,
|
|
175
|
+
frame.hasDirectiveLimit,
|
|
176
|
+
namedType
|
|
177
|
+
);
|
|
178
|
+
fieldMaxDepth = resolved.maxDepth;
|
|
179
|
+
hasDirectiveLimit = resolved.hasDirectiveLimit;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
let ignoredFieldsOnPath = frame.ignoredFieldsOnPath;
|
|
185
|
+
let effectivelyIgnored = isIgnored;
|
|
186
|
+
if (isIgnored && config.limitIgnoredRecursion) {
|
|
187
|
+
const parentName = frame.parentType ? (0, import_graphql2.getNamedType)(frame.parentType)?.name : void 0;
|
|
188
|
+
const recursionKey = parentName ? `${parentName}:${fieldName}` : fieldName;
|
|
189
|
+
if (ignoredFieldsOnPath.has(recursionKey)) {
|
|
190
|
+
effectivelyIgnored = false;
|
|
191
|
+
} else {
|
|
192
|
+
ignoredFieldsOnPath = new Set(ignoredFieldsOnPath);
|
|
193
|
+
ignoredFieldsOnPath.add(recursionKey);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const newDepth = effectivelyIgnored ? frame.currentDepth : frame.currentDepth + 1;
|
|
197
|
+
const pathSegment = selection.alias?.value ?? fieldName;
|
|
198
|
+
const fieldPath = pathPush(frame.path, pathSegment);
|
|
199
|
+
if (newDepth > globalMaxDepth) {
|
|
200
|
+
globalMaxDepth = newDepth;
|
|
201
|
+
}
|
|
202
|
+
if (newDepth > fieldMaxDepth) {
|
|
203
|
+
const violation = {
|
|
204
|
+
depth: newDepth,
|
|
205
|
+
maxDepth: fieldMaxDepth,
|
|
206
|
+
node: selection,
|
|
207
|
+
path: pathToArray(fieldPath)
|
|
208
|
+
};
|
|
209
|
+
if (config.shortCircuit) {
|
|
210
|
+
return { depth: newDepth, violation };
|
|
211
|
+
}
|
|
212
|
+
if (!deepestViolation || violation.depth > deepestViolation.depth) {
|
|
213
|
+
deepestViolation = violation;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
stack.push({
|
|
217
|
+
currentDepth: newDepth,
|
|
218
|
+
hasDirectiveLimit,
|
|
219
|
+
ignoredFieldsOnPath,
|
|
220
|
+
maxDepth: fieldMaxDepth,
|
|
221
|
+
node: selection,
|
|
222
|
+
parentType: fieldType,
|
|
223
|
+
path: fieldPath,
|
|
224
|
+
visitedFragments: frame.visitedFragments
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case import_graphql2.Kind.FRAGMENT_SPREAD: {
|
|
229
|
+
const fragmentName = selection.name.value;
|
|
230
|
+
if (frame.visitedFragments.has(fragmentName)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const fragment = fragments.get(fragmentName);
|
|
234
|
+
if (!fragment) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const fragmentVisited = new Set(frame.visitedFragments);
|
|
238
|
+
fragmentVisited.add(fragmentName);
|
|
239
|
+
const parentType2 = fragment.typeCondition ? resolveTypeCondition(fragment.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
240
|
+
stack.push({
|
|
241
|
+
currentDepth: frame.currentDepth,
|
|
242
|
+
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
243
|
+
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
244
|
+
maxDepth: frame.maxDepth,
|
|
245
|
+
node: fragment,
|
|
246
|
+
parentType: parentType2,
|
|
247
|
+
path: frame.path,
|
|
248
|
+
visitedFragments: fragmentVisited
|
|
249
|
+
});
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case import_graphql2.Kind.INLINE_FRAGMENT: {
|
|
253
|
+
const parentType2 = selection.typeCondition ? resolveTypeCondition(selection.typeCondition.name.value, schema, frame.parentType) : frame.parentType;
|
|
254
|
+
stack.push({
|
|
255
|
+
currentDepth: frame.currentDepth,
|
|
256
|
+
hasDirectiveLimit: frame.hasDirectiveLimit,
|
|
257
|
+
ignoredFieldsOnPath: frame.ignoredFieldsOnPath,
|
|
258
|
+
maxDepth: frame.maxDepth,
|
|
259
|
+
node: selection,
|
|
260
|
+
parentType: parentType2,
|
|
261
|
+
path: frame.path,
|
|
262
|
+
visitedFragments: frame.visitedFragments
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
default: {
|
|
267
|
+
const exhaustiveCheck = selection;
|
|
268
|
+
throw new Error(`Unhandled selection kind: ${exhaustiveCheck.kind}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { depth: globalMaxDepth, violation: deepestViolation };
|
|
274
|
+
}
|
|
275
|
+
function collectInterfaces(type) {
|
|
276
|
+
const interfaces = [];
|
|
277
|
+
const seen = /* @__PURE__ */ new Set();
|
|
278
|
+
const stack = [...type.getInterfaces()];
|
|
279
|
+
for (let iface = stack.pop(); iface !== void 0; iface = stack.pop()) {
|
|
280
|
+
if (seen.has(iface.name)) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
seen.add(iface.name);
|
|
284
|
+
interfaces.push(iface);
|
|
285
|
+
for (const parent of iface.getInterfaces()) {
|
|
286
|
+
if (!seen.has(parent.name)) {
|
|
287
|
+
stack.push(parent);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return interfaces;
|
|
292
|
+
}
|
|
293
|
+
function createTraversalCaches() {
|
|
294
|
+
return {
|
|
295
|
+
directiveDepths: /* @__PURE__ */ new Map(),
|
|
296
|
+
interfaces: /* @__PURE__ */ new Map()
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function extractDefinitions(definitions) {
|
|
300
|
+
const fragments = /* @__PURE__ */ new Map();
|
|
301
|
+
const operations = [];
|
|
302
|
+
for (const definition of definitions) {
|
|
303
|
+
if (definition.kind === import_graphql2.Kind.FRAGMENT_DEFINITION) {
|
|
304
|
+
fragments.set(definition.name.value, definition);
|
|
305
|
+
} else if (definition.kind === import_graphql2.Kind.OPERATION_DEFINITION) {
|
|
306
|
+
operations.push(definition);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return { fragments, operations };
|
|
310
|
+
}
|
|
311
|
+
function getCachedInterfaces(caches, type) {
|
|
312
|
+
const cached = caches.interfaces.get(type.name);
|
|
313
|
+
if (cached !== void 0) {
|
|
314
|
+
return cached;
|
|
315
|
+
}
|
|
316
|
+
const result = collectInterfaces(type);
|
|
317
|
+
caches.interfaces.set(type.name, result);
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
function pathPush(parent, segment) {
|
|
321
|
+
return { parent, segment };
|
|
322
|
+
}
|
|
323
|
+
function pathToArray(node) {
|
|
324
|
+
const result = [];
|
|
325
|
+
let current = node;
|
|
326
|
+
while (current) {
|
|
327
|
+
result.push(current.segment);
|
|
328
|
+
current = current.parent;
|
|
329
|
+
}
|
|
330
|
+
result.reverse();
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
function resolveFieldDirectiveDepth(caches, config, currentDepth, currentMaxDepth, fieldDef, hasDirectiveLimit, namedType) {
|
|
334
|
+
const cacheKey = `${namedType.name}:${fieldDef.name}`;
|
|
335
|
+
let directiveDepth;
|
|
336
|
+
if (caches.directiveDepths.has(cacheKey)) {
|
|
337
|
+
directiveDepth = caches.directiveDepths.get(cacheKey);
|
|
338
|
+
} else {
|
|
339
|
+
directiveDepth = getDepthFromDirective(fieldDef);
|
|
340
|
+
if (directiveDepth === void 0) {
|
|
341
|
+
const interfaces = getCachedInterfaces(caches, namedType);
|
|
342
|
+
for (const iface of interfaces) {
|
|
343
|
+
const ifaceField = iface.getFields()[fieldDef.name];
|
|
344
|
+
const ifaceDepth = getDepthFromDirective(ifaceField);
|
|
345
|
+
if (ifaceDepth !== void 0) {
|
|
346
|
+
directiveDepth = directiveDepth === void 0 ? ifaceDepth : Math.min(directiveDepth, ifaceDepth);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
caches.directiveDepths.set(cacheKey, directiveDepth);
|
|
351
|
+
}
|
|
352
|
+
if (directiveDepth !== void 0) {
|
|
353
|
+
const relativeMax = currentDepth + directiveDepth;
|
|
354
|
+
const maxDepth = config.directiveMode === "cap" ? Math.min(currentMaxDepth, relativeMax) : hasDirectiveLimit ? Math.min(currentMaxDepth, relativeMax) : relativeMax;
|
|
355
|
+
return { hasDirectiveLimit: true, maxDepth };
|
|
356
|
+
}
|
|
357
|
+
return { hasDirectiveLimit, maxDepth: currentMaxDepth };
|
|
358
|
+
}
|
|
359
|
+
function resolveTypeCondition(typeConditionName, schema, currentParentType) {
|
|
360
|
+
if (schema) {
|
|
361
|
+
const type = schema.getType(typeConditionName);
|
|
362
|
+
if (type && (0, import_graphql2.isCompositeType)(type)) {
|
|
363
|
+
return type;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return currentParentType;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/depth-limit.ts
|
|
370
|
+
var DIRECTIVE_MODES = /* @__PURE__ */ new Set(["cap", "override"]);
|
|
371
|
+
var IGNORE_MODES = /* @__PURE__ */ new Set(["exclude", "skip"]);
|
|
372
|
+
var INTROSPECTION_MODES = /* @__PURE__ */ new Set(["all", "none", "typename"]);
|
|
373
|
+
function depthLimit(maxDepth, options, callback) {
|
|
374
|
+
if (!Number.isInteger(maxDepth) || maxDepth < 0) {
|
|
375
|
+
throw new Error(`Invalid maxDepth: ${maxDepth}. Must be a non-negative integer.`);
|
|
376
|
+
}
|
|
377
|
+
const normalized = normalizeDepthLimitArgs(options, callback);
|
|
378
|
+
return createValidationRule(maxDepth, normalized.options, normalized.callback);
|
|
379
|
+
}
|
|
380
|
+
function assertBooleanOption(name, value) {
|
|
381
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
382
|
+
throw new TypeError(`Invalid ${name}: expected boolean, received ${typeof value}.`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function createValidationRule(maxDepth, options, callback) {
|
|
386
|
+
const shortCircuit = options?.shortCircuit ?? callback == null;
|
|
387
|
+
return function depthLimitValidationRule(context) {
|
|
388
|
+
let anonymousCount = 0;
|
|
389
|
+
const caches = createTraversalCaches();
|
|
390
|
+
const document = context.getDocument();
|
|
391
|
+
const depths = callback ? {} : void 0;
|
|
392
|
+
const { fragments, operations } = extractDefinitions(document.definitions);
|
|
393
|
+
const schema = context.getSchema() ?? void 0;
|
|
394
|
+
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
|
+
}
|
|
401
|
+
const config = {
|
|
402
|
+
caseInsensitiveIgnore: options?.caseInsensitiveIgnore ?? false,
|
|
403
|
+
directiveMode: options?.directiveMode ?? "cap",
|
|
404
|
+
ignore: options?.ignore,
|
|
405
|
+
ignoreMode: options?.ignoreMode ?? "exclude",
|
|
406
|
+
introspectionMode: options?.ignoreIntrospection ?? "typename",
|
|
407
|
+
limitIgnoredRecursion: options?.limitIgnoredRecursion ?? false,
|
|
408
|
+
shortCircuit,
|
|
409
|
+
useDirective
|
|
410
|
+
};
|
|
411
|
+
const rootTypeMap = schema ? {
|
|
412
|
+
mutation: schema.getMutationType() ?? void 0,
|
|
413
|
+
query: schema.getQueryType() ?? void 0,
|
|
414
|
+
subscription: schema.getSubscriptionType() ?? void 0
|
|
415
|
+
} : {
|
|
416
|
+
mutation: void 0,
|
|
417
|
+
query: void 0,
|
|
418
|
+
subscription: void 0
|
|
419
|
+
};
|
|
420
|
+
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
|
+
}
|
|
433
|
+
const rootType = rootTypeMap[operation.operation];
|
|
434
|
+
let result;
|
|
435
|
+
try {
|
|
436
|
+
result = calculateDepth(caches, config, fragments, maxDepth, operation, rootType, schema);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
if (error instanceof Error && error.name === "IgnoreRuleError") {
|
|
439
|
+
context.reportError(
|
|
440
|
+
new import_graphql3.GraphQLError(error.message, {
|
|
441
|
+
extensions: {
|
|
442
|
+
code: ERROR_CODES.IGNORE_RULE_ERROR
|
|
443
|
+
},
|
|
444
|
+
nodes: [operation],
|
|
445
|
+
originalError: error
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
if (depths) {
|
|
453
|
+
setDepthResult(depths, operationName, result.depth);
|
|
454
|
+
}
|
|
455
|
+
if (result.violation) {
|
|
456
|
+
const depthValue = result.violation.depth;
|
|
457
|
+
const depthLabel = shortCircuit ? `at least ${depthValue}` : `${depthValue}`;
|
|
458
|
+
const violationPath = result.violation.path;
|
|
459
|
+
const pathSuffix = violationPath.length > 0 ? ` (at ${violationPath.join(".")})` : "";
|
|
460
|
+
context.reportError(
|
|
461
|
+
new import_graphql3.GraphQLError(
|
|
462
|
+
`'${operationName}' has depth ${depthLabel} which exceeds maximum allowed depth of ${result.violation.maxDepth}${pathSuffix}`,
|
|
463
|
+
{
|
|
464
|
+
extensions: {
|
|
465
|
+
code: ERROR_CODES.QUERY_TOO_DEEP,
|
|
466
|
+
depth: depthValue,
|
|
467
|
+
maxDepth: result.violation.maxDepth,
|
|
468
|
+
path: violationPath,
|
|
469
|
+
shortCircuit
|
|
470
|
+
},
|
|
471
|
+
nodes: result.violation.node ? [operation, result.violation.node] : [operation]
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (callback && depths) {
|
|
478
|
+
callback(depths);
|
|
479
|
+
}
|
|
480
|
+
return {};
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function isIgnoreRule(rule) {
|
|
484
|
+
return typeof rule === "function" || rule instanceof RegExp || typeof rule === "string";
|
|
485
|
+
}
|
|
486
|
+
function normalizeDepthLimitArgs(options, callback) {
|
|
487
|
+
if (callback !== void 0 && typeof callback !== "function") {
|
|
488
|
+
throw new TypeError("Invalid callback: expected a function.");
|
|
489
|
+
}
|
|
490
|
+
if (typeof options === "function") {
|
|
491
|
+
if (callback) {
|
|
492
|
+
throw new TypeError("Invalid depthLimit arguments: callback provided twice.");
|
|
493
|
+
}
|
|
494
|
+
return { callback: options, options: void 0 };
|
|
495
|
+
}
|
|
496
|
+
if (options !== void 0 && (options === null || typeof options !== "object" || Array.isArray(options))) {
|
|
497
|
+
const receivedType = Array.isArray(options) ? "array" : options === null ? "null" : typeof options;
|
|
498
|
+
throw new TypeError(`Invalid options: expected an object, received ${receivedType}.`);
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
callback,
|
|
502
|
+
options: options ? normalizeDepthLimitOptions(options) : void 0
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function normalizeDepthLimitOptions(options) {
|
|
506
|
+
assertBooleanOption("caseInsensitiveIgnore", options.caseInsensitiveIgnore);
|
|
507
|
+
if (options.directiveMode !== void 0 && !DIRECTIVE_MODES.has(options.directiveMode)) {
|
|
508
|
+
throw new TypeError(
|
|
509
|
+
`Invalid directiveMode: "${options.directiveMode}". Must be "cap" or "override".`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
if (options.ignoreIntrospection !== void 0 && !INTROSPECTION_MODES.has(options.ignoreIntrospection)) {
|
|
513
|
+
throw new TypeError(
|
|
514
|
+
`Invalid ignoreIntrospection: "${options.ignoreIntrospection}". Must be "all", "none", or "typename".`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
if (options.ignoreMode !== void 0 && !IGNORE_MODES.has(options.ignoreMode)) {
|
|
518
|
+
throw new TypeError(
|
|
519
|
+
`Invalid ignoreMode: "${options.ignoreMode}". Must be "exclude" or "skip".`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
assertBooleanOption("limitIgnoredRecursion", options.limitIgnoredRecursion);
|
|
523
|
+
assertBooleanOption("shortCircuit", options.shortCircuit);
|
|
524
|
+
assertBooleanOption("useDirective", options.useDirective);
|
|
525
|
+
const ignore = normalizeIgnoreRules(options.ignore);
|
|
526
|
+
return { ...options, ignore };
|
|
527
|
+
}
|
|
528
|
+
function normalizeIgnoreRules(ignore) {
|
|
529
|
+
if (ignore == null) {
|
|
530
|
+
return void 0;
|
|
531
|
+
}
|
|
532
|
+
const rules = Array.isArray(ignore) ? ignore : [ignore];
|
|
533
|
+
for (const [index, rule] of rules.entries()) {
|
|
534
|
+
if (!isIgnoreRule(rule)) {
|
|
535
|
+
const receivedType = Array.isArray(rule) ? "array" : rule === null ? "null" : typeof rule;
|
|
536
|
+
throw new TypeError(
|
|
537
|
+
`Invalid ignore rule at index ${index}: expected string, RegExp, or function, received ${receivedType}.`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return rules;
|
|
542
|
+
}
|
|
543
|
+
function setDepthResult(target, key, value) {
|
|
544
|
+
Object.defineProperty(target, key, {
|
|
545
|
+
configurable: true,
|
|
546
|
+
enumerable: true,
|
|
547
|
+
value,
|
|
548
|
+
writable: true
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
552
|
+
0 && (module.exports = {
|
|
553
|
+
ERROR_CODES,
|
|
554
|
+
depthDirectiveTypeDefs,
|
|
555
|
+
depthLimit
|
|
556
|
+
});
|
|
557
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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"]}
|