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
23 changes: 22 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: npm

- run: npm ci
Expand All @@ -49,3 +49,24 @@ jobs:
# Transitive dep vulnerabilities (postcss, sharp, @hono/node-server)
# are pinned via root package.json overrides — see commit history.
- run: npm audit --audit-level=high

# The Astro example's Playwright suite: builds the example, serves it, and
# drives a real browser. Unlike the PokeAPI suite above it makes no outside
# requests (it blocks them), so it runs on every push.
astro-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm

- run: npm ci

- run: npx playwright install --with-deps chromium
working-directory: examples/astro-docs

- run: npm run test:e2e --workspace=@inkform/example-astro-docs

2 changes: 1 addition & 1 deletion .github/workflows/openapi-breaking-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24

- run: npm ci

Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Release

# Publishes @inkform/framework or @inkform/cli to public npm.
# Publishes @inkform/framework, @inkform/cli or @inkform/astro to public npm.
#
# Authentication is npm Trusted Publishing (OIDC) — there is NO npm token
# stored in this repository's secrets. GitHub mints a short-lived OIDC token
Expand All @@ -16,6 +16,7 @@ name: Release
#
# framework-v0.5.0 → publishes packages/framework
# cli-v0.5.0 → publishes packages/cli
# astro-v0.1.0 → publishes packages/astro
#
# The tag's version MUST equal the version already committed in that
# package's package.json — the job refuses to guess. Bump, commit, push,
Expand All @@ -27,13 +28,14 @@ on:
tags:
- 'framework-v*'
- 'cli-v*'
- 'astro-v*'
workflow_dispatch:
inputs:
package:
description: 'Which package to release'
required: true
type: choice
options: [framework, cli]
options: [framework, cli, astro]
dry_run:
description: 'Dry run (pack and validate, publish nothing)'
required: true
Expand Down Expand Up @@ -63,7 +65,7 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 22
node-version: 24
cache: npm
registry-url: 'https://registry.npmjs.org'

Expand Down Expand Up @@ -101,6 +103,7 @@ jobs:
case "$SLUG" in
framework) DIR='packages/framework' ;;
cli) DIR='packages/cli' ;;
astro) DIR='packages/astro' ;;
*) echo "::error::Unrecognized release target '$SLUG'"; exit 1 ;;
esac

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ yarn-error.log*
.turbo/
*.tsbuildinfo
.vercel
.astro/
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,51 @@ All notable changes to this project are documented here. Format loosely
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions
track `packages/framework`'s own `package.json`.

## [Unreleased]

## [0.8.0] — 2026-10-23

### Added

- **`@inkform/astro`** (new package, 0.1.0): an Astro 7 integration that
serves a content folder from any Astro site under one base path: docs pages
from docs.json, the native API Reference, blog, changelog, `llms.txt`, and
Pagefind search. React renders the framework's components at build time
only. See [`packages/astro`](./packages/astro/README.md) and its
[CHANGELOG](./packages/astro/CHANGELOG.md).
- **`@inkform/framework/mdx-options`**: the remark/rehype plugin lists and
next-mdx-remote options `<Mdx>` renders with, plus `compileMdx(source,
components)` for hosts that render MDX to a React element themselves. Unlike
passing the component map straight to compiled MDX, `compileMdx` resolves
every capitalized component the source uses through the map, so a
`mdxComponents()` map's fallback renders unknown components instead of
failing.
- **`@inkform/framework/routes`**: `apiTab(config)`, `apiTabSlug(config)`
(`"API Reference"` → `api-reference`, the rule every theme's `apiBasePath()`
uses), `docRouteSlugs(config)` and `joinHref(base, slug)`. Pure, no IO.
- **`basePath` option for `buildLlmsTxt()`**: prefixes doc, blog and changelog
links for a site that serves its docs under a path (`/docs/quickstart`).
Defaults to `''`, so existing output is unchanged.
- Subpath exports for browser code outside React: `./mermaid/render`
(`renderDiagram`, `themeConfig`), `./openapi-render/try-it` (the Try It
request executor) and `./openapi-render/sample` (`sampleFromSchema`).

