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

upsun-sdk-checker

v0.1.1

Published

Compare Upsun SDK signatures across multiple language implementations (node, PHP, ...) via GitHub API

Downloads

63

Readme

SDK Signature Checker

A TypeScript tool to compare method signatures across different SDK implementations (Node.js, PHP, Python, and potentially other languages).

🎯 Objective

Verify that all Task classes in your SDKs have the same public methods with identical parameter signatures, making it easier to maintain parity across different implementations.

🏗️ Architecture

The project is organized in a modular way to facilitate adding new SDKs:

src/
├── types/           # Shared TypeScript types
├── github/          # GitHub API client
├── parsers/         # Language-specific parsers
│   ├── base.ts          # Abstract Parser class
│   ├── typescript.ts    # TypeScript/Node.js parser
│   ├── php.ts           # PHP parser
│   ├── python.ts        # Python parser
│   ├── type-normalizer.ts # Common type name normalization
│   └── factory.ts       # Factory to get the right parser
├── reporters/       # Report generators
│   └── console.ts   # Colored console report
├── loader.ts        # Load SDKs from GitHub
├── comparison.ts    # Comparison engine
├── config.ts        # SDK configuration
└── index.ts         # Main entry point

📦 Installation

npm install

🚀 Usage

npm run full:checks

The script will:

  1. Fetch files from GitHub (no cloning necessary)
  2. Parse Task classes from each SDK
  3. Compare methods and their signatures
  4. Display a detailed report in the terminal

🔧 Configuration

Add a new SDK

Edit src/config.ts:

export const SDK_CONFIGS: SDKConfig[] = [
  {
    language: 'node',
    owner: 'upsun',
    repo: 'upsun-sdk-node',
    tasksPath: 'src/core/tasks',
    modelsPath: 'src/models', // Optional: path to model/type files for parameter type resolution
    branch: 'develop', // Optional: specify a branch (defaults to 'main' if not specified)
  },
  {
    language: 'php',
    owner: 'upsun',
    repo: 'upsun-sdk-php',
    tasksPath: 'src/Core/Tasks',
    branch: 'main', // You can test different branches across different SDKs
  },
  {
    language: 'python',
    owner: 'upsun',
    repo: 'upsun-sdk-python',
    tasksPath: 'src/upsun/tasks',
    // branch: 'develop', // Optional
  },
  // Example: Golang SDK
  // {
  //   language: 'golang',
  //   owner: 'upsun',
  //   repo: 'upsun-sdk-go',
  //   tasksPath: 'core/tasks',
  // },
];

Specifying branches

The branch property is optional for each SDK configuration:

  • If not specified, the tool defaults to the main branch
  • You can specify different branches for each SDK to test cross-version compatibility
  • For example, test the develop branch of one SDK against the main branch of another

This is useful for:

  • Testing feature branches before release
  • Validating compatibility between SDK versions
  • Comparing different development branches

Overriding branches via environment variables

You can override the branches specified in src/config.ts using environment variables without modifying the configuration file. Environment variables follow the pattern:

SDK_<LANGUAGE>_BRANCH=branchname

Examples:

# Test specific branch of Node SDK
SDK_NODE_BRANCH=develop npm run full:checks

# Test specific branches of multiple SDKs
SDK_NODE_BRANCH=feature/new-api SDK_PHP_BRANCH=develop npm run full:checks

# Override Python SDK branch
SDK_PYTHON_BRANCH=feature/my-branch npm run full:checks

# Override one SDK, use configured branch for others
SDK_PHP_BRANCH=testing npm run full:checks

The environment variable name is constructed from the language:

  • nodeSDK_NODE_BRANCH
  • phpSDK_PHP_BRANCH
  • pythonSDK_PYTHON_BRANCH
  • golangSDK_GOLANG_BRANCH

Comparison options

You can control what gets compared via environment variables (or a .env file):

| Variable | Default | Description | | ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | EXCLUDE_DEPRECATED | true | Exclude methods annotated with @deprecated from the comparison | | EXCLUDE_FILTER_METHODS | false | Exclude methods that have a parameter whose name contains filter (useful when Node uses a single filters object but PHP/Python have flat parameters) | | EXCLUDE_OPTIONAL_PARAMS | false | Ignore optional parameters when comparing signatures. A parameter is considered optional if: it is explicitly marked ?, its type is an interface where all fields are optional (resolved via modelsPath), or it uses an all-optional inline type |

