Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
This folder stores temp files that Docusaurus' client bundler accesses.
DO NOT hand-modify files in this folder because they will be overwritten in the
next build. You can clear all build artifacts (including this folder) with the
`docusaurus clear` command.

View File

@@ -0,0 +1,163 @@
{
"entrypoints": [
"main"
],
"origins": {
"456": [
456
],
"622": [
622
],
"17896441": [
869,
401
],
"main": [
354,
869,
792
],
"runtime~main": [
792,
869,
354
],
"0e384e19": [
976
],
"22dd74f7": [
567
],
"5e95c892": [
647
],
"a7bd4aaa": [
98
],
"a94703ab": [
869,
48
],
"aba21aa0": [
742
],
"styles": [
48,
354,
401,
792,
869
]
},
"assets": {
"48": {
"js": [
{
"file": "assets/js/a94703ab.d21732ca.js",
"hash": "248801145fd8928c",
"publicPath": "/assets/js/a94703ab.d21732ca.js"
}
]
},
"98": {
"js": [
{
"file": "assets/js/a7bd4aaa.2cf1ac5a.js",
"hash": "6f3292ea4c81c4b7",
"publicPath": "/assets/js/a7bd4aaa.2cf1ac5a.js"
}
]
},
"354": {
"js": [
{
"file": "assets/js/runtime~main.3eeea4d6.js",
"hash": "93ef5a0e7bc387d8",
"publicPath": "/assets/js/runtime~main.3eeea4d6.js"
}
]
},
"401": {
"js": [
{
"file": "assets/js/17896441.e558579f.js",
"hash": "e2a2a91703a75265",
"publicPath": "/assets/js/17896441.e558579f.js"
}
]
},
"456": {
"js": [
{
"file": "assets/js/456.41b11149.js",
"hash": "c105dca6e9081ec2",
"publicPath": "/assets/js/456.41b11149.js"
}
]
},
"567": {
"js": [
{
"file": "assets/js/22dd74f7.6d35978e.js",
"hash": "e9c668ccbb4eac4b",
"publicPath": "/assets/js/22dd74f7.6d35978e.js"
}
]
},
"622": {
"js": [
{
"file": "assets/js/622.98edb4f0.js",
"hash": "c4687fee7338a5a1",
"publicPath": "/assets/js/622.98edb4f0.js"
}
]
},
"647": {
"js": [
{
"file": "assets/js/5e95c892.c151ec16.js",
"hash": "99a9fdc9726d32c0",
"publicPath": "/assets/js/5e95c892.c151ec16.js"
}
]
},
"742": {
"js": [
{
"file": "assets/js/aba21aa0.a9cfd991.js",
"hash": "4bb470462c0c0dbb",
"publicPath": "/assets/js/aba21aa0.a9cfd991.js"
}
]
},
"792": {
"js": [
{
"file": "assets/js/main.f54ec557.js",
"hash": "862bce4131b89d15",
"publicPath": "/assets/js/main.f54ec557.js"
}
]
},
"869": {
"css": [
{
"file": "assets/css/styles.58cbff57.css",
"hash": "403c7cfb68551a78",
"publicPath": "/assets/css/styles.58cbff57.css"
}
]
},
"976": {
"js": [
{
"file": "assets/js/0e384e19.4cce6415.js",
"hash": "1da36c0f3c4b923e",
"publicPath": "/assets/js/0e384e19.4cce6415.js"
}
]
}
}
}

View File