### Changed

- `next` is an optional peer dependency. Nothing in the framework imports it,
and npm no longer installs Next.js into a site that only uses the Astro
integration or the framework's loaders.
- `<Mdx>` reads its plugins and options from `./mdx-options`; its output is
unchanged.
- `slugify` lives in a pure `./slug` module; `./content` re-exports it.
- The root `vite` override now applies to `vitest` only (`^7.3.7`), since
Astro 7 needs Vite 8.
- Templates and examples move to Next.js 16.4.2, for the critical advisories
in 16.0.0–16.4.0 (GHSA-2464-fhr4-w9xw, GHSA-673q-mjm6-r5xr) and the Server
Actions denial of service (GHSA-p3m5-59mp-jv5r). Lockfile fixes for
DOMPurify (GHSA-cg2g-xh64-v4q7 and two more) and `ip-address`
(GHSA-rxp7-6hxx-x8q5). `npm audit --audit-level=high` is clean again.

## [0.7.0] — 2026-10-22

### Added
Expand Down
19 changes: 12 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ npm install
packages/
framework/ @inkform/framework — the MDX + OpenAPI engine (published)
cli/ @inkform/cli — the npx scaffolder (published)
astro/ @inkform/astro — Astro 7 integration (published)

templates/
canopy/ @inkform/theme-canopy — dark, green accent (default)
Expand All @@ -52,13 +53,15 @@ examples/
pokeapi-docs/ Full PokéAPI docs site + Playwright e2e tests
markdown-docs/ Markdown/MDX feature reference
inkform-docs/ This framework's own documentation (dogfooded)
astro-docs/ An Astro host app using @inkform/astro + Playwright tests

