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

@jetci/sdk-local

v0.1.4-alpha.3

Published

Readme

@jetci/sdk-local

@jetci/sdk-local contains all tools required to operate the CI locally on your dev machine.

The recommended user-facing setup is:

pnpm dlx @jetci/cli

For direct or unattended local-only setup, use:

pnpm dlx @jetci/sdk-local install

This installs the local SDK under .jetci, configures Git hooks, and adds @jetci/sdk-local to .jetci/package.json under jetci.providers. The older install-hooks command remains as an alias.

Secrets

Local hook registrations can declare secrets:

onPreCommit({}, {secrets: ["NPM_TOKEN"]}, async ctx => {
  console.log(ctx.secrets?.NPM_TOKEN)
})

@jetci/sdk-local resolves declared secrets from environment variables with the same names and injects them into the event context. Jobs do not have a separate secrets API.

Jobs

@jetci/sdk-local jobs run through a host-controlled executor. By default, job commands run in Docker using node:22-bookworm-slim with network disabled. The image is configurable per job, so projects can use toolchain images such as .NET, Java, Rust, or Go:

const dotnetTest = defineJob(
  "dotnet-test",
  {image: "mcr.microsoft.com/dotnet/sdk:9.0"},
  async ctx => {
    await ctx.run("dotnet test")
  },
)

You can opt into local process execution for trusted workflows:

const test = defineJob("test", {environment: "local"}, async ctx => {
  await ctx.run("pnpm test")
})

Docker jobs are booted by the local host. The orchestrator and job job function stay in the host runtime, while ctx.run() executes inside the selected container with the repository mounted at /workspace. The host communicates with the container through Docker (docker run / docker exec), so custom images do not need Node or JetCI installed. Secrets are not exposed to jobs unless an orchestrator explicitly passes values in job params.

This mirrors the provider model expected for cloud runners: @jetci/sdk defines the job registration and executor hook, while each host decides how to satisfy that execution request. @jetci/sdk-local uses Docker; a future GitHub runner could map the same job metadata to GitHub-hosted jobs, self-hosted runners, container jobs, or another isolated compute backend.

Checkout

ctx.checkout(repo) creates an isolated source snapshot for the job and makes later ctx.run() calls default to that workspace. It does not run git checkout in the user's current working tree. For onPreCommit, the local SDK checks out the staged Git index so jobs see the content that is about to be committed, including staged-but-not-committed changes.

The local host resolves the requested RepoState to a commit with git rev-parse --verify, exports it with git archive, extracts it under .jetci/.internal/tmp/workspaces/<id>, and removes that workspace when the job finishes. Docker jobs execute commands from the matching /workspace/.jetci/.tmp/workspaces/<id> path.

RepoState.commit is the preferred checkout target. sha remains for compatibility, and ref, branch, or tag names are fallback metadata.

Package Managers

Job commands run as separate process invocations, so command-specific environment variables must be passed to each ctx.run() that needs them. In Node images, use Corepack before later pnpm commands:

await ctx.run("corepack enable pnpm")
await ctx.run("pnpm install --frozen-lockfile")
await ctx.run("pnpm -r build")

If you install pnpm into PNPM_HOME, pass that env value to each command that uses pnpm. Docker shell commands automatically add PNPM_HOME to PATH when PATH is not explicitly provided. If you do provide PATH, do not use shell syntax such as $PATH inside the env value because Docker does not expand it before starting the command:

const pnpmEnv = {PNPM_HOME: "/pnpm"}
await ctx.run("corepack prepare pnpm@latest --activate", {env: pnpmEnv})
await ctx.run("pnpm config set store-dir .pnpm-store", {env: pnpmEnv})

corepack use pnpm@latest can run an install through Corepack, but it does not reliably leave a pnpm executable available for subsequent ctx.run() calls in Docker images.

Artifacts

ctx.artifactUpload() accepts Node readable streams, Uint8Array, ArrayBuffer, files, and directories. Paths are relative to the current job workspace. Set zip: true on a path source to store it as a ZIP archive:

const artifact = await ctx.artifactUpload(
    {path: "dist", zip: true},
    {name: "dist.zip", persist: false},
)

await ctx.artifactDownload({
    src: artifact,
    path: "restored-dist",
    unzip: true,
})

Temporary artifacts are available to jobs in the same workflow run and removed when the listener finishes. Artifacts with persist: true remain under .jetci/.artifacts/persistent for later local runs. Both local-process and Docker jobs use the same artifact store.

Cache

Manual runs reuse durable memo and job-result cache entries by default. Pass --clear-cache to ignore existing entries and replace them with newly computed results. Pass --no-cache to ignore existing entries without saving new results:

pnpm dlx @jetci/cli run <event-key> --clear-cache
pnpm dlx @jetci/cli run <event-key> --no-cache

IDE and other structured clients can request the multiplexed event stream:

pnpm dlx @jetci/cli run <event-key> --event-stream=json-lines

This keeps one provider stdout while identifying orchestrator and worker lifecycle/output records separately. SDK Local automatically attributes console.log, console.error, direct stdout/stderr writes, and ctx.run() child output to the active worker. Normal CLI runs remain human-readable and do not enable event framing.

Structured worker events also carry repository-relative source locations when Node can resolve the workflow source map. On failure, Local reports the innermost failing JobContext operation (ctx.run, ctx.checkout, artifact operations, or ctx.withCache). An outer context helper preserves a location already attached by an inner helper, allowing IDE clients to navigate to the actual failed CI line without coupling themselves to SDK Local. Each context call also has an operation-started/operation-finished lifecycle, allowing test-style clients to show ctx.* calls beneath their worker and attach output and navigation before a failure occurs.

IDE integrations can discover the locally configured listeners and jobs through the versioned JSON protocol:

pnpm dlx @jetci/cli inspect

The response is emitted on a line prefixed with JETCI_INSPECTION_JSON= and contains protocolVersion, configurations, and jobs. Manual-trigger configurations include runnable: true and their manualKey; other event listeners remain visible but cannot be invoked manually. Consumers should ignore other output and read the last prefixed line, allowing discovery diagnostics to remain human-readable and backwards compatible.

Enabled first- and third-party providers can be discovered without executing them:

pnpm dlx @jetci/cli providers

The last JETCI_PROVIDERS_JSON= line contains validated static provider manifests for the package specifiers explicitly listed in .jetci/package.json. See the provider protocol for the manifest, trust, inspection, and execution contracts.

ctx.withCache() restores paths from the newest immutable raw-directory generation before running the callback. New cache data is prepared in a staging directory and published with an atomic rename only when the configured save rule allows it.

await ctx.withCache({
  key: "pnpm-node-modules",
  paths: ["node_modules"],
  restoreKeys: ["pnpm-"],
  save: "on-success",
}, async () => {
  await ctx.run("pnpm install --frozen-lockfile")
  await ctx.run("pnpm -r build")
})

Cache paths must be relative to the job workspace. restoreKeys are prefix fallbacks, with the newest matching generation restored first. Each cache key retains its two newest generations. Per-key filesystem locks protect concurrent restores, publication, and cleanup. A failed save: "on-success" callback never publishes its staging directory, so the previous good generation remains intact. save: "always" still publishes callback output after failure. Existing .tar.gz and legacy directory caches remain readable as compatibility fallbacks, but all new saves use raw generations under .jetci/.cache/<key>.cache/generations.