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

github-typescript-utils

v0.2.3

Published

TypeScript utilities for GitHub Actions workflows - REST API, Octokit, context helpers, and common workflow functions

Readme

github-typescript-utils

TypeScript utilities for GitHub Actions workflows with github-typescript.

This package provides a comprehensive set of TypeScript utilities for GitHub Actions workflows, including REST API interactions, Octokit helpers, context utilities, and common workflow functions. Designed specifically for use with the github-typescript composite action, these utilities simplify working with GitHub's API, Actions context, and core functionality.

Features

📋 Complete Function Reference

| Category | Functions | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | | 💬 Comments | createStickyComment, findCommentByIdentifier, searchComments, deleteComment, deleteStickyComment | Create, find, search, and manage issue/PR comments | | 🔀 Pull Requests | findPullRequestsByLabels, getPullRequest, addLabelsToPullRequest, removeLabelsFromPullRequest, pullRequestHasLabels, getPullRequestFiles | Find, manage, and interact with pull requests | | 🔍 Advanced PR Search | findPRsWithLabels, searchPullRequests, findOpenPRsWithLabel, checkLabelConflicts | Advanced pull request search and label conflict detection | | 🌿 Branch Management | checkBranchExists, listAllBranches, getBranchProtection, getDefaultBranch | Branch existence, listing, and protection management | | 🚀 Deployments | listDeployments, getDeploymentStatuses, setDeploymentStatus, deleteDeployment, createDeployment | Deployment lifecycle management | | 🔧 Context & Utils | getRepoInfo, getCurrentPullRequestNumber, getCurrentIssueNumber, isPullRequestContext, isIssueContext, getCurrentSHA, getCurrentBranch, getRepositoryUrl, getIssueUrl, getPullRequestUrl | GitHub Actions context extraction and URL helpers | | 📝 Text & Formatting | escapeMarkdown, codeBlock, createMarkdownTable, truncateText, formatDate, parseGitHubDate, delay | Text formatting, markdown utilities, and date handling | | 🔤 String Utilities | snakeToCamel, camelToSnake, kebabToCamel, camelToKebab, capitalize, toTitleCase | String case conversion and text transformation | | ⚙️ Input Processing | sanitizeInput, sanitizeInputs, getBranch | Input sanitization and branch extraction from various GitHub events |

✨ Key Features

  • Type Safety - Full TypeScript support with comprehensive type definitions
  • Universal Compatibility - Works with all GitHub event types and contexts
  • Comprehensive Testing - 110+ tests with 94%+ code coverage
  • GitHub API Optimized - Efficient API usage with proper error handling and pagination support
  • Developer Friendly - Intuitive APIs with TypeScript intellisense and JSDoc documentation

Installation

# With pnpm (recommended)
pnpm add github-typescript-utils

# With npm
npm install github-typescript-utils

# With yarn
yarn add github-typescript-utils

Peer Dependencies:

  • @actions/core (^1.10.0)
  • @actions/github (^6.0.0)

Usage with github-typescript

Dependency Setup

For esbuild to resolve github-typescript-utils, you need the package available in node_modules. Choose one approach:

Option A: Root Dependencies (Simplest)

Add to your repository's root package.json:

{
  "dependencies": {
    "github-typescript-utils": "^0.2.0"
  }
}

Workflow setup:

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm
- run: pnpm install

- uses: tkstang/github-typescript@v1
  with:
    ts-file: .github/scripts/manage-pr.ts

Option B: Isolated CI Dependencies

Create .github/scripts/package.json:

{
  "name": "ci-scripts",
  "private": true,
  "type": "module",
  "dependencies": {
    "github-typescript-utils": "^0.2.0"
  }
}

Workflow setup:

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: pnpm
    cache-dependency-path: .github/scripts/pnpm-lock.yaml
- run: pnpm install
  working-directory: .github/scripts

- uses: tkstang/github-typescript@v1
  with:
    working-directory: .github/scripts
    ts-file: manage-pr.ts

Script Example

Create a TypeScript script that imports the utilities:

// .github/scripts/manage-pr.ts
import {
  createStickyComment,
  findPullRequestsByLabels,
  getRepoInfo,
  getCurrentPullRequestNumber,
  type GitHubContext,
} from "github-typescript-utils";

type Args = {
  message: string;
  labels: string[];
};

export default async function run({
  core,
  github,
  context,
  args,
}: GitHubContext & { args: Args }) {
  const repo = getRepoInfo({ core, github, context });
  const prNumber = getCurrentPullRequestNumber({ core, github, context });

  if (!prNumber) {
    core.setFailed("This action must run on a pull request");
    return;
  }

  // Create a sticky comment
  await createStickyComment({
    ctx: { core, github, context },
    repo,
    issueNumber: prNumber,
    options: {
      identifier: "status-update",
      body: `## PR Status\n\n${args.message}`,
    },
  });

  // Find PRs with specific labels
  const prs = await findPullRequestsByLabels({
    ctx: { core, github, context },
    repo,
    options: { labels: args.labels },
  });

  core.info(`Found ${prs.length} PRs with labels: ${args.labels.join(", ")}`);
  return { success: true, prCount: prs.length };
}