archive/templates/ Legacy themes (aurora, fern, cedar, mono, base, galley)
preserved for reference — not scaffoldable via CLI
```

Only `@inkform/framework` and `@inkform/cli` are published to npm. Themes and
examples are private workspace packages used for development and demos.
Only `@inkform/framework`, `@inkform/cli` and `@inkform/astro` are published
to npm. Themes and examples are private workspace packages used for
development and demos.

---

Expand All @@ -67,7 +70,7 @@ examples are private workspace packages used for development and demos.
```bash
npm install # install every workspace + run postinstall prune
npm run typecheck # typecheck all packages
npm test # framework unit tests (Vitest)
npm test # framework + Astro integration unit tests (Vitest)
npm run build # next build across every theme + example
npm run build:examples # build the three demo sites only
```
Expand Down Expand Up @@ -100,15 +103,17 @@ cd /tmp/test-docs && npm install && npm run dev
Override the GitHub ref the CLI downloads from with `INKFORM_DOCS_REF=<branch>`
when testing against a fork or feature branch.

**End-to-end tests** (manual — deliberately excluded from CI):
**End-to-end tests:**

```bash
npm run test:e2e --workspace=@inkform/example-pokeapi
npm run test:e2e --workspace=@inkform/example-pokeapi # manual, not in CI
npm run test:e2e --workspace=@inkform/example-astro-docs # also runs in CI
```

The e2e suite makes one real request to the live PokéAPI per run. Fine for a
The PokéAPI suite makes one real request to the live PokéAPI per run. Fine for a
developer running it locally once, but not wired into CI to avoid hammering a
free, community-funded API on every push.
free, community-funded API on every push. The Astro suite builds and serves its
own example and blocks every request that leaves it, so CI runs it.

---

Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ The binary is also available as `inkform-docs` when installed globally.

To embed the engine in an existing Next.js app or build a custom theme from scratch, see [`packages/framework`](./packages/framework) and [`packages/framework/ARCHITECTURE.md`](./packages/framework/ARCHITECTURE.md).

**On Astro?** [`@inkform/astro`](./packages/astro) serves the same content folder from any Astro 7 site, under a path like `/docs`, with no Next.js and no React in the browser:

```ts
// astro.config.ts
import inkform from '@inkform/astro';
export default defineConfig({ integrations: [inkform({ base: '/docs' })] });
```

## Features

| | |
Expand Down Expand Up @@ -106,12 +114,14 @@ This repository is an npm workspaces monorepo. Published packages ship to npm; e
| --- | --- | :---: | --- |
| [`packages/framework`](./packages/framework) | `@inkform/framework` | ✓ | The MDX + OpenAPI rendering engine |
| [`packages/cli`](./packages/cli) | `@inkform/cli` | ✓ | `npx` scaffolder (`inkform-docs` binary) |
| [`packages/astro`](./packages/astro) | `@inkform/astro` | ✓ (not yet released) | Astro 7 integration: the same content, served from an Astro site |
| [`templates/canopy`](./templates/canopy) | `@inkform/theme-canopy` | | Canopy theme |
| [`templates/shadcn`](./templates/shadcn) | `@inkform/theme-shadcn` | | Shadcn theme |
| [`templates/galley`](./templates/galley) | `@inkform/theme-galley` | | Galley theme |
| [`examples/pokeapi-docs`](./examples/pokeapi-docs) | `@inkform/example-pokeapi` | | Full PokéAPI docs site (OpenAPI + e2e tests) |
| [`examples/markdown-docs`](./examples/markdown-docs) | `@inkform/example-markdown` | | Markdown/MDX feature reference |
| [`examples/inkform-docs`](./examples/inkform-docs) | `@inkform/example-inkform-docs` | | This framework's own docs (dogfooded) |
| [`examples/astro-docs`](./examples/astro-docs) | `@inkform/example-astro-docs` | | An Astro host app with docs under `/docs` (Playwright tests) |

## Develop in this repo

Expand All @@ -122,7 +132,7 @@ git clone https://github.com/inkform-dev/framework.git
cd framework
npm install # installs all workspaces
npm run typecheck # typecheck every package
npm test # framework unit tests (Vitest)
npm test # framework and Astro integration unit tests (Vitest)
npm run build # build all workspaces
npm run build:examples # build the three demo sites only
```
Expand Down
2 changes: 2 additions & 0 deletions examples/astro-docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test-results/
playwright-report/
28 changes: 28 additions & 0 deletions examples/astro-docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Astro docs example

An Astro 7 app that serves Inkform docs under `/docs` with
[`@inkform/astro`](../../packages/astro). It's shaped like a real host: its
home page (`src/pages/index.astro`) is its own, rendered on demand with a React
island; it runs with `output: 'server'` and the Node adapter; and Astro's
`security.csp` is on.

```bash
npm run dev --workspace=@inkform/example-astro-docs # http://localhost:4321/docs
npm run build --workspace=@inkform/example-astro-docs
npm run start --workspace=@inkform/example-astro-docs # serves the build
npm run test:e2e --workspace=@inkform/example-astro-docs # Playwright: builds, serves, tests
```

- `content/docs/` holds `docs.json`, the pages and `openapi.json`.
[`essentials/components.mdx`](./content/docs/essentials/components.mdx) uses
every component in the kit.
- `content/blog/` and `content/changelog/` turn on `/docs/blog` and
`/docs/changelog`.
- [`e2e/docs.spec.ts`](./e2e/docs.spec.ts) checks that every page renders with
no CSP violation, that tabs, code groups, accordions, the TOC, the theme
toggle, the drawer, diagrams, the API sample pickers and Try It work, that
search finds pages, and that the docs pages load no React.

