Skip to content

TypeScript API Reference

Complete reference for gaji's TypeScript API.

Core Classes

Workflow

Represents a GitHub Actions workflow.

typescript
class Workflow {
  constructor(config: WorkflowConfig)
  addJob(id: string, job: Job | CompositeJob | CallJob): this
  static fromObject(def: WorkflowDefinition, id?: string): Workflow
  build(filename?: string): void
  toJSON(): WorkflowDefinition
}
MethodDescription
addJob(id, job)Add a job to the workflow. Accepts Job, CompositeJob, or CallJob.
fromObject(def, id?)Create a Workflow from a raw WorkflowDefinition object. Useful for wrapping existing YAML-like definitions.
build(filename?)Compile and output the workflow as YAML.
toJSON()Serialize to a WorkflowDefinition object.

WorkflowConfig

typescript
interface WorkflowConfig {
  name: string
  on: WorkflowTriggers
  env?: Record<string, string>
  permissions?: WorkflowPermissions
  concurrency?: WorkflowConcurrency
}

Example

typescript
const workflow = new Workflow({
  name: "CI",
  on: {
    push: { branches: ["main"] },
    pull_request: { branches: ["main"] },
  },
  env: {
    NODE_ENV: "production",
  },
})
  .addJob("test", testJob)
  .addJob("build", buildJob);

workflow.build("ci");

Workflow.fromObject() Example

typescript
const workflow = Workflow.fromObject({
  name: "Raw Workflow",
  on: { push: {} },
  jobs: {
    test: {
      "runs-on": "ubuntu-latest",
      steps: [{ run: "echo hello" }],
    },
  },
});

workflow.build("raw");

Job

Represents a job in a workflow.

typescript
class Job {
  constructor(runsOn: string | string[], options?: Partial<JobDefinition>)
  addStep(step: Step): this
  needs(jobs: string | string[]): this
  env(variables: Record<string, string>): this
  when(condition: string): this
  permissions(perms: Permissions): this
  outputs(outputs: Record<string, string>): this
  strategy(strategy: JobStrategy): this
  continueOnError(v: boolean): this
  timeoutMinutes(m: number): this
  toJSON(): JobDefinition
}
MethodDescription
addStep(step)Append a step to the job.
needs(jobs)Set job dependencies.
env(variables)Set environment variables.
when(condition)Set the job's if condition (e.g., "github.ref == 'refs/heads/main'").
permissions(perms)Set job-level permissions (e.g., { contents: 'read' }).
outputs(outputs)Define job outputs.
strategy(strategy)Set matrix strategy.
continueOnError(v)Set the continue-on-error flag.
timeoutMinutes(m)Set the timeout-minutes value.
toJSON()Serialize to a JobDefinition object.

The optional options parameter in the constructor allows setting all job options at once:

typescript
const job = new Job("ubuntu-latest", {
  needs: ["test"],
  env: { NODE_ENV: "production" },
  "timeout-minutes": 30,
});

Example

typescript
const job = new Job("ubuntu-latest")
  .needs(["test"])
  .env({
    NODE_ENV: "production",
  })
  .when("github.event_name == 'push'")
  .permissions({ contents: "read" })
  .strategy({
    matrix: {
      node: ["18", "20", "22"],
    },
  })
  .outputs({
    version: "${{ steps.version.outputs.value }}",
  })
  .continueOnError(false)
  .timeoutMinutes(30)
  .addStep(checkout({}))
  .addStep({ run: "npm test" });

CompositeAction

Create reusable composite actions.

typescript
class CompositeAction {
  constructor(config: CompositeActionConfig)
  addStep(step: Step): this
  build(filename: string): void
}

CompositeActionConfig

typescript
interface CompositeActionConfig {
  name: string
  description: string
  inputs?: Record<string, ActionInput>
  outputs?: Record<string, ActionOutput>
}

Example

typescript
import { CompositeAction } from "../generated/index.js";

const setupEnv = new CompositeAction({
  name: "Setup Environment",
  description: "Setup Node.js and install dependencies",
  inputs: {
    "node-version": {
      description: "Node.js version",
      required: true,
      default: "20",
    },
  },
})
  .addStep(checkout({}))
  .addStep(setupNode({
    with: {
      "node-version": "${{ inputs.node-version }}",
    },
  }))
  .addStep({
    run: "npm ci",
  });

setupEnv.build("setup-env");

