Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Documentation

# The Docusaurus site in docs/ is published to GitHub Pages at https://imapkit.com/. Pull requests that change it
# only build it (broken links fail the build), pushes to master build and deploy.
on:
push:
branches:
- master
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
workflow_dispatch:

permissions:
contents: read

# a newer push supersedes the build of a pull request, a deployment from master always runs to completion
concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
name: Build
runs-on: ubuntu-24.04
timeout-minutes: 15
defaults:
run:
working-directory: docs
steps:
- uses: actions/checkout@v6
- name: Use Node.js 24
uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: docs/package-lock.json
- run: npm ci
- name: Type check
run: npm run typecheck
- name: Build website
run: npm run build
- name: Upload artifact
if: github.ref == 'refs/heads/master'
uses: actions/upload-pages-artifact@v5
with:
path: docs/build

deploy:
name: Deploy
if: github.ref == 'refs/heads/master'
needs: build
runs-on: ubuntu-24.04
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ CHANGELOG.md
.claude
dist
src/cert.ts
# the Docusaurus site: dependencies, build output and generated files
docs/node_modules
docs/build
docs/.docusaurus
docs/package-lock.json
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ ESLint (`eslint.config.js`, with typescript-eslint) enforces `const`/`let` (no `
- Shared types live in `src/types.ts` (`Message`, `Mailbox`, `ParsedCommand`, `CommandHandler`, `Plugin`, the hook types ...). `IMAPServer` and `IMAPConnection` have an index signature for the state plugins keep on them, plus `declare`d fields for everything the core uses. IMAP attribute trees (parsed commands, responses) use the open `Attribute` alias.
- The package root (`src/index.ts`) default-exports the `imapkit(options)` factory, which also carries `TAG_REGEX`, `IMAPServer` and `IMAPConnection` as properties, and exports the types. `package.json` `exports` maps `.`, `./lib/server` and `./lib/*` to both builds, so the old deep imports (`imapkit/lib/plugins/idle`) keep working; `test/package.test.ts` checks the shapes.

## Documentation site

`docs/` is the Docusaurus site published at https://imapkit.com/ (GitHub Pages, site root, `baseUrl: '/'`), a project of its own with its own `package.json` and lockfile: run `npm install`, `npm start`, `npm run build` and `npm run typecheck` inside `docs/`. The pages are in `docs/docs/` (served under `/docs/`), the homepage is `docs/src/pages/index.tsx`, `docs/static/CNAME` holds the domain. `.github/workflows/docs.yml` builds the site for pull requests that touch `docs/` (broken links fail the build) and deploys it from master. The npm package only ships `bin`, `cert` and `dist` (the `files` field), so `docs/` never reaches npm; root ESLint ignores `docs/`, Prettier formats its sources, and release-please excludes it (`exclude-paths`), so use `docs:` commits for documentation changes. Verify every claim in the docs against `src/` and the README.

## Releases

Releases are automated with release-please (`release-please-config.json`, `.release-please-manifest.json`): use Conventional Commit messages (`fix:`, `feat:`, `chore:` ...) on master, merge the release PR it opens, and `.github/workflows/release.yaml` waits for the `test.yml` run on that commit and then publishes to npm through trusted publishing (OIDC, no token). Do not bump `version` in package.json by hand.
Expand Down
20 changes: 20 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
12 changes: 12 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# ImapKit documentation

The Docusaurus site published at [imapkit.com](https://imapkit.com/). The pages are in `docs/`, the homepage in `src/pages/index.tsx`.

```bash
npm install
npm start # dev server with hot reload
npm run build # static site in build/, broken links fail the build
npm run typecheck
```

`.github/workflows/docs.yml` in the repository root builds the site for pull requests that change `docs/` and deploys it to GitHub Pages from master. There is no manual deploy.
38 changes: 38 additions & 0 deletions docs/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
sidebar_position: 1
title: Installation
---

# Installation

ImapKit needs Node.js 20 or newer. It also runs on the latest [Bun](https://bun.sh/) and [Deno](https://deno.com/) releases (`npm:imapkit` in Deno).

## As a library

```bash
npm install --save-dev imapkit
```

The package ships ES modules and CommonJS, each with type declarations:

```javascript
import imapkit from 'imapkit';
// or with CommonJS: const imapkit = require('imapkit');
```

## As a standalone server

```bash
npm install -g imapkit
imapkit -p 1143
```

Point your IMAP client to `localhost:1143` and log in with user name `testuser` and password `testpass`. Run `imapkit --help` to see all options.

## Optional SMTP listener

The SMTP listener (`--smtpPort`, or the `smtp` option) appends every message it receives to INBOX. It needs the [smtp-server](https://www.npmjs.com/package/smtp-server) package, which is not installed with ImapKit:

```bash
npm install smtp-server
```
31 changes: 31 additions & 0 deletions docs/docs/getting-started/quick-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
sidebar_position: 2
title: Quick Start
---

# Quick Start

Start a server on a free port, connect your client, and change the server state from the test:

```javascript title="test/client.test.js"
import { test } from 'node:test';
import assert from 'node:assert';
import imapkit from 'imapkit';

test('client sees a new message', async () => {
const server = imapkit({ plugins: ['IDLE'] });
const port = await server.start();

// ... connect your IMAP client to 127.0.0.1:port as testuser / testpass

const { uid } = server.control.addMessage('INBOX', {
raw: 'Subject: hello\r\n\r\nHi!\r\n',
flags: ['\\Seen']
});
assert.strictEqual(uid, 1);

await server.stop();
});
```

Every server starts from the `storage` option, or from an empty INBOX, so tests never depend on each other.
20 changes: 20 additions & 0 deletions docs/docs/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
slug: /
sidebar_position: 1
title: Introduction
---

# ImapKit

ImapKit is a scriptable, in-memory IMAP server for testing IMAP clients. It implements IMAP4rev1 ([RFC 3501](https://www.rfc-editor.org/rfc/rfc3501)) and, as an optional plugin, IMAP4rev2 ([RFC 9051](https://www.rfc-editor.org/rfc/rfc9051)), with more than 50 extensions that can be turned on and off per server instance.

Nothing is ever written to disk: the mailbox tree comes from a JSON object, so every new server starts from the same known state.

## Why ImapKit

- **Strict by design.** ImapKit answers client input that breaks the RFCs with `BAD` or `NO`, so client bugs show up in your test suite instead of in production.
- **Control from your tests.** The control API (`server.control`) adds messages, changes flags, resets UIDVALIDITY and disconnects sessions while clients are connected, and every change reaches the sessions the way a change by another client would.
- **Any language.** The optional REST API exposes the same operations over HTTP, with server events as Server-Sent Events.
- **Scripted faults.** Script rules and quirk presets make the server misbehave on purpose, like real servers do: late responses, literals everywhere, throttling, split output, dropped connections.

ImapKit is maintained by the team behind [EmailEngine](https://emailengine.app/?utm_source=imapkit.com&utm_medium=docs&utm_campaign=oss-docs) and [ImapFlow](https://imapflow.com/).
137 changes: 137 additions & 0 deletions docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { themes as prismThemes } from 'prism-react-renderer';
import type { Config } from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';

// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)

const config: Config = {
title: 'ImapKit',
tagline: 'A scriptable, in-memory IMAP server for testing IMAP clients',
favicon: 'img/logo.svg',

future: {
v4: true
},

// Served from GitHub Pages at the root of the imapkit.com domain
url: 'https://imapkit.com',
baseUrl: '/',

organizationName: 'postalsys',
projectName: 'imapkit',

onBrokenLinks: 'throw',

i18n: {
defaultLocale: 'en',
locales: ['en']
},

presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
// the site lives in the docs/ folder of the imapkit repository, the pages in docs/docs/
editUrl: 'https://github.com/postalsys/imapkit/tree/master/docs/'
},
blog: false,
theme: {
customCss: './src/css/custom.css'
}
} satisfies Preset.Options
]
],

themeConfig: {
colorMode: {
respectPrefersColorScheme: true
},
navbar: {
title: 'ImapKit',
logo: {
alt: 'ImapKit Logo',
src: 'img/logo.svg'
},
items: [
{
type: 'docSidebar',
sidebarId: 'docsSidebar',
position: 'left',
label: 'Documentation'
},
{
href: 'https://github.com/postalsys/imapkit',
label: 'GitHub',
position: 'right'
},
{
href: 'https://www.npmjs.com/package/imapkit',
label: 'npm',
position: 'right'
}
]
},
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [
{
label: 'Introduction',
to: '/docs/'
},
{
label: 'Quick Start',
to: '/docs/getting-started/quick-start'
}
]
},
{
title: 'Community',
items: [
{
label: 'GitHub Issues',
href: 'https://github.com/postalsys/imapkit/issues'
},
{
label: 'npm',
href: 'https://www.npmjs.com/package/imapkit'
}
]
},
{
title: 'Ecosystem',
items: [
{
label: 'EmailEngine',
href: 'https://emailengine.app/?utm_source=imapkit.com&utm_medium=footer&utm_campaign=oss-docs'
},
{
label: 'ImapFlow',
href: 'https://imapflow.com/'
},
{
label: 'Nodemailer',
href: 'https://nodemailer.com/'
},
{
label: 'Ethereal',
href: 'https://ethereal.email/'
}
]
}
],
copyright: `Copyright 漏 ${new Date().getFullYear()} Postal Systems O脺. Licensed under MIT.`
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ['bash']
}
} satisfies Preset.ThemeConfig
};

export default config;
Loading