npm package: @log10/everest-sdk
Description: TypeScript SDK for the Everest API
Latest version: 0.1.0

# @log10/everest-sdk

TypeScript SDK for the Everest API. Provides first-class support for uploading
input documents and downloading report exports — the two operations that are
most painful to do directly against the raw GraphQL API.

## Requirements

- Node.js 18+ (uses native `fetch` and `FormData`)
- A service account API key (starts with `sk_live_`)

## Installation

```bash
npm install @log10/everest-sdk
# or
pnpm add @log10/everest-sdk
```

## Quick start

```ts
import { EverestClient } from '@log10/everest-sdk';
import { readFile, writeFile } from 'fs/promises';

const client = new EverestClient({
  apiKey: process.env.EVEREST_API_KEY!,
  workspaceId: process.env.EVEREST_WORKSPACE_ID!,
});

// Upload a file
const buf = await readFile('./data.pdf');
const [doc] = await client.files.upload([{ name: 'data.pdf', content: buf, type: 'application/pdf' }]);
console.log('Uploaded:', doc.id);

// Download a report as PDF
const reports = await client.reports.list();
const latest = reports[0];
const gen = latest.latestGeneration!;
// You need both the generation ID and a revision ID.
// Revisions are listed via the full report query — see "Advanced" below.
const pdf = await client.reports.download(gen.id, '<revision-id>', { format: 'pdf' });
await writeFile('./report.pdf', pdf);
```

## Authentication

Create a service account in the Everest workspace settings and copy its API key.
Store the key in an environment variable — never hard-code it.

```ts
const client = new EverestClient({
  apiKey: process.env.EVEREST_API_KEY!,   // sk_live_...
  workspaceId: process.env.EVEREST_WORKSPACE_ID!,
});
```

## Files

### Upload

Upload one or more files. Returns the created `InputDocument` records.

```ts
// From a Buffer (Node.js)
import { readFile } from 'fs/promises';
const buf = await readFile('./report.pdf');
const [doc] = await client.files.upload([
  { name: 'report.pdf', content: buf, type: 'application/pdf' },
]);

// Multiple files at once
const docs = await client.files.upload([
  { name: 'q1.pdf', content: q1Buf },
  { name: 'q2.pdf', content: q2Buf },
]);

// From a browser File object
const [doc] = await client.files.upload([fileInputElement.files[0]]);

// Attach tags on upload
const [doc] = await client.files.upload([{ name: 'data.csv', content: buf }], {
  tagIds: ['tag_abc'],
});
```

### List

```ts
// All files
const files = await client.files.list();

// Filter by name
const reports = await client.files.list({ name: 'Q4' });

// Sort
const newest = await client.files.list({ sort: 'createdAt', order: 'desc' });
```

### Download

```ts
// Get a short-lived signed URL (valid for 10 minutes)
const url = await client.files.getDownloadUrl('doc_abc');

// Or download directly to a Uint8Array
const bytes = await client.files.download('doc_abc');
await writeFile('./downloaded.pdf', bytes);
```

### Delete

```ts
await client.files.delete(['doc_abc', 'doc_def']);
```

## Reports

### List

```ts
const reports = await client.reports.list();

// Search by name
const q4 = await client.reports.list({ search: 'Q4 2024' });

// Filter by template
const filtered = await client.reports.list({ templateIds: ['tmpl_abc'] });
```

### Download as PDF or Markdown

`getDownloadUrl` returns a 10-minute signed URL. `download` fetches it and
returns a `Uint8Array`.

```ts
// Get a signed URL
const url = await client.reports.getDownloadUrl(generationId, revisionId, { format: 'pdf' });

// Download directly
const pdfBytes = await client.reports.download(generationId, revisionId, { format: 'pdf' });
const mdBytes  = await client.reports.download(generationId, revisionId, { format: 'md' });

await writeFile('./report.pdf', pdfBytes);
```

### Export as DOCX

DOCX export is an asynchronous server-side render job. The SDK starts the job,
polls until it completes, and returns the file bytes.