Use in your workflow:

- name: Manage PR
  uses: tkstang/github-typescript@v1
  with:
    ts-file: .github/scripts/manage-pr.ts
    args: ${{ toJson({ message: 'Build completed!', labels: ['ready-for-review'] }) }}

API Reference

Comments

createStickyComment(options)

Creates or updates a "sticky" comment that can be updated in subsequent runs rather than creating duplicates.

await createStickyComment({
  ctx: { core, github, context },
  repo: { owner: "tkstang", repo: "my-repo" },
  issueNumber: 123,
  options: {
    identifier: "build-status",
    body: "✅ Build completed successfully!",
    updateIfExists: true, // default: true
  },
});

searchComments(options)

Search for comments based on various criteria:

const comments = await searchComments({
  ctx: { core, github, context },
  repo,
  issueNumber: 123,
  options: {
    bodyContains: "error",
    author: "dependabot[bot]",
    createdAfter: new Date("2024-01-01"),
    limit: 10,
  },
});

deleteComment(options) / deleteStickyComment(options)

Remove comments by ID or sticky identifier:

// Delete by ID
await deleteComment({
  ctx: { core, github, context },
  repo,
  commentId: 456789,
});

// Delete sticky comment
await deleteStickyComment({
  ctx: { core, github, context },
  repo,
  issueNumber: 123,
  identifier: "build-status",
});

Pull Requests

findPullRequestsByLabels(options)

Find pull requests that have all specified labels:

const prs = await findPullRequestsByLabels({
  ctx: { core, github, context },
  repo,
  options: {
    labels: ["bug", "ready-for-review"],
    state: "open", // 'open' | 'closed' | 'all'
    limit: 20,
  },
});

addLabelsToPullRequest(options) / removeLabelsFromPullRequest(options)

Manage PR labels:

await addLabelsToPullRequest({
  ctx: { core, github, context },
  repo,
  pullNumber: 123,
  labels: ["approved", "ready-to-merge"],
});

await removeLabelsFromPullRequest({
  ctx: { core, github, context },
  repo,
  pullNumber: 123,
  labels: ["work-in-progress"],
});

getPullRequestFiles(options)

Get the list of files changed in a PR:

const files = await getPullRequestFiles({
  ctx: { core, github, context },
  repo,
  pullNumber: 123,
});

console.log(
  files.map((f) => `${f.filename} (+${f.additions}/-${f.deletions})`)
);

Context Utilities

Repository and Context Information

// Get repository info from context
const repo = getRepoInfo({ core, github, context });
// Returns: { owner: 'tkstang', repo: 'my-repo' }

// Get current PR/issue number
const prNumber = getCurrentPullRequestNumber({ core, github, context });
const issueNumber = getCurrentIssueNumber({ core, github, context });

// Check context type
const isPR = isPullRequestContext({ core, github, context });
const isIssue = isIssueContext({ core, github, context });

// Get commit/branch info
const sha = getCurrentSHA({ core, github, context });
const branch = getCurrentBranch({ core, github, context });

URL Helpers

const repoUrl = getRepositoryUrl({ core, github, context });
const prUrl = getPullRequestUrl({ core, github, context }, 123);
const issueUrl = getIssueUrl({ core, github, context }, 456);

Markdown Utilities

Text Formatting

import {
  escapeMarkdown,
  codeBlock,
  createMarkdownTable,
  truncateText,
} from "github-typescript-utils";

// Escape special markdown characters
const safe = escapeMarkdown("Text with *special* characters");

// Create code blocks
const code = codeBlock('console.log("hello");', "javascript");

// Create tables
const table = createMarkdownTable(
  ["File", "Status", "Changes"],
  [
    ["src/index.ts", "✅", "+15/-3"],
    ["README.md", "📝", "+2/-0"],
  ]
);

// Truncate long text
const short = truncateText("Very long text...", 50);

String Utilities

Case Conversion

import {
  snakeToCamel,
  camelToSnake,
  kebabToCamel,
  camelToKebab,
  capitalize,
  toTitleCase,
} from "github-typescript-utils";

// Convert between naming conventions
const camelCase = snakeToCamel("hello_world"); // "helloWorld"
const snakeCase = camelToSnake("helloWorld"); // "hello_world"
const kebabCase = camelToKebab("helloWorld"); // "hello-world"
const camelFromKebab = kebabToCamel("hello-world"); // "helloWorld"

// Text transformation
const capitalized = capitalize("hello"); // "Hello"
const titleCase = toTitleCase("hello world"); // "Hello World"

Branch Management

Branch Operations

import {
  checkBranchExists,
  listAllBranches,
  getBranchProtection,
  getDefaultBranch,
} from "github-typescript-utils";

// Check if branch exists
const exists = await checkBranchExists({
  ctx: { core, github, context },
  repo,
  branch: "feature-branch",
});

// List all branches
const branches = await listAllBranches({
  ctx: { core, github, context },
  repo,
  limit: 50,
});

// Get branch protection rules
const protection = await getBranchProtection({
  ctx: { core, github, context },
  repo,
  branch: "main",
});

