Skip to main content
The Mesa SDK provides a type-safe TypeScript client for interacting with the Depot API. It offers an intuitive, resource-based interface with built-in pagination, retries, and error handling. The SDK is great for programmatic version control.

Installation

npm install @mesadev/sdk
# or
pnpm add @mesadev/sdk
# or
bun add @mesadev/sdk

Quick Start

import { Mesa } from "@mesadev/sdk";

const mesa = new Mesa({
  apiKey: process.env.MESA_API_KEY,
});

// Create a repository
const repo = await mesa.repos.create({
  org: "my-org",
  body: { name: "my-repo" },
});

// List all repositories
const { result } = await mesa.repos.list({ org: "my-org" });
console.log(result.repos);

Configuration

The SDK accepts the following configuration options:
const mesa = new Mesa({
  // Required: Your API key for authentication
  apiKey: "your-api-key",

  // Optional: Override the default server URL
  serverURL: "https://api.mesa.dev",

  // Optional: Default timeout for requests (in milliseconds)
  timeoutMs: 30000,

  // Optional: Retry configuration
  retryConfig: {
    strategy: "backoff",
    backoff: {
      initialInterval: 500,
      maxInterval: 60000,
      exponent: 1.5,
      maxElapsedTime: 300000,
    },
  },
});

Environment Variables

The SDK automatically reads from these environment variables if not explicitly configured:
  • MESA_API_KEY - Your API key

Resources

The SDK organizes methods into resource namespaces that mirror the API structure.

Repositories

// Create a repository
const repo = await mesa.repos.create({
  org: "my-org",
  body: {
    name: "my-repo",
    defaultBranch: "main", // optional, defaults to "main"
  },
});

// List repositories (paginated)
const { result } = await mesa.repos.list({
  org: "my-org",
  limit: 50,      // optional
  cursor: "...",  // optional, for pagination
});

// Get a specific repository
const repo = await mesa.repos.get({
  org: "my-org",
  repo: "my-repo",
});

// Update a repository
const updated = await mesa.repos.update({
  org: "my-org",
  repo: "my-repo",
  body: { name: "new-name" },
});

// Delete a repository
await mesa.repos.delete({
  org: "my-org",
  repo: "my-repo",
});

Branches

// List branches (paginated)
const { result } = await mesa.branches.list({
  org: "my-org",
  repo: "my-repo",
});

// Create a branch
const branch = await mesa.branches.create({
  org: "my-org",
  repo: "my-repo",
  body: {
    name: "feature-branch",
    headSha: "abc123...",  // SHA to branch from
  },
});

// Delete a branch
await mesa.branches.delete({
  org: "my-org",
  repo: "my-repo",
  branch: "feature-branch",
});

Commits

// List commits (paginated)
const { result } = await mesa.commits.list({
  org: "my-org",
  repo: "my-repo",
  ref: "main",  // optional, branch or tag
});

// Get a specific commit
const commit = await mesa.commits.get({
  org: "my-org",
  repo: "my-repo",
  sha: "abc123...",
});

// Create a commit programmatically
const commit = await mesa.commits.create({
  org: "my-org",
  repo: "my-repo",
  body: {
    branch: "main",
    message: "Add new file",
    author: {
      name: "John Doe",
      email: "john@example.com",
    },
    edits: [
      {
        path: "README.md",
        action: "create",
        content: "# Hello World",
      },
    ],
  },
});

Content

// Get file content (returns base64-encoded content)
const file = await mesa.content.get({
  org: "my-org",
  repo: "my-repo",
  path: "src/index.ts",
  ref: "main",  // optional
});

// Get directory listing
const dir = await mesa.content.get({
  org: "my-org",
  repo: "my-repo",
  path: "src",
  ref: "main",
});

// The response type depends on whether the path is a file or directory
if (file.type === "file") {
  console.log(atob(file.content));  // Decode base64 content
} else {
  console.log(file.entries);  // Array of directory entries
}

Diffs

// Get diff between two refs
const diff = await mesa.diffs.get({
  org: "my-org",
  repo: "my-repo",
  base: "main",
  head: "feature-branch",
});

// Response includes file changes
for (const file of diff.files) {
  console.log(`${file.status}: ${file.path}`);
}

Merge

// Merge a branch into another (fast-forward when possible)
const result = await mesa.merge.perform({
  org: "my-org",
  repo: "my-repo",
  base: "main",
  body: {
    head: "feature-branch",
    author: {
      name: "John Doe",
      email: "john@example.com",
    },
  },
});

console.log(result.mergeType); // "fast_forward" or "merge_commit"
console.log(result.sha);       // Resulting commit SHA
// Merge with a custom commit message
const result = await mesa.merge.perform({
  org: "my-org",
  repo: "my-repo",
  base: "main",
  body: {
    head: "feature-branch",
    message: "Ship feature X",
    author: {
      name: "John Doe",
      email: "john@example.com",
    },
  },
});
// Merge and delete the head branch afterward
const result = await mesa.merge.perform({
  org: "my-org",
  repo: "my-repo",
  base: "main",
  body: {
    head: "feature-branch",
    author: {
      name: "John Doe",
      email: "john@example.com",
    },
    deleteBranch: true,
  },
});