# Include deprecated methods in the comparison
EXCLUDE_DEPRECATED=false npm run full:checks

# Ignore methods with a 'filter' parameter
EXCLUDE_FILTER_METHODS=true npm run full:checks

# Ignore optional parameters when comparing signatures
EXCLUDE_OPTIONAL_PARAMS=true npm run full:checks

# Combine options
EXCLUDE_DEPRECATED=false EXCLUDE_FILTER_METHODS=true EXCLUDE_OPTIONAL_PARAMS=true npm run full:checks

Deprecation is detected from:

  • PHP@deprecated in the /** */ docblock before the method
  • TypeScript / Node@deprecated in the JSDoc before the method
  • Python@deprecated decorator on the method

GitHub Token (optional)

To avoid GitHub API rate limits (60 req/h unauthenticated vs 5 000 req/h authenticated), define a token:

export GITHUB_TOKEN=your_github_token_here
npm run full:checks

The token also grants access to private repositories.

Add a parser for a new language

  1. Create a new parser in src/parsers/ that extends the Parser class:
// src/parsers/golang.ts
import { ClassInfo } from '../types/index.js';
import { Parser } from './base.js';

export class GolangParser extends Parser {
  readonly language = 'Golang';
  readonly fileExtension = '.go';

  parseFile(fileName: string, content: string): ClassInfo | null {
    // Implement your parsing logic here
  }
}
  1. Register the parser in src/parsers/factory.ts:
import { GolangParser } from './golang.js';

export class ParserFactory {
  private static parsers: Map<string, Parser> = new Map([
    ['typescript', new TypeScriptParser()],
    ['node', new TypeScriptParser()],
    ['php', new PHPParser()],
    ['python', new PythonParser()],
    ['golang', new GolangParser()], // New parser
  ]);
  // ...
}

📊 Report Example

════════════════════════════════════════════════════════════════════════════════
  SDK Signature Comparison Report
════════════════════════════════════════════════════════════════════════════════

❌ Missing Classes

────────────────────────────────────────────────────────────────────────────────

📦 BackupTask
   Missing from: node

📦 DomainTask
   Missing from: php

────────────────────────────────────────────────────────────────────────────────

════════════════════════════════════════════════════════════════════════════════

📦 Class: ActivityTask
   Languages: node, php
────────────────────────────────────────────────────────────────────────────────

  ⚠️  Missing Methods:
     node:
       - getStatus()

  ⚠️  Signature Differences:

     Method: create()
       node: (name, options?)
       php: (name, config)

════════════════════════════════════════════════════════════════════════════════
  Summary
────────────────────────────────────────────────────────────────────────════════
  Total classes analyzed: 7
  Missing classes: 2
  ⚠️  Found 5 issue(s)

❌ Missing Classes:
   - BackupTask: missing from node
   - DomainTask: missing from php
════════════════════════════════════════════════════════════════════════════════

🧪 What is Compared

  • Class presence: Detects when a class exists in some SDKs but not in others
  • Missing classes report: Highlights classes that need to be implemented across all SDKs
  • Presence of public methods: Identifies missing methods in each implementation
  • Parameter names: Ensures method signatures use consistent parameter names
  • Parameter order: Verifies the order of parameters across implementations
  • Optional vs required parameters: Checks parameter optionality consistency
  • Type normalization: Converts language-specific type names to a common vocabulary before comparing
  • Deprecated methods: Automatically excluded (configurable via EXCLUDE_DEPRECATED)
  • Filter methods: Optionally excluded when SDK conventions differ (configurable via EXCLUDE_FILTER_METHODS)
  • Optional parameters: Optionally ignored during signature comparison (configurable via EXCLUDE_OPTIONAL_PARAMS)

🔍 Technical Details

Parsers

Each parser is responsible for:

  • Identifying classes in source files
  • Extracting public methods and their parameters
  • Detecting @deprecated annotations / decorators
  • Filtering out irrelevant methods (constructors, dunder methods, etc.)

