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

@stepper-io/dependency-parser

v1.0.0

Published

TypeScript AST-based dependency parser for Stepper dynamic fields

Downloads

120

Readme

Dependency Parser

A TypeScript-based dependency parser for Stepper.io integrations that analyzes function code to detect dependencies on context properties (context.input or context.auth).

Features

  • Static dependency analysis: Detects which context properties are accessed in function code
  • Import following: Recursively analyzes imported helper functions to discover nested dependencies
  • Destructuring support: Handles object destructuring patterns
  • Aliasing support: Tracks variables that point to context properties
  • Limitation detection: Identifies patterns that prevent complete static analysis
  • Caching: Optimizes performance for repeated analyses
  • TypeScript-first: Built with full TypeScript support

Installation

pnpm install @stepper-io/dependency-parser

Basic Usage

Simple Dependency Detection

import { parseDependencies } from '@stepper-io/dependency-parser';

const functionCode = `async (context) => {
  if (!context.input.board_id) return [];
  const { group_id } = context.input;
  return [board_id, group_id];
}`;

const result = parseDependencies(functionCode);
console.log(result.dependencies); // ['board_id', 'group_id']

Import Following

Enable import following to analyze helper functions in separate files:

import fs from 'fs';
import { parseDependencies } from '@stepper-io/dependency-parser';

const actionCode = `
import { getItems } from './api';

export const action = async (context) => {
  if (!context.input.board_id) return [];
  return getItems(context, context.input.board_id);
};
`;

const result = parseDependencies(actionCode, {
  followImports: true,
  currentFilePath: '/app/actions/updateItem.ts',
  fileSystem: {
    readFileSync: (path) => fs.readFileSync(path, 'utf-8'),
    existsSync: fs.existsSync,
  },
  maxImportDepth: 3, // Default: 3
});

console.log(result.dependencies); // ['board_id', 'column_id']
// ^ Includes dependencies from both the action and getItems()

console.log(result.resolvedFunctions); // ['getItems']
console.log(result.analysisDepth); // 1

API Reference

parseDependencies(functionCode, options?)

Analyzes a function to detect dependencies on context properties.

Parameters:

  • functionCode (string): The function code to analyze
  • options (ParserOptions, optional): Configuration options

Returns: ParserResult

ParserOptions

interface ParserOptions {
  // Basic options
  includeMessages?: boolean; // Include human-readable limitation messages (default: true)
  contextProperty?: 'input' | 'auth'; // Which context property to analyze (default: 'input')

  // Import following options (NEW)
  followImports?: boolean; // Enable import following (default: false)
  maxImportDepth?: number; // Maximum recursion depth (default: 3)
  currentFilePath?: string; // Required when followImports is true
  fileSystem?: FileSystemInterface; // Required when followImports is true
  _cache?: ImportAnalysisCache; // Optional cache instance
}

interface FileSystemInterface {
  readFileSync(path: string, encoding: 'utf-8'): string;
  existsSync(path: string): boolean;
}

ParserResult

interface ParserResult {
  dependencies: string[]; // Detected dependencies
  limitations: LimitationType[]; // Analysis limitations
  limitationMessages?: string[]; // Human-readable messages
  unresolvedFunctions?: string[]; // Functions that couldn't be analyzed

  // Import following results (NEW)
  resolvedFunctions?: string[]; // Successfully analyzed imports
  analysisDepth?: number; // Maximum recursion depth reached
}

type LimitationType =
  | 'DYNAMIC_PROPERTY_ACCESS' // e.g., context.input[key]
  | 'SPREAD_OPERATOR' // e.g., { ...rest } = context.input
  | 'UNRESOLVED_FUNCTION_CALL' // Function with context not analyzed
  | 'PARSE_ERROR'; // Invalid syntax

Import Following

The import following feature recursively analyzes imported helper functions to discover nested dependencies that would otherwise be missed.

How It Works

  1. Module Resolution: Resolves import paths to absolute file paths

    • Supports relative imports: ./api, ../utils/helper
    • Tries multiple extensions: .ts, .tsx, .js, .jsx
    • Skips external packages: @stepper-io/core, lodash, etc.
  2. Function Extraction: Extracts exported function definitions

    • Arrow functions: export const fn = async () => {...}
    • Function declarations: export function fn() {...}
  3. Parameter Mapping: Maps function call arguments to parameters

    • getItems(context) → parameter context points to context
    • validate(context.input) → parameter input points to context.input
  4. Recursive Analysis: Analyzes function bodies recursively

    • Respects maxImportDepth limit
    • Detects and breaks circular dependencies
    • Caches results for performance