console.log(result.branchDeleted); // true
The merge endpoint returns 409 MERGE_CONFLICT when the branches have conflicting changes that cannot be auto-resolved. Catch this error to handle conflicts in your workflow.

Admin (API Keys)

// Create an API key
const key = await mesa.admin.createApiKey({
  org: "my-org",
  body: {
    name: "CI/CD Key",
    scopes: ["git:read", "git:write"],
  },
});
console.log(key.key);  // Only shown once!

// List API keys
const keys = await mesa.admin.listApiKeys({
  org: "my-org",
});

// Revoke an API key
await mesa.admin.revokeApiKey({
  org: "my-org",
  id: "key_123",
});

Pagination

List endpoints return a PageIterator that supports both direct access and async iteration.

Direct Access

// Get first page
const page = await mesa.repos.list({ org: "my-org" });
console.log(page.result.repos);
console.log(page.result.hasMore);
console.log(page.result.nextCursor);

// Get next page
if (page.result.hasMore) {
  const nextPage = await page.next();
}

Async Iteration

// Iterate through all pages
for await (const page of await mesa.repos.list({ org: "my-org" })) {
  for (const repo of page.result.repos) {
    console.log(repo.name);
  }
}

Manual Cursor Pagination

let cursor: string | undefined;

do {
  const { result } = await mesa.repos.list({
    org: "my-org",
    cursor,
    limit: 100,
  });

  for (const repo of result.repos) {
    console.log(repo.name);
  }

  cursor = result.nextCursor ?? undefined;
} while (cursor);

Error Handling

The SDK throws typed errors that you can catch and handle:
import { Mesa } from "@mesadev/sdk";
import * as errors from "mesa/models/errors";

try {
  await mesa.repos.get({ org: "my-org", repo: "nonexistent" });
} catch (error) {
  if (error instanceof errors.GetOrgRepoNotFoundError) {
    console.log("Repository not found");
  } else if (error instanceof errors.GetOrgRepoUnauthorizedError) {
    console.log("Invalid API key");
  } else if (error instanceof errors.GetOrgRepoForbiddenError) {
    console.log("Access denied");
  } else {
    throw error;
  }
}

Error Types

All API errors follow a consistent structure:
{
  code: string;      // Error code (e.g., "NOT_FOUND")
  message: string;   // Human-readable message
  details?: object;  // Additional error details
}

Per-Request Options

You can override configuration on a per-request basis:
const repo = await mesa.repos.get(
  { org: "my-org", repo: "my-repo" },
  {
    // Override timeout for this request
    timeoutMs: 60000,

    // Override retry configuration
    retries: {
      strategy: "backoff",
      backoff: {
        initialInterval: 1000,
        maxInterval: 30000,
        exponent: 2,
        maxElapsedTime: 60000,
      },
    },

    // Add custom headers
    headers: {
      "X-Custom-Header": "value",
    },
  }
);

TypeScript Support

The SDK is fully typed. Import types from the models:
import type {
  PostOrgReposRequest,
  PostOrgReposResponse,
  GetOrgReposRequest,
  Repo,
} from "mesa/models/operations";

// Use types for better IDE support
const request: PostOrgReposRequest = {
  org: "my-org",
  body: { name: "my-repo" },
};

Complete Example

import { Mesa } from "@mesadev/sdk";

async function main() {
  const mesa = new Mesa({
    apiKey: process.env.MESA_API_KEY!,
  });

  // Create a repository
  const repo = await mesa.repos.create({
    org: "my-org",
    body: { name: "example-repo" },
  });
  console.log(`Created repository: ${repo.name}`);

  // Create initial commit
  await mesa.commits.create({
    org: "my-org",
    repo: "example-repo",
    body: {
      branch: "main",
      message: "Initial commit",
      author: {
        name: "Bot",
        email: "bot@example.com",
      },
      edits: [
        {
          path: "README.md",
          action: "create",
          content: "# Example Repository\n\nThis is an example.",
        },
      ],
    },
  });

  // Create a feature branch
  const mainCommits = await mesa.commits.list({
    org: "my-org",
    repo: "example-repo",
    ref: "main",
  });

  await mesa.branches.create({
    org: "my-org",
    repo: "example-repo",
    body: {
      name: "feature",
      headSha: mainCommits.result.commits[0].sha,
    },
  });

  // Make changes on feature branch
  await mesa.commits.create({
    org: "my-org",
    repo: "example-repo",
    body: {
      branch: "feature",
      message: "Add feature",
      author: {
        name: "Bot",
        email: "bot@example.com",
      },
      edits: [
        {
          path: "feature.ts",
          action: "create",
          content: "export const feature = true;",
        },
      ],
    },
  });

  // Get diff between branches
  const diff = await mesa.diffs.get({
    org: "my-org",
    repo: "example-repo",
    base: "main",
    head: "feature",
  });

  console.log(`Changes: ${diff.files.length} files`);

  // Merge feature branch into main and clean up
  const merge = await mesa.merge.perform({
    org: "my-org",
    repo: "example-repo",
    base: "main",
    body: {
      head: "feature",
      author: {
        name: "Bot",
        email: "bot@example.com",
      },
      deleteBranch: true,
    },
  });

  console.log(`Merged via ${merge.mergeType}: ${merge.sha}`);
}

main().catch(console.error);