You must supply the report content as Markdown. The easiest way is to download
it first with `format: 'md'`:

```ts
// 1. Get the markdown content of the revision
const mdUrl = await client.reports.getDownloadUrl(generationId, revisionId, { format: 'md' });
const markdown = await fetch(mdUrl).then((r) => r.text());

// 2. Export as DOCX (SDK handles polling automatically)
const docxBytes = await client.reports.exportAsDocx(generationId, markdown);
await writeFile('./report.docx', docxBytes);
```

Optional formatting options:

```ts
const docxBytes = await client.reports.exportAsDocx(generationId, markdown, {
  includeToc: true,          // table of contents
  includeTof: true,          // table of figures
  includeTot: true,          // table of tables
  autoNumberHeadings: true,  // 1.1, 1.2, ...
  stripHeadingNumbering: false,
  sectionCaptions: true,
  tabCaptionSeparator: true,
  styleTemplateOverrideId: 'style_xyz',
});
```

## Advanced: finding revision IDs

The `download` and `getDownloadUrl` methods require a `reportGenerationRevisionId`.
Revisions are nested under report generations. Until the SDK exposes a dedicated
query for this, you can reach them via a raw GraphQL query:

```ts
// This is a workaround until a first-class revisions API is added.
// Fetch the full report with its revisions and pick the latest.
const reports = await client.reports.list();
// latestGeneration is included, but revisions require a deeper query.
// For now, store revision IDs from the Everest UI or use the revisions
// returned by your report generation workflow.
```

The SDK will add a `client.reports.getRevisions(generationId)` helper in a
future release.

## Error handling

All methods throw `EverestAPIError` for GraphQL-level errors:

```ts
import { EverestAPIError } from '@log10/everest-sdk';

try {
  await client.files.upload([{ name: 'data.pdf', content: buf }]);
} catch (err) {
  if (err instanceof EverestAPIError) {
    console.error('API error:', err.message);
    console.error('Details:', err.errors);
  }
}
```

## Configuration

```ts
const client = new EverestClient({
  apiKey: 'sk_live_...',
  workspaceId: 'ws_...',

  // Optional
  baseUrl: 'https://api.log10.io',   // defaults to production
  exportTimeoutMs: 300_000,          // 5 min — how long to wait for DOCX exports
  exportPollIntervalMs: 2_000,       // 2 s — how often to poll for export status
});
```

## Complete example: upload → export

```ts
import { EverestClient } from '@log10/everest-sdk';
import { readFile, writeFile } from 'fs/promises';

const client = new EverestClient({
  apiKey: process.env.EVEREST_API_KEY!,
  workspaceId: process.env.EVEREST_WORKSPACE_ID!,
});

async function main() {
  // 1. Upload source documents
  const rawData = await readFile('./raw-data.pdf');
  const [uploadedDoc] = await client.files.upload([
    { name: 'raw-data.pdf', content: rawData, type: 'application/pdf' },
  ]);
  console.log(`Uploaded: ${uploadedDoc.id}`);

  // 2. List reports and find the one we want to export
  const reports = await client.reports.list({ search: 'Monthly Report' });
  const report = reports[0];
  if (!report?.latestGeneration?.completedAt) {
    throw new Error('No completed generation found');
  }

  const generationId = report.latestGeneration.id;
  // revisionId comes from your report generation workflow
  const revisionId = '<revision-id>';

  // 3. Download as PDF
  const pdf = await client.reports.download(generationId, revisionId, { format: 'pdf' });
  await writeFile('./report.pdf', pdf);
  console.log('PDF saved.');

  // 4. Export as DOCX
  const mdUrl = await client.reports.getDownloadUrl(generationId, revisionId, { format: 'md' });
  const markdown = await fetch(mdUrl).then((r) => r.text());

  const docx = await client.reports.exportAsDocx(generationId, markdown, { includeToc: true });
  await writeFile('./report.docx', docx);
  console.log('DOCX saved.');
}

main().catch(console.error);
```