Example: Monday.com Integration

// actions/updateItem.ts
import { getColumns, mondayGraphQL } from './api';

export const action = async (context) => {
  const { board_id } = context.input;
  if (!board_id) return [];

  const columns = await getColumns(context, board_id);
  return columns;
};

// api.ts
import { mondayGraphQL } from './graphql';

export const getColumns = async (context, board_id) => {
  const data = await mondayGraphQL(context, query, { board_id: [board_id] });
  return data.boards[0].columns.filter((c) => c.id === context.input.column_id);
  //                                                      ^^^ Nested dependency!
};

Without import following:

const result = parseDependencies(actionCode);
// dependencies: ['board_id']
// unresolvedFunctions: ['getColumns']
// limitations: ['UNRESOLVED_FUNCTION_CALL']

With import following:

const result = parseDependencies(actionCode, {
  followImports: true,
  currentFilePath: '/app/actions/updateItem.ts',
  fileSystem: { readFileSync: fs.readFileSync, existsSync: fs.existsSync },
});
// dependencies: ['board_id', 'column_id'] ← Found the nested dependency!
// resolvedFunctions: ['getColumns', 'mondayGraphQL']
// unresolvedFunctions: []
// analysisDepth: 2

Performance Optimization

Use caching for better performance when analyzing multiple actions:

import { ImportAnalysisCache } from '@stepper-io/dependency-parser';

const cache = new ImportAnalysisCache();

// Analyze multiple actions with shared cache
for (const action of actions) {
  const result = parseDependencies(action.code, {
    followImports: true,
    currentFilePath: action.filePath,
    fileSystem: fs,
    _cache: cache,
  });
}

console.log(`Cache size: ${cache.size()} entries`);

Performance Characteristics:

  • Cold analysis (no cache): < 200ms per action
  • Warm analysis (with cache): < 50ms per action
  • Memory usage: < 50MB for 100 integrations

Depth Limiting

Control how deep the parser follows imports:

const result = parseDependencies(code, {
  followImports: true,
  maxImportDepth: 2, // Stop after 2 levels
  currentFilePath: '/app/action.ts',
  fileSystem: fs,
});

// If action → helper1 → helper2 → helper3:
// - helper1 and helper2 are analyzed (depth 1, 2)
// - helper3 is marked as UNRESOLVED_FUNCTION_CALL (depth 3 > limit)

Circular Dependency Handling

The parser automatically detects and breaks circular imports:

// a.ts
import { funcB } from './b';
export const funcA = (context) => {
  const val = context.input.fieldA;
  return funcB(context);
};

// b.ts
import { funcA } from './a';
export const funcB = (context) => {
  const val = context.input.fieldB;
  return funcA(context); // ← Circular!
};

The parser will analyze both functions once, avoiding infinite loops:

// dependencies: ['fieldA', 'fieldB']
// resolvedFunctions: ['funcA', 'funcB']

Other API Functions

analyzeFieldDependencies(fields, options?)

Analyzes an array of Stepper input fields (supports both static and dynamic fields).

buildAppDependenciesMap(app, options?)

Builds a complete dependency map for a Stepper app (all actions, triggers, and connections).

import { buildAppDependenciesMap } from '@stepper-io/dependency-parser';

const dependencyMap = buildAppDependenciesMap(myApp, {
  followImports: true,
  currentFilePath: '/app/index.ts',
  fileSystem: fs,
});

console.log(dependencyMap.actions['create_item'].dependencies);
console.log(dependencyMap.triggers['new_item'].dependencies);

Supported Patterns

Property Access

context.input.board_id;
context.auth.api_key;

Destructuring

const { board_id, group_id } = context.input;
const { input } = context;
const value = input.item_id;

Aliasing

const { input } = context;
const boardId = input.board_id;

Optional Chaining

context.input?.optional_field;

Limitations

The parser detects but cannot fully analyze these patterns:

Dynamic Property Access

const key = 'board_id';
context.input[key]; // Cannot determine statically

Spread Operator

const { board_id, ...rest } = context.input; // Cannot enumerate rest

Unresolved Function Calls (without import following)

helper(context); // Cannot analyze what helper does