npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

graphql-static-analysis

v0.1.0

Published

A composable static-analysis engine for GraphQL operations

Readme

graphql-static-analysis

graphql-static-analysis is a composable static-analysis engine for validated GraphQL operations. It implements the TreeSummary model from graphql-lean.

The engine owns the GraphQL-specific reasoning: possible runtime object types, @include and @skip, fragments, field collection by response name, and merged child selection sets. Each analysis only defines how a collected field is summarized, how simultaneous summaries combine, and how runtime alternatives join.

The package includes:

  • IBM GraphQL Cost Directives analysis with separate type and field costs;
  • maximum response-size analysis under a configurable list bound;
  • a custom-analysis API;
  • a migration layer for graphql-query-complexity.

It uses graphql-js for parsing, validation, operation selection, argument coercion, and variable coercion. Both the flat GraphQL.js 16 variable map and the wrapped GraphQL.js 17 variable container are supported and covered by the CI matrix.

Installation

npm install graphql graphql-static-analysis

Node.js 20 or newer is required. The package provides both ES module and CommonJS entry points.

Recommended API

The source-oriented entry points validate and coerce their inputs. For analyses that support ahead-of-execution use, omit variables to consider every feasible @include/@skip assignment. Pass variables: {} (or a request map) to analyze one concrete request and apply operation defaults. IBM cost always requires variables because argument values affect its result.

Maximum response size

import { buildSchema } from "graphql";
import { estimateMaxResponseSize } from "graphql-static-analysis";

const schema = buildSchema(`
  type Query { products: [Product!]! }
  type Product { id: ID!, name: String! }
`);

const fields = estimateMaxResponseSize({
  schema,
  document: `{ products { id name } }`,
  listSize: 100,
});

The estimate counts response-object fields at every level. Every list wrapper multiplies selected children by listSize; arithmetic saturates at Number.MAX_SAFE_INTEGER.

IBM GraphQL Cost Directives

import { CostModel, estimateCost } from "graphql-static-analysis";

const model = CostModel.fromSchema(schema);
const result = estimateCost({
  schema,
  costModel: model,
  document,
  variables: requestVariables,
  // Optional finite deployment fallback for otherwise-unbounded lists.
  defaultListSize: 100,
});

console.log(result.typeCost, result.fieldCost);

Without defaultListSize, a list without an applicable @listSize bound has infinite cost. Type and field costs are intentionally independent; an unbounded list of objects whose selected scalar fields all have zero field weight can have infinite type cost and finite field cost.

Construct CostModel, CostEstimator, or MaxResponseSizeEstimator once and reuse them when many operations share a schema.

Migrating from graphql-query-complexity

For the smallest first step, change the import and retain the familiar estimators:

import {
  createComplexityRule,
  fieldExtensionsEstimator,
  simpleEstimator,
} from "graphql-static-analysis/compat";

const rule = createComplexityRule({
  maximumComplexity: 1_000,
  variables,
  estimators: [fieldExtensionsEstimator(), simpleEstimator()],
});

The compatibility entry point exports getComplexity, createComplexityRule, simpleEstimator, fieldExtensionsEstimator, directiveEstimator, and createComplexityDirective. Its option names and threshold behavior match the established package.

The calculation is intentionally not bug-for-bug identical. TreeSummary first collects overlapping fields with the same response name, merges their children, and then calls an estimator once for the resolver field. It also keeps Boolean decisions correlated in the ExactCase mode (see below). Compare observed scores before reusing an old production threshold.

The recommended transition is:

  1. Switch to graphql-static-analysis/compat, keeping existing estimators and logging scores without changing enforcement.
  2. Compare scores for representative and adversarial operations, then choose a new threshold.
  3. Move policy to estimateCost (IBM spec) or your own domain-specific analyzeOperation algebra so list cardinality, field work, and returned type cost are modeled explicitly rather than compressed into an opaque scalar.

See the migration guide for semantic differences and side-by-side examples.

Precision and performance modes

| | AnalysisMode.ExactCase | AnalysisMode.Syntactic | | --- | --- | --- | | Field collection | Groups every field that executes together under one response name. | Keeps fields from distinct cumulative conditions separate. | | Boolean scope | Shares unknown assignments across the whole operation. | Summarizes recursive selection siblings independently. | | Precision | Most precise; the default. | Conservative for nonnegative/additive analyses and potentially less precise. | | Cost | Can enumerate many feasible Boolean cases. | Usually less expensive. |

Use exact mode unless analysis latency is more important than precision:

import { AnalysisMode } from "graphql-static-analysis";

const fields = estimateMaxResponseSize({
  schema,
  document,
  listSize: 100,
  mode: AnalysisMode.Syntactic,
});

Low-level and custom analyses

Use Analyzer when inputs have already been parsed, validated, selected, and coerced:

const analyzer = new Analyzer(schema);
const summary = analyzer
  .operation(document, operation)
  .mode(AnalysisMode.ExactCase)
  .variableValues(coercedVariables)
  .analyze(algebra);

This path assumes the schema, document, operation, and variables are already valid. Use analyzeOperation when the library should parse, validate, select, and coerce. See Adding a custom analysis and Engine architecture.

Differential fuzzing

The engine has a bounded structural-input harness that compares canonical TypeScript observations with a native oracle built from the Lean model. It supports deterministic exhaustive and seeded checks, exact-schedule comparison, retained regression inputs, and coverage-guided Jazzer.js campaigns. See TreeSummary differential fuzzing.

Formal model and TypeScript implementation confidence

The implementation follows the condition/tree-summary split and analysis algebras of the Lean model. It was reviewed against Lean revision fbf060d8. The Lean proofs establish properties of that model, not of this TypeScript source directly. This repository therefore maintains behavioral regression tests for field collection, Boolean correlation, abstract runtime types, IBM cost, and the compatibility API.

License

Licensed under the MIT License. See the license file.