| Language | Parser | Deprecation detection | Type extraction | | ----------------- | ---------------------------------------------- | ---------------------------------- | ------------------------------------------ | | TypeScript / Node | AST via @typescript-eslint/typescript-estree | @deprecated in JSDoc | Source text via AST range + models context | | PHP | Regex | @deprecated in /** */ docblock | Inline type hints | | Python | Line-by-line with decorator tracking | @deprecated decorator | Type annotations |

Model files context (modelsPath)

For TypeScript / Node SDKs, parameter types are often defined in separate model files (e.g. UpdateUserRequest, FilterListOrders). If you configure a modelsPath, the loader will fetch all model files and pass their content to the parser. The parser then scans them for interface/type declarations where every property is optional — those types are treated as optional parameters.

Important: A parameter is only treated as optional if all fields of its interface type are optional. If at least one field is required (e.g. GrantProjectUserAccessRequestInner.userId: string), the parameter is kept as required in the comparison.

Type normalization

Each language uses different names for the same primitive types. All parsers run their extracted types through a shared normalizeType() function (src/parsers/type-normalizer.ts) before storing them, so the comparison engine always works with a common vocabulary.

| Raw (TypeScript AST) | Raw (Python) | Raw (PHP) | Normalized | | -------------------- | --------------------------- | -------------- | ------------------------- | | TSStringKeyword | str | string | string | | TSNumberKeyword | int, float | int, float | number | | TSBooleanKeyword | bool | bool | boolean | | TSArrayType | list, List[X] | array | array / array<X> | | TSObjectKeyword | dict, Dict[K,V] | — | object / object<K, V> | | TSAnyKeyword | Any | mixed | any | | — | Optional[X] / X \| None | ?string | string? |

Custom class types (e.g. ProjectFilters) are left as-is since they are already language-agnostic.

Comparison Engine

  • Compares classes with the same name across all SDKs
  • Applies ComparisonOptions to filter methods before comparison
  • Identifies missing methods in each SDK
  • Detects signature differences (parameter names and order)
  • Generates a comprehensive report:
    • Missing classes section: Classes that don't exist in all SDKs (shown first)
    • Class comparison section: Analyses classes present in multiple SDKs
    • Missing methods: Lists methods missing in specific language implementations
    • Signature differences: Shows parameter differences between implementations
    • Summary: Total classes analyzed, missing class count, and total issues

🛠️ Available Scripts

  • npm run build : Compile TypeScript
  • npm run full:checks : Launch the comparison
  • npm run clean : Clean generated files

🔁 CI/CD

This repository includes two GitHub Actions workflows:

  • .github/workflows/qa.yml
    • Triggered on push (main, develop) and pull_request
    • Runs linting, build, and the reference command:
EXCLUDE_OPTIONAL_PARAMS=true EXCLUDE_DEPRECATED=true SDK_PHP_BRANCH=main SDK_NODE_BRANCH=main npm run full:checks
  • .github/workflows/publish.yml
    • Triggered when a GitHub Release is published
    • Builds the package and publishes it to npmjs

Required GitHub Secrets

  • NPM_TOKEN: npm automation token with publish permission for the target package

Release Flow

  1. Update package.json version
  2. Push changes and tag
  3. Create a GitHub Release from the tag
  4. publish.yml publishes the package to npmjs automatically

Using from another repository CI

After the package is published, a consuming repository can install and run it with:

npm install upsun-sdk-checker
EXCLUDE_OPTIONAL_PARAMS=true EXCLUDE_DEPRECATED=true SDK_PHP_BRANCH=main SDK_NODE_BRANCH=main npx upsun-sdk-checker full:checks

full:checks is exposed as a CLI alias for CI usage.

📝 Notes

  • Constructors and destructors are automatically excluded from comparison
  • Only public methods are compared
  • @deprecated methods are excluded by default (set EXCLUDE_DEPRECATED=false to include them)
  • Methods with a filter parameter can be excluded via EXCLUDE_FILTER_METHODS=true
  • Optional parameters can be excluded from signature comparison via EXCLUDE_OPTIONAL_PARAMS=true
  • The script returns exit code 1 if there are differences, 0 otherwise (useful for CI/CD)

🤝 Contributing

To add support for a new language:

  1. Create a new parser in src/parsers/ extending Parser (see golang.example.ts for a template)
  2. Register it in src/parsers/factory.ts
  3. Export it from src/parsers/index.ts
  4. Add the configuration in src/config.ts
  5. Test with npm run full:checks