// Get default branch
const defaultBranch = await getDefaultBranch({
  ctx: { core, github, context },
  repo,
});

Deployment Management

Deployment Lifecycle

import {
  listDeployments,
  createDeployment,
  setDeploymentStatus,
  getDeploymentStatuses,
  deleteDeployment,
} from "github-typescript-utils";

// Create a deployment
const deployment = await createDeployment({
  ctx: { core, github, context },
  repo,
  ref: "main",
  environment: "production",
  description: "Deploy v1.0.0",
});

// Set deployment status
await setDeploymentStatus({
  ctx: { core, github, context },
  repo,
  deploymentId: deployment.id,
  state: "success",
  description: "Deployment completed successfully",
});

// List deployments
const deployments = await listDeployments({
  ctx: { core, github, context },
  repo,
  environment: "production",
});

Advanced PR Search

Enhanced PR Operations

import {
  findPRsWithLabels,
  searchPullRequests,
  checkLabelConflicts,
  findOpenPRsWithLabel,
} from "github-typescript-utils";

// Find PRs with multiple labels
const prs = await findPRsWithLabels({
  ctx: { core, github, context },
  repo,
  labels: ["bug", "urgent"],
  excludePRs: [123], // Exclude specific PR numbers
});

// Advanced PR search
const searchResults = await searchPullRequests({
  ctx: { core, github, context },
  repo,
  options: {
    labels: ["feature"],
    author: "dependabot[bot]",
    state: "open",
  },
});

// Check for label conflicts
const conflict = await checkLabelConflicts({
  ctx: { core, github, context },
  repo,
  prNumber: 123,
  label: "sync-branch: main",
});

if (conflict.hasConflict) {
  core.warning(`Label conflict with PR #${conflict.conflictingPR?.number}`);
}

Input Processing

Input Sanitization and Branch Extraction

import {
  sanitizeInput,
  sanitizeInputs,
  getBranch,
} from "github-typescript-utils";

// Remove quotes from workflow inputs
const cleanInput = sanitizeInput('"quoted-value"'); // "quoted-value"

// Sanitize all string properties in an object
const cleanInputs = sanitizeInputs({
  name: '"John"',
  age: 30,
  title: '"Developer"',
}); // { name: "John", age: 30, title: "Developer" }

// Extract branch from any GitHub event
const branch = getBranch({ core, github, context });
// Works with: pull_request, push, workflow_run, etc.

Type Definitions

The package exports comprehensive TypeScript types:

import type {
  // Core types
  GitHubContext,
  RepoInfo,
  PullRequest,
  IssueComment,

  // Comment types
  StickyCommentOptions,
  CommentSearchOptions,

  // Pull request types
  PullRequestSearchOptions,
  PullRequestFile,
  AdvancedPRSearchOptions,

  // Deployment types
  Deployment,
  DeploymentStatus,
} from "github-typescript-utils";

Examples

Build Status Updater

// .github/scripts/update-build-status.ts
import {
  createStickyComment,
  getRepoInfo,
  getCurrentPullRequestNumber,
} from "github-typescript-utils";

export default async function run({ core, github, context, args }) {
  const repo = getRepoInfo({ core, github, context });
  const prNumber = getCurrentPullRequestNumber({ core, github, context });

  if (!prNumber) return { skipped: true };

  const status = args.success ? "✅ Passed" : "❌ Failed";
  const details = args.details || "";

  await createStickyComment({
    ctx: { core, github, context },
    repo,
    issueNumber: prNumber,
    options: {
      identifier: "ci-status",
      body: `## 🚀 CI Status\n\n${status}\n\n${details}`,
    },
  });

  return { updated: true };
}

PR Triage Bot

// .github/scripts/triage-prs.ts
import {
  findPullRequestsByLabels,
  addLabelsToPullRequest,
  getRepoInfo,
} from "github-typescript-utils";

export default async function run({ core, github, context }) {
  const repo = getRepoInfo({ core, github, context });

  // Find stale PRs
  const stalePRs = await findPullRequestsByLabels({
    ctx: { core, github, context },
    repo,
    options: {
      labels: ["needs-review"],
      state: "open",
      sort: "updated",
      direction: "asc",
      limit: 10,
    },
  });

  // Label old PRs as stale
  for (const pr of stalePRs) {
    const daysSinceUpdate =
      (Date.now() - new Date(pr.updated_at).getTime()) / (1000 * 60 * 60 * 24);

    if (daysSinceUpdate > 7) {
      await addLabelsToPullRequest({
        ctx: { core, github, context },
        repo,
        pullNumber: pr.number,
        labels: ["stale"],
      });

      core.info(
        `Labeled PR #${pr.number} as stale (${Math.round(
          daysSinceUpdate
        )} days old)`
      );
    }
  }

  return { processed: stalePRs.length };
}

Contributing

  1. Clone the repository
  2. Install dependencies: pnpm install
  3. Make changes to src/ files
  4. Build: pnpm run build
  5. Test your changes
  6. Submit a pull request

License

MIT - see LICENSE file for details.