This generates action.yml that can be used like:

typescript
// In another workflow
const setupEnv = getAction("./setup-env");

const job = new Job("ubuntu-latest")
  .addStep(setupEnv({
    with: {
      "node-version": "20",
    },
  }));

JavaScriptAction

Create Node.js-based GitHub Actions.

typescript
class JavaScriptAction {
  constructor(config: JavaScriptActionConfig, runs: JavaScriptActionRuns)
  build(filename: string): void
}

JavaScriptActionConfig

typescript
interface JavaScriptActionConfig {
  name: string
  description: string
  inputs?: Record<string, ActionInputDefinition>
  outputs?: Record<string, ActionOutputDefinition>
}

JavaScriptActionRuns

typescript
interface JavaScriptActionRuns {
  using: 'node12' | 'node16' | 'node20'
  main: string
  pre?: string
  post?: string
  'pre-if'?: string
  'post-if'?: string
}

Example

typescript
import { JavaScriptAction } from "../generated/index.js";

const action = new JavaScriptAction(
  {
    name: "Hello World",
    description: "Greet someone and record the time",
    inputs: {
      "who-to-greet": {
        description: "Who to greet",
        required: true,
        default: "World",
      },
    },
    outputs: {
      time: {
        description: "The time we greeted you",
      },
    },
  },
  {
    using: "node20",
    main: "dist/index.js",
  },
);

action.build("hello-world");

This generates .github/actions/hello-world/action.yml.

Use CallAction.from() to reference it in a workflow:

typescript
const step = {
  id: "hello",
  ...CallAction.from(action).toJSON(),
  with: { "who-to-greet": "Mona the Octocat" },
};

CompositeJob

Create reusable job templates via TypeScript class inheritance. CompositeJob extends Job, so all Job methods are available.

typescript
class CompositeJob extends Job {
  constructor(runsOn: string | string[], options?: Partial<JobDefinition>)
}

Unlike Job, CompositeJob is designed to be subclassed with extends to create domain-specific, parameterized job templates. It produces the same YAML output as a regular Job.

CompositeJob vs Job

Use Job directly for one-off jobs. Use CompositeJob when you want to create a reusable class that encapsulates a common job pattern with parameters.

Example

typescript
import { CompositeJob } from "../generated/index.js";

// Define a reusable job template
class NodeTestJob extends CompositeJob {
  constructor(nodeVersion: string) {
    super("ubuntu-latest");

    this
      .addStep(checkout({}))
      .addStep(setupNode({
        with: {
          "node-version": nodeVersion,
        },
      }))
      .addStep({ run: "npm ci" })
      .addStep({ run: "npm test" });
  }
}

// Use in workflows
const workflow = new Workflow({
  name: "Test Matrix",
  on: { push: { branches: ["main"] } },
})
  .addJob("test-node-18", new NodeTestJob("18"))
  .addJob("test-node-20", new NodeTestJob("20"))
  .addJob("test-node-22", new NodeTestJob("22"));

You can also create more complex reusable jobs:

typescript
class DeployJob extends CompositeJob {
  constructor(environment: "staging" | "production") {
    super("ubuntu-latest");

    this
      .env({
        ENVIRONMENT: environment,
        API_URL: environment === "production"
          ? "https://api.example.com"
          : "https://staging.api.example.com",
      })
      .addStep(checkout({}))
      .addStep(setupNode({ with: { "node-version": "20" } }))
      .addStep({
        name: "Deploy",
        run: `npm run deploy:${environment}`,
        env: {
          DEPLOY_TOKEN: "${{ secrets.DEPLOY_TOKEN }}",
        },
      });
  }
}

// Use in workflow
const workflow = new Workflow({
  name: "Deploy",
  on: { push: { tags: ["v*"] } },
})
  .addJob("deploy-staging", new DeployJob("staging"))
  .addJob("deploy-production", new DeployJob("production").needs(["deploy-staging"]));

CallJob

Call a reusable workflow defined in another repository or file. Unlike Job, a CallJob has no steps — it delegates entirely to the referenced workflow via uses.

typescript
class CallJob {
  constructor(uses: string)
  with(inputs: Record<string, unknown>): this
  secrets(s: Record<string, unknown> | 'inherit'): this
  needs(deps: string | string[]): this
  when(condition: string): this
  permissions(perms: Permissions): this
  toJSON(): object
}
MethodDescription
with(inputs)Pass inputs to the reusable workflow.
secrets(s)Pass secrets explicitly, or use 'inherit' to forward all secrets.
needs(deps)Set job dependencies.
when(condition)Set the job's if condition.
permissions(perms)Set job-level permissions.