@@ -0,0 +1,6 @@
export default [
require("/home/paul/web/genesis/node_modules/.bun/infima@0.2.0-alpha.45/node_modules/infima/dist/css/default/default.css"),
require("/home/paul/web/genesis/node_modules/.bun/@docusaurus+theme-classic@3.8.1+a362615079116658/node_modules/@docusaurus/theme-classic/lib/prism-include-languages"),
require("/home/paul/web/genesis/node_modules/.bun/@docusaurus+theme-classic@3.8.1+a362615079116658/node_modules/@docusaurus/theme-classic/lib/nprogress"),
require("/home/paul/web/genesis/apps/docs/src/css/custom.css"),
];

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{"options":{"sidebarPath":"/home/paul/web/genesis/apps/docs/sidebars.ts","routeBasePath":"/","path":"docs","editCurrentVersion":false,"editLocalizedFiles":false,"tagsBasePath":"tags","include":["**/*.{md,mdx}"],"exclude":["**/_*.{js,jsx,ts,tsx,md,mdx}","**/_*/**","**/*.test.{js,jsx,ts,tsx}","**/__tests__/**"],"sidebarCollapsible":true,"sidebarCollapsed":true,"docsRootComponent":"@theme/DocsRoot","docVersionRootComponent":"@theme/DocVersionRoot","docRootComponent":"@theme/DocRoot","docItemComponent":"@theme/DocItem","docTagsListComponent":"@theme/DocTagsListPage","docTagDocListComponent":"@theme/DocTagDocListPage","docCategoryGeneratedIndexComponent":"@theme/DocCategoryGeneratedIndexPage","remarkPlugins":[],"rehypePlugins":[],"recmaPlugins":[],"beforeDefaultRemarkPlugins":[],"beforeDefaultRehypePlugins":[],"admonitions":true,"showLastUpdateTime":false,"showLastUpdateAuthor":false,"includeCurrentVersion":true,"disableVersioning":false,"versions":{},"breadcrumbs":true,"onInlineTags":"warn","id":"default"},"versionsMetadata":[{"versionName":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","path":"/","tagsPath":"/tags","isLast":true,"routePriority":-1,"sidebarFilePath":"/home/paul/web/genesis/apps/docs/sidebars.ts","contentPath":"/home/paul/web/genesis/apps/docs/docs","contentPathLocalized":"/home/paul/web/genesis/apps/docs/i18n/en/docusaurus-plugin-content-docs/current"}]}

View File

@@ -0,0 +1,4 @@
{
"name": "docusaurus-plugin-content-docs",
"id": "default"
}

View File

@@ -0,0 +1 @@
{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"main":[{"type":"link","label":"Introduction","href":"/","docId":"intro","unlisted":false}]},"docs":{"intro":{"id":"intro","title":"Introduction","description":"Welcome to the SuavePlan monorepo documentation. This site is the front door","sidebar":"main"}}}}

View File

@@ -0,0 +1,20 @@
{
"id": "intro",
"title": "Introduction",
"description": "Welcome to the SuavePlan monorepo documentation. This site is the front door",
"source": "@site/docs/intro.md",
"sourceDirName": ".",
"slug": "/",
"permalink": "/",
"draft": false,
"unlisted": false,
"tags": [],
"version": "current",
"sidebarPosition": 1,
"frontMatter": {
"slug": "/",
"sidebar_position": 1,
"title": "Introduction"
},
"sidebar": "main"
}

View File

@@ -0,0 +1,4 @@
{
"name": "docusaurus-plugin-content-pages",
"id": "default"
}

View File

@@ -0,0 +1,356 @@
/*
* AUTOGENERATED - DON'T EDIT
* Your edits in this file will be overwritten in the next build!
* Modify the docusaurus.config.js file at your site's root instead.
*/
export default {
"title": "SuavePlan",
"tagline": "Enterprise-grade monorepo, rebuilt bottom-up",
"favicon": "img/favicon.ico",
"url": "https://docs.suaveplan.com",
"baseUrl": "/",
"organizationName": "SuavePlan",
"projectName": "genesis",
"onBrokenLinks": "warn",
"onBrokenMarkdownLinks": "warn",
"i18n": {
"defaultLocale": "en",
"locales": [
"en"
],
"path": "i18n",
"localeConfigs": {}
},
"presets": [
[
"classic",
{
"docs": {
"sidebarPath": "./sidebars.ts",
"routeBasePath": "/"
},
"blog": false,
"theme": {
"customCss": "./src/css/custom.css"
}
}
]
],
"themeConfig": {
"navbar": {
"title": "SuavePlan",
"logo": {
"alt": "SuavePlan",
"src": "img/logo.svg"
},
"items": [
{
"type": "docSidebar",
"sidebarId": "main",
"position": "left",
"label": "Docs"
},
{
"href": "https://github.com/SuavePlan/genesis",
"label": "GitHub",
"position": "right"
}
],
"hideOnScroll": false
},
"footer": {
"style": "dark",
"links": [
{
"title": "Repo",
"items": [
{
"label": "GitHub",
"href": "https://github.com/SuavePlan/genesis"
}
]
}
],
"copyright": "Copyright © 2026 SuavePlan."
},
"prism": {
"theme": {
"plain": {
"color": "#393A34",
"backgroundColor": "#f6f8fa"
},
"styles": [
{
"types": [
"comment",
"prolog",
"doctype",
"cdata"
],
"style": {
"color": "#999988",
"fontStyle": "italic"
}
},
{
"types": [
"namespace"
],
"style": {
"opacity": 0.7
}
},
{
"types": [
"string",
"attr-value"
],
"style": {
"color": "#e3116c"
}
},
{
"types": [
"punctuation",
"operator"
],
"style": {
"color": "#393A34"
}
},
{
"types": [
"entity",
"url",
"symbol",
"number",
"boolean",
"variable",
"constant",
"property",
"regex",
"inserted"
],
"style": {
"color": "#36acaa"
}
},
{
"types": [
"atrule",
"keyword",
"attr-name",
"selector"
],
"style": {
"color": "#00a4db"
}
},
{
"types": [
"function",
"deleted",
"tag"
],
"style": {
"color": "#d73a49"
}
},
{
"types": [
"function-variable"
],
"style": {
"color": "#6f42c1"
}
},
{
"types": [
"tag",
"selector",
"keyword"
],
"style": {
"color": "#00009f"
}
}
]
},
"darkTheme": {
"plain": {
"color": "#F8F8F2",
"backgroundColor": "#282A36"
},
"styles": [
{
"types": [
"prolog",
"constant",
"builtin"
],
"style": {
"color": "rgb(189, 147, 249)"
}
},
{
"types": [
"inserted",
"function"
],
"style": {
"color": "rgb(80, 250, 123)"
}
},
{
"types": [
"deleted"
],
"style": {
"color": "rgb(255, 85, 85)"
}
},
{
"types": [
"changed"
],
"style": {
"color": "rgb(255, 184, 108)"
}
},
{
"types": [
"punctuation",
"symbol"
],
"style": {
"color": "rgb(248, 248, 242)"
}
},
{
"types": [
"string",
"char",
"tag",
"selector"
],
"style": {
"color": "rgb(255, 121, 198)"
}
},
{
"types": [
"keyword",
"variable"
],
"style": {
"color": "rgb(189, 147, 249)",
"fontStyle": "italic"
}
},
{
"types": [
"comment"
],
"style": {
"color": "rgb(98, 114, 164)"
}
},
{
"types": [
"attr-name"
],
"style": {
"color": "rgb(241, 250, 140)"
}
}
]
},
"additionalLanguages": [
"bash",
"json",
"yaml",
"toml"
],
"magicComments": [
{
"className": "theme-code-block-highlighted-line",
"line": "highlight-next-line",
"block": {
"start": "highlight-start",
"end": "highlight-end"
}
}
]
},
"colorMode": {
"defaultMode": "light",
"disableSwitch": false,
"respectPrefersColorScheme": false
},
"docs": {
"versionPersistence": "localStorage",
"sidebar": {
"hideable": false,
"autoCollapseCategories": false
}
},
"blog": {
"sidebar": {
"groupByYear": true
}
},
"metadata": [],
"tableOfContents": {
"minHeadingLevel": 2,
"maxHeadingLevel": 3
}
},
"baseUrlIssueBanner": true,
"future": {
"v4": {
"removeLegacyPostBuildHeadAttribute": false,
"useCssCascadeLayers": false
},
"experimental_faster": {
"swcJsLoader": false,
"swcJsMinimizer": false,
"swcHtmlMinimizer": false,
"lightningCssMinimizer": false,
"mdxCrossCompilerCache": false,
"rspackBundler": false,
"rspackPersistentCache": false,
"ssgWorkerThreads": false
},
"experimental_storage": {
"type": "localStorage",
"namespace": false
},
"experimental_router": "browser"
},
"onBrokenAnchors": "warn",
"onDuplicateRoutes": "warn",
"staticDirectories": [
"static"
],
"customFields": {},
"plugins": [],
"themes": [],
"scripts": [],
"headTags": [],
"stylesheets": [],
"clientModules": [],
"titleDelimiter": "|",
"noIndex": false,
"markdown": {
"format": "mdx",
"mermaid": false,
"mdx1Compat": {
"comments": true,
"admonitions": true,
"headingIds": true
},
"anchors": {
"maintainCase": false
}
}
};

View File

@@ -0,0 +1,33 @@
{
"docusaurus-plugin-content-docs": {
"default": {
"path": "/",
"versions": [
{
"name": "current",
"label": "Next",
"isLast": true,
"path": "/",
"mainDocId": "intro",
"docs": [
{
"id": "intro",
"path": "/",
"sidebar": "main"
}
],
"draftIds": [],
"sidebars": {
"main": {
"link": {
"path": "/",
"label": "intro"
}
}
}
}
],
"breadcrumbs": true
}
}
}

View File

@@ -0,0 +1,17 @@
{
"defaultLocale": "en",
"locales": [
"en"
],
"path": "i18n",
"currentLocale": "en",
"localeConfigs": {
"en": {
"label": "English",
"direction": "ltr",
"htmlLang": "en",
"calendar": "gregory",
"path": "en"
}
}
}

View File

@@ -0,0 +1,8 @@
export default {
"0e384e19": [() => import(/* webpackChunkName: "0e384e19" */ "@site/docs/intro.md"), "@site/docs/intro.md", require.resolveWeak("@site/docs/intro.md")],
"17896441": [() => import(/* webpackChunkName: "17896441" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")],
"22dd74f7": [() => import(/* webpackChunkName: "22dd74f7" */ "@generated/docusaurus-plugin-content-docs/default/p/index-466.json"), "@generated/docusaurus-plugin-content-docs/default/p/index-466.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/index-466.json")],
"5e95c892": [() => import(/* webpackChunkName: "5e95c892" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")],
"a7bd4aaa": [() => import(/* webpackChunkName: "a7bd4aaa" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")],
"a94703ab": [() => import(/* webpackChunkName: "a94703ab" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")],
"aba21aa0": [() => import(/* webpackChunkName: "aba21aa0" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")],};

View File

@@ -0,0 +1,33 @@
import React from 'react';
import ComponentCreator from '@docusaurus/ComponentCreator';
export default [
{
path: '/',
component: ComponentCreator('/', '719'),
routes: [
{
path: '/',
component: ComponentCreator('/', 'd8d'),
routes: [
{
path: '/',
component: ComponentCreator('/', 'e93'),
routes: [
{
path: '/',
component: ComponentCreator('/', 'bc9'),
exact: true,
sidebar: "main"
}
]
}
]
}
]
},
{
path: '*',
component: ComponentCreator('*'),
},
];

View File

@@ -0,0 +1,19 @@
{
"/-719": {
"__comp": "5e95c892",
"__context": {
"plugin": "aba21aa0"
}
},
"/-d8d": {
"__comp": "a7bd4aaa",
"__props": "22dd74f7"
},
"/-e93": {
"__comp": "a94703ab"
},
"/-bc9": {
"__comp": "17896441",
"content": "0e384e19"
}
}

View File

@@ -0,0 +1,31 @@
{
"docusaurusVersion": "3.8.1",
"siteVersion": "0.1.0",
"pluginVersions": {
"docusaurus-plugin-content-docs": {
"type": "package",
"name": "@docusaurus/plugin-content-docs",
"version": "3.8.1"
},
"docusaurus-plugin-content-pages": {
"type": "package",
"name": "@docusaurus/plugin-content-pages",
"version": "3.8.1"
},
"docusaurus-plugin-sitemap": {
"type": "package",
"name": "@docusaurus/plugin-sitemap",
"version": "3.8.1"
},
"docusaurus-plugin-svgr": {
"type": "package",
"name": "@docusaurus/plugin-svgr",
"version": "3.8.1"
},
"docusaurus-theme-classic": {
"type": "package",
"name": "@docusaurus/theme-classic",
"version": "3.8.1"
}
}
}

View File

@@ -0,0 +1,4 @@
{
"type": "localStorage",
"namespace": ""
}

12
apps/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
node_modules
build
out
docs-app
docs/.vitepress/cache
docs/.vitepress/dist
# All mirrored content under docs/ except the VitePress config is
# regenerated by scripts/snapshot-docs-content.ts.
docs/*.md
!docs/.vitepress/**
docs/packages
docs/openspec

62
apps/docs/README.md Normal file
View File

@@ -0,0 +1,62 @@
# @suaveplan/docs - Docusaurus 3.10 documentation site for the SuavePlan monorepo
The `@suaveplan/docs` app is the public-facing documentation portal for
the genesis monorepo. It serves a Docusaurus 3.10 site that renders the
package READMEs, OpenSpec specs, and architecture guides under a single
sidebar.
## Local development
```bash
bun install
bun run --filter @suaveplan/docs dev
```
The dev server runs on `http://localhost:6043`. Hot-reload is enabled.
## Runtime: pure Bun
This app runs end-to-end under Bun — no Node required. The site is built
with **VitePress** (Vite + Vue) which works cleanly under Bun's runtime;
we previously tried Docusaurus 3.10 but its webpack-bundled SSR step
fails with `require.resolveWeak is not a function` regardless of Bun
flags or `@docusaurus/faster` config.
The static output is served in production by a Bun bytecode-compiled
binary (`docs-app`) — no Node, no Bun runtime, no `node_modules` on the
deployment target.
## Content pipeline
`scripts/snapshot-docs-content.ts` (at the repo root) mirrors every
`README.md`, package module `.md`, and OpenSpec capability spec from the
source tree into `apps/docs/docs/`, preserving paths so the auto-generated
relative links keep resolving locally. The snapshot runs automatically as
part of `bun run build`. All mirrored content is gitignored — the source
of truth stays in the package directories.
## Production build
```bash
bun run --filter @suaveplan/docs build
bun run --filter @suaveplan/docs serve
```
The static output lands in `apps/docs/build/`. Serve it from any static
host — Cloudflare Pages, Vercel, or a plain `bun --static-serve` setup.
## Content layout
- `docs/` — markdown / MDX pages picked up by the auto-generated sidebar.
- `src/css/custom.css` — site-wide theme variables.
- `docusaurus.config.ts` — navbar, footer, presets.
This app intentionally has zero workspace `@suaveplan/*` runtime deps. It
is a documentation portal that reads filesystem content; it does not import
genesis libraries.
<!-- begin:auto-spec-link -->
## OpenSpec
Canonical capability spec: [`../../openspec/specs/docs/spec.md`](../../openspec/specs/docs/spec.md).
<!-- end:auto-spec-link -->

View File

@@ -0,0 +1,27 @@
# docker-compose for @suaveplan/docs build.
#
# Runs Docusaurus inside a Node 22 LTS container. The `docs/` directory
# from the host (populated by `bun scripts/snapshot-docs-content.ts`) is
# mounted read-only as the input; the host's `build/` directory is mounted
# read-write as the output. After the one-shot build completes, the host
# has `apps/docs/build/` ready to be served by the Bun bytecode binary.
#
# Usage from `apps/docs/`:
# bun run snapshot # populate ./docs
# docker compose run --rm --build builder
# bun run build:server # compile the static-server binary
#
# Or all in one step:
# bun run build:all
services:
builder:
build:
context: ./docusaurus
dockerfile: Dockerfile
image: suaveplan-docs-builder
container_name: suaveplan-docs-builder
volumes:
- ./docs:/work/docs:ro
- ./build:/work/build
working_dir: /work

View File

@@ -0,0 +1,32 @@
# syntax=docker/dockerfile:1.7
#
# Build container for @suaveplan/docs.
#
# This image holds the Docusaurus toolchain (Node 22 LTS + npm + docusaurus
# deps). It is run via docker-compose to consume a snapshot of the repo's
# markdown content (mounted as `/work/docs`) and produce the static build
# output (mounted as `/work/build` -> host `apps/docs/build/`).
#
# The Bun bytecode-compiled runtime server lives outside this image.
FROM node:22-bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
ENV NODE_ENV=production
ENV NPM_CONFIG_FUND=false
ENV NPM_CONFIG_AUDIT=false
WORKDIR /work
# Toolchain deps first so the layer caches when only content changes.
COPY package.json ./
RUN npm install --no-save --omit=optional
# Site configuration — these change rarely, content changes often.
COPY docusaurus.config.ts sidebars.ts ./
COPY src ./src
COPY tsconfig.json ./
# Output to a subdirectory of the mounted volume so Docusaurus can rmdir
# its own output between runs without touching the bind mount itself.
CMD ["npx", "docusaurus", "build", "--out-dir", "/work/build/site"]

View File

@@ -0,0 +1,67 @@
import type { Config } from "@docusaurus/types";
import type * as Preset from "@docusaurus/preset-classic";
import { themes as prismThemes } from "prism-react-renderer";
const config: Config = {
title: "SuavePlan",
tagline: "Enterprise-grade monorepo, rebuilt bottom-up",
favicon: "img/favicon.ico",
url: "https://docs.suaveplan.com",
baseUrl: "/",
organizationName: "SuavePlan",
projectName: "genesis",
onBrokenLinks: "warn",
onBrokenMarkdownLinks: "warn",
// Use plain CommonMark rather than MDX so spec files containing
// ICU-style `{var}` braces and bare HTML-like tokens render as
// literals instead of being treated as JSX expressions.
markdown: {
format: "md",
},
i18n: {
defaultLocale: "en",
locales: ["en"],
},
presets: [
[
"classic",
{
docs: {
path: "docs",
routeBasePath: "/",
sidebarPath: "./sidebars.ts",
},
blog: false,
theme: {
customCss: "./src/css/custom.css",
},
} satisfies Preset.Options,
],
],
themeConfig: {
navbar: {
title: "SuavePlan",
items: [
{
type: "docSidebar",
sidebarId: "main",
position: "left",
label: "Docs",
},
],
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ["bash", "json", "yaml", "toml"],
},
} satisfies Preset.ThemeConfig,
};
export default config;

View File

@@ -0,0 +1,35 @@
{
"name": "@suaveplan/docs-builder",
"version": "0.1.0",
"private": true,
"description": "Containerized Docusaurus build environment for @suaveplan/docs",
"scripts": {
"build": "docusaurus build",
"serve": "docusaurus serve --port 6043",
"clear": "docusaurus clear"
},
"dependencies": {
"@docusaurus/core": "^3.10.0",
"@docusaurus/preset-classic": "^3.10.0",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.1.1",
"prism-react-renderer": "^2.4.0",
"react": "19.2.0",
"react-dom": "19.2.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.10.0",
"@docusaurus/types": "^3.10.0"
},
"browserslist": {
"production": [">0.5%", "not dead", "not op_mini all"],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">=20"
}
}

View File

@@ -0,0 +1,7 @@
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
const sidebars: SidebarsConfig = {
main: [{ type: "autogenerated", dirName: "." }],
};
export default sidebars;

View File

@@ -0,0 +1,13 @@
:root {
--ifm-color-primary: #2e7d8f;
--ifm-color-primary-dark: #2a7081;
--ifm-color-primary-darker: #286a7a;
--ifm-color-primary-darkest: #205864;
--ifm-color-primary-light: #328a9d;
--ifm-color-primary-lighter: #3590a4;
--ifm-color-primary-lightest: #3da4ba;
--ifm-code-font-size: 95%;
}
[data-theme="dark"] {
--ifm-color-primary: #61c4d8;
}

View File

@@ -0,0 +1,8 @@
{
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": ".",
"noEmit": true
},
"include": ["src/", "docusaurus.config.ts", "sidebars.ts"]
}

25
apps/docs/package.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "@suaveplan/docs-portal",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "Docusaurus 3.10 documentation portal for the SuavePlan monorepo, built via docker-compose and served by a Bun bytecode binary",
"scripts": {
"snapshot": "bun ../../scripts/snapshot-docs-content.ts",
"build:docs": "docker compose run --rm --build builder",
"build:server": "bun build --compile --minify --bytecode --target=bun-linux-x64-baseline src/server/index.ts --outfile=docs-app",
"build:all": "bun run snapshot && bun run build:docs && bun run build:server",
"serve": "bun run src/server/index.ts",
"clean": "rm -rf build out docs-app",
"typecheck": "bunx tsc --noEmit -p tsconfig.json"
},
"engines": {
"bun": ">=1.3"
},
"devDependencies": {
"@biomejs/biome": "catalog:tooling",
"@types/bun": "catalog:",
"@types/node": "catalog:",
"bun-types": "catalog:"
}
}

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env bun
/**
* Bun static-server for the `@suaveplan/docs` Docusaurus build output.
*
* Designed to be `bun build --compile`-ed into a single self-contained
* binary for production deployment. The binary expects to find the
* Docusaurus output at `./build/` (relative to its current working
* directory) by default; override with the `DOCS_BUILD_DIR` env var.
*
* Configuration:
* PORT — listen port (default: 3997)
* HOST — listen address (default: 127.0.0.1)
* DOCS_BUILD_DIR — absolute or relative path to the Docusaurus build
* output (default: "./build")
*
* Production deployment: place the binary alongside its `build/` directory,
* point a systemd unit at the binary, and front it with a reverse proxy
* (Caddy / nginx) that terminates TLS.
*/
import { existsSync, statSync } from "node:fs";
import { extname, join, normalize, resolve, sep } from "node:path";
const PORT = Number.parseInt(process.env.PORT ?? "3997", 10);
const HOST = process.env.HOST ?? "127.0.0.1";
const BUILD_DIR = resolve(process.env.DOCS_BUILD_DIR ?? "./build");
const MIME: Readonly<Record<string, string>> = {
".html": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "application/javascript; charset=utf-8",
".mjs": "application/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".svg": "image/svg+xml",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
".otf": "font/otf",
".map": "application/json; charset=utf-8",
".txt": "text/plain; charset=utf-8",
".xml": "application/xml; charset=utf-8",
".webmanifest": "application/manifest+json",
};
const HASHED_ASSET_RE = /\.[0-9a-f]{8,}\.[a-z0-9]+$/iu;
function contentType(path: string): string {
return MIME[extname(path).toLowerCase()] ?? "application/octet-stream";
}
function cacheHeaders(path: string): Record<string, string> {
// Docusaurus content-hashes static assets — they can be cached forever.
// HTML and the root-relative service-worker / manifest must revalidate.
if (HASHED_ASSET_RE.test(path)) {
return { "cache-control": "public, max-age=31536000, immutable" };
}
const ext = extname(path).toLowerCase();
if (ext === ".html" || ext === ".webmanifest" || ext === ".xml") {
return { "cache-control": "public, max-age=0, must-revalidate" };
}
return { "cache-control": "public, max-age=3600" };
}
function resolveSafe(urlPath: string): string | undefined {
// Strip query / hash and decode percent-escapes.
let decoded: string;
try {
decoded = decodeURIComponent(urlPath.split("?")[0]?.split("#")[0] ?? "/");
} catch {
return undefined;
}
// Normalize and ensure the resolved path stays inside BUILD_DIR.
const joined = normalize(join(BUILD_DIR, decoded));
const guard = BUILD_DIR.endsWith(sep) ? BUILD_DIR : `${BUILD_DIR}${sep}`;
if (joined !== BUILD_DIR && !joined.startsWith(guard)) return undefined;
return joined;
}
function tryServe(absPath: string): Response | undefined {
if (!existsSync(absPath)) return undefined;
let info: ReturnType<typeof statSync>;
try {
info = statSync(absPath);
} catch {
return undefined;
}
if (info.isDirectory()) {
const indexPath = join(absPath, "index.html");
if (!existsSync(indexPath)) return undefined;
return serveFile(indexPath);
}
return serveFile(absPath);
}
function serveFile(absPath: string): Response {
const file = Bun.file(absPath);
return new Response(file, {
headers: {
"content-type": contentType(absPath),
...cacheHeaders(absPath),
},
});
}
function notFound(): Response {
const fallback = join(BUILD_DIR, "404.html");
if (existsSync(fallback)) {
return new Response(Bun.file(fallback), {
status: 404,
headers: {
"content-type": "text/html; charset=utf-8",
"cache-control": "public, max-age=0, must-revalidate",
},
});
}
return new Response("Not Found", {
status: 404,
headers: { "content-type": "text/plain; charset=utf-8" },
});
}
if (!existsSync(BUILD_DIR)) {
console.error(`[docs-app] build dir not found: ${BUILD_DIR}`);
process.exit(2);
}
const server = Bun.serve({
port: PORT,
hostname: HOST,
fetch(req: Request): Response {
const url = new URL(req.url);
if (req.method !== "GET" && req.method !== "HEAD") {
return new Response("Method Not Allowed", {
status: 405,
headers: { allow: "GET, HEAD" },
});
}
// Health endpoint for systemd / reverse-proxy probes.
if (url.pathname === "/_health") {
return new Response("ok", {
status: 200,
headers: { "content-type": "text/plain; charset=utf-8" },
});
}
const safePath = resolveSafe(url.pathname);
if (!safePath) return notFound();
const response = tryServe(safePath);
if (response) return response;
// Docusaurus routes everything to its own SPA-ish HTML — let the
// build's 404.html handle unknowns so the client-side router can
// recover.
return notFound();
},
});
console.log(
`[docs-app] serving ${BUILD_DIR} on http://${server.hostname}:${server.port}`
);

16
apps/docs/tsconfig.json Normal file
View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"types": ["node", "bun"]
},
"include": ["src/", "docs/.vitepress/"]
}