`cookie` is a direct dependency only because of this monorepo's hoisting:
another workspace puts an older `cookie` at the root, where Astro's server
build would find it. A standalone app doesn't need it.
29 changes: 29 additions & 0 deletions examples/astro-docs/astro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import node from '@astrojs/node';
import react from '@astrojs/react';
import inkform from '@inkform/astro';
import { defineConfig } from 'astro/config';

// A host app shaped like the forms app: on-demand pages by default, React
// islands on its own pages, Astro's CSP on. The docs live under /docs and
// are prerendered.
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
integrations: [react(), inkform({ base: '/docs', contentRoot: './content', mermaid: true })],
// This site has no Markdown pages of its own. Docs code blocks are
// highlighted by the integration, which keeps their colors CSP-safe.
markdown: { syntaxHighlight: false },
security: {
csp: {
algorithm: 'SHA-256',
directives: [
"default-src 'self'",
"img-src 'self' data:",
// The API Reference's Try It console calls the API from the browser.
"connect-src 'self' https://api.example.com",
'frame-src https://www.youtube.com',
"object-src 'none'",
],
},
},
});
17 changes: 17 additions & 0 deletions examples/astro-docs/content/blog/hello-astro.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Inkform on Astro
date: 2026-10-23
author: Inkform
description: The same content folder, now served by an Astro site.
tags: [release]
---

The Astro integration renders the framework's components at build time and
ships no React to the browser.

## Why

Some sites are Astro apps. Their docs can now live in the same app, under
`/docs`, with the same `docs.json`.

<Tip>See the [Quickstart](/quickstart) to set it up.</Tip>
11 changes: 11 additions & 0 deletions examples/astro-docs/content/changelog/2026-10-01-v0-0-1.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: First sketch
date: 2026-10-01
version: 0.0.1
---

A route per `docs.json` page.

```ts
injectRoute({ pattern: '/docs/[...slug]', entrypoint: '…' });
```
9 changes: 9 additions & 0 deletions examples/astro-docs/content/changelog/2026-10-23-v0-1-0.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: The Astro integration
date: 2026-10-23
version: 0.1.0
---

- Docs pages, the API Reference, blog and changelog under one base path.
- Pagefind search, built after `astro build`.
- `llms.txt` and `llms-full.txt`.
46 changes: 46 additions & 0 deletions examples/astro-docs/content/docs/docs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "Inkform on Astro",
"version": "1.0.0",
"logo": { "light": "/logo-light.svg", "dark": "/logo-dark.svg" },
"favicon": "/favicon.svg",
"colors": { "primary": "#c24e3d" },
"navbarLinks": [{ "name": "GitHub", "href": "https://github.com/inkform-dev/framework" }],
"cta": { "label": "Home", "href": "/" },
"tabs": [
{
"tab": "Guides",
"navigation": [
{
"group": "Get Started",
"pages": [
{ "title": "Introduction", "slug": "", "file": "index.mdx", "icon": "book-open" },
{ "title": "Quickstart", "slug": "quickstart", "file": "quickstart.mdx", "icon": "rocket" },
{
"title": "Host an Astro site",
"slug": "guides/astro",
"file": "guides/astro.mdx",
"icon": "layers",
"children": [
{ "title": "Content Security Policy", "slug": "guides/csp", "file": "guides/csp.mdx", "icon": "shield" }
]
}
]
},
{
"group": "Essentials",
"pages": [
{ "title": "Components", "slug": "essentials/components", "file": "essentials/components.mdx", "icon": "blocks" },
{ "title": "Markdown Syntax", "slug": "essentials/markdown", "file": "essentials/markdown.mdx", "icon": "type" },
{ "title": "Code Blocks", "slug": "essentials/code-blocks", "file": "essentials/code-blocks.mdx", "icon": "code" },
{ "title": "Navigation", "slug": "essentials/navigation", "file": "essentials/navigation.mdx", "icon": "map" }
]
}
]
},
{
"tab": "API Reference",
"openapi": "openapi.json",
"apiReference": { "renderer": "native" }
}
]
}
Loading
Loading