Example

typescript
import { CallJob, Workflow } from "../generated/index.js";

const deploy = new CallJob("octo-org/deploy/.github/workflows/deploy.yml@main")
  .with({ environment: "production" })
  .secrets("inherit")
  .needs(["build"]);

const workflow = new Workflow({
  name: "Release",
  on: { push: { tags: ["v*"] } },
})
  .addJob("deploy", deploy);

workflow.build("release");

Generated YAML:

yaml
jobs:
  deploy:
    uses: octo-org/deploy/.github/workflows/deploy.yml@main
    with:
      environment: production
    secrets: inherit
    needs:
      - build

CallAction

Reference a local composite or JavaScript action built by gaji, for use as a step in a job.

typescript
class CallAction {
  constructor(uses: string)
  static from(action: CompositeAction | JavaScriptAction): CallAction
  toJSON(): Step
}
MethodDescription
from(action)Create a CallAction from a CompositeAction or JavaScriptAction instance. Automatically resolves the .github/actions/<id> path.

Example

typescript
import { CompositeAction, CallAction, Job } from "../generated/index.js";

const setupEnv = new CompositeAction({
  name: "Setup",
  description: "Setup environment",
});
setupEnv.build("setup-env");

const job = new Job("ubuntu-latest")
  .addStep({
    ...CallAction.from(setupEnv).toJSON(),
    with: { "node-version": "20" },
  });

Functions

getAction()

Get a typed action function.

typescript
function getAction<T extends string>(
  ref: T
): (config?: ActionConfig) => Step

Example

typescript
const checkout = getAction("actions/checkout@v4");
const setupNode = getAction("actions/setup-node@v4");

// Use with full type safety
const step = checkout({
  name: "Checkout code",
  with: {
    // ✅ Autocomplete available!
    repository: "owner/repo",
    ref: "main",
    "fetch-depth": 0,
  },
});

Type Definitions

Step

A workflow step.

typescript
interface Step {
  name?: string
  id?: string
  if?: string
  uses?: string
  with?: Record<string, string | number | boolean>
  run?: string
  env?: Record<string, string>
  "continue-on-error"?: boolean
  "timeout-minutes"?: number
}

WorkflowTriggers

Workflow trigger events.

typescript
interface WorkflowTriggers {
  push?: PushTrigger
  pull_request?: PullRequestTrigger
  schedule?: ScheduleTrigger[]
  workflow_dispatch?: WorkflowDispatchTrigger
  [key: string]: any
}

interface PushTrigger {
  branches?: string[]
  tags?: string[]
  paths?: string[]
}

interface PullRequestTrigger {
  branches?: string[]
  types?: string[]
  paths?: string[]
}

interface ScheduleTrigger {
  cron: string
}

JobStrategy

Job matrix strategy.

typescript
interface JobStrategy {
  matrix?: {
    [key: string]: string[] | number[]
  }
  "fail-fast"?: boolean
  "max-parallel"?: number
}

ActionInput

Action input definition (for CompositeAction).

typescript
interface ActionInput {
  description: string
  required?: boolean
  default?: string
}

ActionOutput

Action output definition (for CompositeAction).

typescript
interface ActionOutput {
  description: string
  value: string
}

Examples

Complete Workflow

typescript
import { getAction, Job, Workflow } from "../generated/index.js";

const checkout = getAction("actions/checkout@v4");
const setupNode = getAction("actions/setup-node@v4");

const test = new Job("ubuntu-latest")
  .addStep(checkout({}))
  .addStep(setupNode({ with: { "node-version": "20" } }))
  .addStep({ run: "npm ci" })
  .addStep({ run: "npm test" });

const build = new Job("ubuntu-latest")
  .needs(["test"])
  .addStep(checkout({}))
  .addStep(setupNode({ with: { "node-version": "20" } }))
  .addStep({ run: "npm ci" })
  .addStep({ run: "npm run build" });

const workflow = new Workflow({
  name: "CI",
  on: {
    push: { branches: ["main"] },
    pull_request: { branches: ["main"] },
  },
})
  .addJob("test", test)
  .addJob("build", build);

workflow.build("ci");

Next Steps

Released under the MIT License.