Jelajahi Sumber

[docs] api reference (#132)

* feat: scaffold api section

* feat: generic core docs

* refactor: ui tweaks

* feat: api docs

* feat: programs api

* fix: remove trailing index

* feat: prebuild

* ci: docs clean script

* chore: docs preview

* feat: docs for other clients

* feat: publish docs

* feat: only trigger on docs
Nick Frostbutter 5 bulan lalu
induk
melakukan
9e4bc55431

+ 74 - 0
.github/workflows/preview-docs.yml

@@ -0,0 +1,74 @@
+name: Preview Documentation
+
+on:
+  pull_request:
+    paths:
+      - 'docs/**'
+
+permissions:
+  contents: read
+
+env:
+  # Among other things, opts out of Turborepo telemetry
+  # See https://consoledonottrack.com/
+  DO_NOT_TRACK: '1'
+  NEXT_TELEMETRY_DISABLED: '1'
+  VERCEL_TELEMETRY_DISABLED: '1'
+  # Some tasks slow down considerably on GitHub Actions runners when concurrency is high
+  TURBO_CONCURRENCY: 1
+  # Enables Turborepo Remote Caching.
+  TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
+  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
+  TURBO_TEAM: ${{ vars.TURBO_TEAM }}
+
+jobs:
+  preview-docs:
+    if: github.actor != 'dependabot[bot]'
+    permissions:
+      pull-requests: write
+    runs-on: ubuntu-latest
+    name: Build Documentation Preview
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install Dependencies
+        uses: ./.github/workflows/actions/install-dependencies
+
+      - name: Install Isolated Docs Dependencies
+        working-directory: ./docs/
+        shell: bash
+        run: pnpm install --ignore-workspace
+
+      - name: Install Vercel CLI
+        run: pnpm install -g vercel
+
+      - name: Deploy to Vercel
+        shell: bash
+        id: vercel_deploy
+        env:
+          BRANCH_NAME: ${{ github.head_ref }}
+          PR_NUMBER: ${{ github.event.pull_request.number }}
+          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
+          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
+          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+        run: |
+          vercel pull --token="$VERCEL_TOKEN" --yes --environment=preview
+          vercel build --token="$VERCEL_TOKEN" --prod=false
+          DEPLOY_OUTPUT=$(vercel deploy --token="$VERCEL_TOKEN" --archive=tgz --env GITHUB_PR_NUMBER="$PR_NUMBER" --env GITHUB_PR_BRANCH="$BRANCH_NAME" --prebuilt --prod=false 2>&1)
+          DEPLOY_EXIT_CODE=$?
+          if [ $DEPLOY_EXIT_CODE -ne 0 ]; then
+            echo "Vercel deploy failed:"
+            echo "$DEPLOY_OUTPUT"
+            exit $DEPLOY_EXIT_CODE
+          fi
+          DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -o 'https://[a-zA-Z0-9.-]*\.vercel\.app' | tail -1)
+          echo "preview_url=$DEPLOY_URL" >> $GITHUB_OUTPUT
+
+      - name: Comment on PR with Preview URL
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_NUMBER: ${{ github.event.pull_request.number }}
+          PREVIEW_URL: '${{ steps.vercel_deploy.outputs.preview_url }}'
+        run: |
+          gh pr comment $PR_NUMBER --body "Documentation Preview: $PREVIEW_URL" --create-if-none --edit-last

+ 59 - 0
.github/workflows/publish-docs.yml

@@ -0,0 +1,59 @@
+name: Publish Documentation
+
+on:
+  workflow_dispatch:
+    branches:
+      - master
+  push:
+    branches:
+      - master
+    paths:
+      - 'docs/**'
+
+permissions:
+  contents: read
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  # Among other things, opts out of Turborepo telemetry
+  # See https://consoledonottrack.com/
+  DO_NOT_TRACK: '1'
+  NEXT_TELEMETRY_DISABLED: '1'
+  VERCEL_TELEMETRY_DISABLED: '1'
+  # Enables Turborepo Remote Caching.
+  TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
+  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
+  TURBO_TEAM: ${{ vars.TURBO_TEAM }}
+
+jobs:
+  deploy-docs:
+    runs-on: ubuntu-latest
+    name: Deploy Documentation
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install Dependencies
+        uses: ./.github/workflows/actions/install-dependencies
+
+      - name: Install Isolated Docs Dependencies
+        working-directory: ./docs/
+        shell: bash
+        run: pnpm install --ignore-workspace
+
+      - name: Install Vercel CLI
+        run: pnpm install -g vercel
+
+      - name: Deploy to Vercel
+        shell: bash
+        env:
+          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
+          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
+          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
+        run: |
+          vercel pull --token="$VERCEL_TOKEN" --yes --environment=production
+          vercel build --token="$VERCEL_TOKEN" --prod
+          vercel deploy --token="$VERCEL_TOKEN" --archive=tgz --prebuilt --prod

+ 4 - 1
.gitignore

@@ -18,6 +18,9 @@ yarn-error.log*
 # turbo
 .turbo
 
+# typedocs output
+.docs
+
 # Sapling SCM
 .sl
 
@@ -31,4 +34,4 @@ test-ledger
 
 .env
 .env.local
-.vercel
+.vercel

+ 9 - 0
docs/.gitignore

@@ -6,6 +6,12 @@
 .content-collections
 .source
 
+# api content
+.docs
+content/api/
+!./content/api/index.mdx
+!./content/api/meta.json
+
 # test & build
 /coverage
 /.next/
@@ -26,3 +32,6 @@ yarn-error.log*
 .env*.local
 .vercel
 next-env.d.ts
+
+# Turborepo
+.turbo

+ 5 - 0
docs/build-api-docs.sh

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cd ..
+pnpm compile:docs --output-logs=hash-only

+ 6 - 0
docs/content/api/index.mdx

@@ -0,0 +1,6 @@
+---
+title: API Reference
+# description: ""
+---
+
+TODO: document the api references

+ 5 - 0
docs/content/api/meta.json

@@ -0,0 +1,5 @@
+{
+  "title": "API Reference",
+  "root": true,
+  "pages": ["!index", "..."]
+}

+ 3 - 1
docs/package.json

@@ -3,11 +3,13 @@
   "version": "0.0.0",
   "private": true,
   "scripts": {
+    "prebuild": "./build-api-docs.sh",
     "build": "next build",
     "dev": "next dev --turbo",
     "start": "next start",
     "postinstall": "fumadocs-mdx",
-    "style:fix": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,css,json,md}'"
+    "clean": "rimraf dist build node_modules .turbo .next .docs ./content/api/gill*",
+    "style:fix": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,css,json,md,mdx}'"
   },
   "engines": {
     "node": ">=20.18.0",

+ 4 - 0
docs/source.config.ts

@@ -26,6 +26,10 @@ export const docs = defineDocs({
   dir: "content/docs",
 });
 
+export const api = defineDocs({
+  dir: "content/api",
+});
+
 export const guides = defineDocs({
   dir: "content/guides",
 });

+ 68 - 0
docs/src/app/api/[[...slug]]/page.tsx

@@ -0,0 +1,68 @@
+import { getPageTreePeers } from "fumadocs-core/server";
+import { Popup, PopupContent, PopupTrigger } from "fumadocs-twoslash/ui";
+import { Card, Cards } from "fumadocs-ui/components/card";
+import { ImageZoom } from "fumadocs-ui/components/image-zoom";
+import { Step, Steps } from "fumadocs-ui/components/steps";
+import { Tab, Tabs } from "fumadocs-ui/components/tabs";
+import defaultMdxComponents from "fumadocs-ui/mdx";
+import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
+import { notFound } from "next/navigation";
+
+import { apiSource } from "@/lib/source";
+import { Spread } from "@/lib/Spread";
+
+export async function generateStaticParams() {
+  return apiSource.generateParams();
+}
+
+export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) {
+  const params = await props.params;
+  const page = apiSource.getPage(params.slug);
+  if (!page) notFound();
+
+  return {
+    title: page.data.title,
+    description: page.data.description,
+  };
+}
+
+export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
+  const params = await props.params;
+  const page = apiSource.getPage(params.slug);
+  if (!page) notFound();
+
+  const MDX = page.data.body;
+  const hasCategory = page.file.name === "index" && page.slugs.length > 0;
+
+  return (
+    <DocsPage toc={page.data.toc} full={page.data.full}>
+      <DocsTitle>{page.data.title}</DocsTitle>
+      <DocsDescription className="mb-16">{page.data.description}</DocsDescription>
+      <DocsBody>
+        <MDX
+          components={{
+            ...defaultMdxComponents,
+            img: (props) => <ImageZoom {...props} />,
+            Popup,
+            PopupContent,
+            PopupTrigger,
+            Spread,
+            Step,
+            Steps,
+            Tab,
+            Tabs,
+          }}
+        />
+      </DocsBody>
+      {hasCategory && (
+        <Cards>
+          {getPageTreePeers(apiSource.pageTree, page.url).map((peer) => (
+            <Card key={peer.url} title={peer.name} href={peer.url}>
+              {peer.description}
+            </Card>
+          ))}
+        </Cards>
+      )}
+    </DocsPage>
+  );
+}

+ 22 - 0
docs/src/app/api/layout.tsx

@@ -0,0 +1,22 @@
+import { DocsLayout } from "fumadocs-ui/layouts/docs";
+import type { ReactNode } from "react";
+import { baseOptions } from "@/app/layout.config";
+import { apiSource } from "@/lib/source";
+import { Metadata } from "next";
+import { siteConfig } from "@/const";
+
+export const metadata: Metadata = {
+  title: {
+    default: `${siteConfig.name} API References`,
+    template: `%s | ${siteConfig.name} API`,
+  },
+  description: siteConfig.description,
+};
+
+export default function Layout({ children }: { children: ReactNode }) {
+  return (
+    <DocsLayout tree={apiSource.pageTree} {...baseOptions}>
+      {children}
+    </DocsLayout>
+  );
+}

+ 7 - 1
docs/src/app/layout.config.tsx

@@ -1,5 +1,5 @@
 import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
-import { BookTextIcon, ShapesIcon } from "lucide-react";
+import { BookTextIcon, CodeXmlIcon, ShapesIcon } from "lucide-react";
 import Image from "next/image";
 
 import icon from "@@/public/icon-black.svg";
@@ -34,5 +34,11 @@ export const baseOptions: BaseLayoutProps = {
       active: "nested-url",
       icon: <ShapesIcon />,
     },
+    {
+      text: "API Reference",
+      url: "/api",
+      active: "nested-url",
+      icon: <CodeXmlIcon />,
+    },
   ],
 };

+ 6 - 1
docs/src/lib/source.ts

@@ -1,4 +1,4 @@
-import { docs, guides } from "@/.source";
+import { api, docs, guides } from "@/.source";
 import { loader } from "fumadocs-core/source";
 
 export const docsSource = loader({
@@ -6,6 +6,11 @@ export const docsSource = loader({
   source: docs.toFumadocsSource(),
 });
 
+export const apiSource = loader({
+  baseUrl: "/api",
+  source: api.toFumadocsSource(),
+});
+
 export const guidesSource = loader({
   baseUrl: "/guides",
   source: guides.toFumadocsSource(),

+ 36 - 0
docs/typedoc-data.mjs

@@ -0,0 +1,36 @@
+// @ts-check
+
+/**
+ * Declare data and information to be injected into the
+ * TypeDoc generated API reference's index files
+ */
+export const typedocPageIndexData = {
+  gill: {
+    frontmatter: {
+      title: "gill",
+      description:
+        "A modern JavaScript/TypeScript client library for interacting with the Solana blockchain.",
+    },
+  },
+  "gill-node": {
+    frontmatter: {
+      title: "gill/node",
+      description:
+        "Functions and utilities designed for interacting with the Solana blockchain using JavaScript server backends and runtimes.",
+    },
+  },
+  "gill-programs": {
+    frontmatter: {
+      title: "gill/programs",
+      description:
+        "Utilize the compatible Solana program clients that ship directly within gill. Including common programs from the official Solana Program Library (SPL), Metaplex, and others.",
+    },
+  },
+  react: {
+    frontmatter: {
+      title: "gill-react",
+      description:
+        "A React hooks library for easily interacting with the Solana blockchain. Built on top of gill and TanStack Query.",
+    },
+  },
+};

+ 1 - 1
docs/vercel.json

@@ -3,6 +3,6 @@
     "silent": true
   },
   "git": {
-    "deploymentEnabled": true
+    "deploymentEnabled": false
   }
 }

+ 5 - 0
package.json

@@ -15,6 +15,7 @@
     "clean:root": "rimraf dist build node_modules .turbo",
     "prebuild": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} build --filter=gill",
     "build": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} build",
+    "compile:docs": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} compile:docs",
     "test": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} test",
     "test:treeshakability:native": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} test:treeshakability:native",
     "test:treeshakability:browser": "turbo run --concurrency=${TURBO_CONCURRENCY:-95.84%} test:treeshakability:browser",
@@ -62,6 +63,10 @@
     "rimraf": "5.0.10",
     "ts-node": "^10.9.2",
     "tsup": "^8.3.5",
+    "typedoc": "^0.28.4",
+    "typedoc-plugin-frontmatter": "^1.3.0",
+    "typedoc-plugin-markdown": "^4.6.3",
+    "typedoc-plugin-mdn-links": "^5.0.2",
     "turbo": "^2.3.1"
   },
   "packageManager": "pnpm@9.1.0",

+ 7 - 1
packages/gill/package.json

@@ -4,7 +4,13 @@
   "version": "0.9.0",
   "description": "a modern javascript/typescript client library for interacting with the Solana blockchain",
   "scripts": {
-    "clean": "rimraf dist build node_modules .turbo",
+    "clean": "rimraf dist build node_modules .turbo .docs",
+    "clean:docs": "rimraf ../../docs/content/api/gill*",
+    "compile:docs-core": "typedoc --options typedoc.core.json",
+    "compile:docs-node": "typedoc --options typedoc.node.json",
+    "compile:docs-programs": "typedoc --options typedoc.programs.json",
+    "compile:docs": "pnpm compile:docs-core && pnpm compile:docs-node && pnpm compile:docs-programs && pnpm move:docs",
+    "move:docs": "pnpm clean:docs && cp -rf ./.docs/* ../../docs/content/api/",
     "compile:js": "tsup --config ./tsup.config.package.ts",
     "compile:typedefs": "tsc -p ./tsconfig.declarations.json",
     "prepublishOnly": "pnpm pkg delete devDependencies",

+ 6 - 0
packages/gill/typedoc.core.json

@@ -0,0 +1,6 @@
+{
+  "$schema": "https://typedoc.org/schema.json",
+  "extends": ["../../typedoc.json"],
+  "entryPoints": ["src/index.ts"],
+  "out": "./.docs/gill"
+}

+ 0 - 5
packages/gill/typedoc.json

@@ -1,5 +0,0 @@
-{
-  "$schema": "https://typedoc.org/schema.json",
-  "extends": ["../typedoc.base.json"],
-  "entryPoints": ["src/index.ts"]
-}

+ 6 - 0
packages/gill/typedoc.node.json

@@ -0,0 +1,6 @@
+{
+  "$schema": "https://typedoc.org/schema.json",
+  "extends": ["../../typedoc.json"],
+  "entryPoints": ["src/node/index.ts"],
+  "out": "./.docs/gill-node"
+}

+ 14 - 0
packages/gill/typedoc.programs.json

@@ -0,0 +1,14 @@
+{
+  "$schema": "https://typedoc.org/schema.json",
+  "extends": ["../../typedoc.json"],
+  "excludeExternals": false,
+  "entryPoints": [
+    "src/programs/compute-budget/index.ts",
+    "src/programs/system/index.ts",
+    "src/programs/address-lookup-table/index.ts",
+    "src/programs/memo/index.ts",
+    "src/programs/token/index.ts",
+    "src/programs/token-metadata/index.ts"
+  ],
+  "out": "./.docs/gill-programs"
+}

+ 6 - 0
packages/react/typedoc.json

@@ -0,0 +1,6 @@
+{
+  "$schema": "https://typedoc.org/schema.json",
+  "extends": ["../../typedoc.json"],
+  "entryPoints": ["src/index.ts"],
+  "out": "./.docs"
+}

+ 164 - 7
pnpm-lock.yaml

@@ -103,10 +103,22 @@ importers:
         version: 10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@22.9.1)(typescript@5.8.3)
       tsup:
         specifier: ^8.3.5
-        version: 8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.7.0)
+        version: 8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.8.0)
       turbo:
         specifier: ^2.3.1
         version: 2.3.1
+      typedoc:
+        specifier: ^0.28.4
+        version: 0.28.5(typescript@5.8.3)
+      typedoc-plugin-frontmatter:
+        specifier: ^1.3.0
+        version: 1.3.0(typedoc-plugin-markdown@4.6.3(typedoc@0.28.5(typescript@5.8.3)))
+      typedoc-plugin-markdown:
+        specifier: ^4.6.3
+        version: 4.6.3(typedoc@0.28.5(typescript@5.8.3))
+      typedoc-plugin-mdn-links:
+        specifier: ^5.0.2
+        version: 5.0.2(typedoc@0.28.5(typescript@5.8.3))
 
   examples/get-started:
     dependencies:
@@ -980,6 +992,9 @@ packages:
     resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
+  '@gerrit0/mini-shiki@3.4.2':
+    resolution: {integrity: sha512-3jXo5bNjvvimvdbIhKGfFxSnKCX+MA8wzHv55ptzk/cx8wOzT+BRcYgj8aFN3yTiTs+zvQQiaZFr7Jce1ZG3fw==}
+
   '@hapi/hoek@9.3.0':
     resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
 
@@ -1312,6 +1327,21 @@ packages:
     cpu: [x64]
     os: [win32]
 
+  '@shikijs/engine-oniguruma@3.4.2':
+    resolution: {integrity: sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q==}
+
+  '@shikijs/langs@3.4.2':
+    resolution: {integrity: sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA==}
+
+  '@shikijs/themes@3.4.2':
+    resolution: {integrity: sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg==}
+
+  '@shikijs/types@3.4.2':
+    resolution: {integrity: sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg==}
+
+  '@shikijs/vscode-textmate@10.0.2':
+    resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
   '@sideway/address@4.1.5':
     resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
 
@@ -1803,6 +1833,9 @@ packages:
   '@types/hast@2.3.10':
     resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
 
+  '@types/hast@3.0.4':
+    resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+
   '@types/istanbul-lib-coverage@2.0.6':
     resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
 
@@ -3588,6 +3621,9 @@ packages:
   lines-and-columns@1.2.4:
     resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
 
+  linkify-it@5.0.0:
+    resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
+
   load-tsconfig@0.2.5:
     resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -3632,6 +3668,9 @@ packages:
   lru-cache@5.1.1:
     resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
 
+  lunr@2.3.9:
+    resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
+
   make-dir@2.1.0:
     resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
     engines: {node: '>=6'}
@@ -3646,10 +3685,17 @@ packages:
   makeerror@1.0.12:
     resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
 
+  markdown-it@14.1.0:
+    resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
+    hasBin: true
+
   math-intrinsics@1.1.0:
     resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
     engines: {node: '>= 0.4'}
 
+  mdurl@2.0.0:
+    resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+
   meow@13.2.0:
     resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==}
     engines: {node: '>=18'}
@@ -3968,6 +4014,10 @@ packages:
   psl@1.13.0:
     resolution: {integrity: sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==}
 
+  punycode.js@2.3.1:
+    resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
+    engines: {node: '>=6'}
+
   punycode@2.3.1:
     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
     engines: {node: '>=6'}
@@ -4414,6 +4464,29 @@ packages:
   typedarray-to-buffer@3.1.5:
     resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
 
+  typedoc-plugin-frontmatter@1.3.0:
+    resolution: {integrity: sha512-xYQFMAecMlsRUjmf9oM/Sq2FVz4zlgcbIeVFNLdO118CHTN06gIKJNSlyExh9+Xl8sK0YhIvoQwViUURxritWA==}
+    peerDependencies:
+      typedoc-plugin-markdown: '>=4.5.0'
+
+  typedoc-plugin-markdown@4.6.3:
+    resolution: {integrity: sha512-86oODyM2zajXwLs4Wok2mwVEfCwCnp756QyhLGX2IfsdRYr1DXLCgJgnLndaMUjJD7FBhnLk2okbNE9PdLxYRw==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      typedoc: 0.28.x
+
+  typedoc-plugin-mdn-links@5.0.2:
+    resolution: {integrity: sha512-Bd3lsVWPSpDkn6NGZyPHpcK088PUvH4SRq4RD97OjA6l8PQA3yOnJhGACtjmIDdcenRTgWUosH+55ANZhx/wkw==}
+    peerDependencies:
+      typedoc: 0.27.x || 0.28.x
+
+  typedoc@0.28.5:
+    resolution: {integrity: sha512-5PzUddaA9FbaarUzIsEc4wNXCiO4Ot3bJNeMF2qKpYlTmM9TTaSHQ7162w756ERCkXER/+o2purRG6YOAv6EMA==}
+    engines: {node: '>= 18', pnpm: '>= 10'}
+    hasBin: true
+    peerDependencies:
+      typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x
+
   typescript-eslint@8.16.0:
     resolution: {integrity: sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4434,6 +4507,9 @@ packages:
     engines: {node: '>=14.17'}
     hasBin: true
 
+  uc.micro@2.1.0:
+    resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
+
   undici-types@6.19.8:
     resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
 
@@ -4628,6 +4704,11 @@ packages:
     engines: {node: '>= 14'}
     hasBin: true
 
+  yaml@2.8.0:
+    resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
+    engines: {node: '>= 14.6'}
+    hasBin: true
+
   yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
     engines: {node: '>=12'}
@@ -5452,6 +5533,14 @@ snapshots:
     dependencies:
       levn: 0.4.1
 
+  '@gerrit0/mini-shiki@3.4.2':
+    dependencies:
+      '@shikijs/engine-oniguruma': 3.4.2
+      '@shikijs/langs': 3.4.2
+      '@shikijs/themes': 3.4.2
+      '@shikijs/types': 3.4.2
+      '@shikijs/vscode-textmate': 10.0.2
+
   '@hapi/hoek@9.3.0': {}
 
   '@hapi/topo@5.1.0':
@@ -5904,6 +5993,26 @@ snapshots:
   '@rollup/rollup-win32-x64-msvc@4.27.3':
     optional: true
 
+  '@shikijs/engine-oniguruma@3.4.2':
+    dependencies:
+      '@shikijs/types': 3.4.2
+      '@shikijs/vscode-textmate': 10.0.2
+
+  '@shikijs/langs@3.4.2':
+    dependencies:
+      '@shikijs/types': 3.4.2
+
+  '@shikijs/themes@3.4.2':
+    dependencies:
+      '@shikijs/types': 3.4.2
+
+  '@shikijs/types@3.4.2':
+    dependencies:
+      '@shikijs/vscode-textmate': 10.0.2
+      '@types/hast': 3.0.4
+
+  '@shikijs/vscode-textmate@10.0.2': {}
+
   '@sideway/address@4.1.5':
     dependencies:
       '@hapi/hoek': 9.3.0
@@ -6512,6 +6621,10 @@ snapshots:
     dependencies:
       '@types/unist': 2.0.11
 
+  '@types/hast@3.0.4':
+    dependencies:
+      '@types/unist': 2.0.11
+
   '@types/istanbul-lib-coverage@2.0.6': {}
 
   '@types/istanbul-lib-report@3.0.3':
@@ -8875,6 +8988,10 @@ snapshots:
 
   lines-and-columns@1.2.4: {}
 
+  linkify-it@5.0.0:
+    dependencies:
+      uc.micro: 2.1.0
+
   load-tsconfig@0.2.5: {}
 
   locate-path@3.0.0:
@@ -8916,6 +9033,8 @@ snapshots:
     dependencies:
       yallist: 3.1.1
 
+  lunr@2.3.9: {}
+
   make-dir@2.1.0:
     dependencies:
       pify: 4.0.1
@@ -8931,8 +9050,19 @@ snapshots:
     dependencies:
       tmpl: 1.0.5
 
+  markdown-it@14.1.0:
+    dependencies:
+      argparse: 2.0.1
+      entities: 4.5.0
+      linkify-it: 5.0.0
+      mdurl: 2.0.0
+      punycode.js: 2.3.1
+      uc.micro: 2.1.0
+
   math-intrinsics@1.1.0: {}
 
+  mdurl@2.0.0: {}
+
   meow@13.2.0: {}
 
   merge-stream@2.0.0: {}
@@ -9129,13 +9259,13 @@ snapshots:
     dependencies:
       find-up: 4.1.0
 
-  postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.3)(yaml@2.7.0):
+  postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.3)(yaml@2.8.0):
     dependencies:
       lilconfig: 3.1.2
     optionalDependencies:
       jiti: 2.4.2
       postcss: 8.5.3
-      yaml: 2.7.0
+      yaml: 2.8.0
 
   postcss@8.5.3:
     dependencies:
@@ -9183,6 +9313,8 @@ snapshots:
     dependencies:
       punycode: 2.3.1
 
+  punycode.js@2.3.1: {}
+
   punycode@2.3.1: {}
 
   pure-rand@6.1.0: {}
@@ -9547,7 +9679,7 @@ snapshots:
 
   tslib@2.8.1: {}
 
-  tsup@8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.7.0):
+  tsup@8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.3)(yaml@2.8.0):
     dependencies:
       bundle-require: 5.0.0(esbuild@0.24.0)
       cac: 6.7.14
@@ -9557,7 +9689,7 @@ snapshots:
       esbuild: 0.24.0
       joycon: 3.1.1
       picocolors: 1.1.1
-      postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.3)(yaml@2.7.0)
+      postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.3)(yaml@2.8.0)
       resolve-from: 5.0.0
       rollup: 4.27.3
       source-map: 0.8.0-beta.0
@@ -9619,6 +9751,28 @@ snapshots:
     dependencies:
       is-typedarray: 1.0.0
 
+  typedoc-plugin-frontmatter@1.3.0(typedoc-plugin-markdown@4.6.3(typedoc@0.28.5(typescript@5.8.3))):
+    dependencies:
+      typedoc-plugin-markdown: 4.6.3(typedoc@0.28.5(typescript@5.8.3))
+      yaml: 2.7.0
+
+  typedoc-plugin-markdown@4.6.3(typedoc@0.28.5(typescript@5.8.3)):
+    dependencies:
+      typedoc: 0.28.5(typescript@5.8.3)
+
+  typedoc-plugin-mdn-links@5.0.2(typedoc@0.28.5(typescript@5.8.3)):
+    dependencies:
+      typedoc: 0.28.5(typescript@5.8.3)
+
+  typedoc@0.28.5(typescript@5.8.3):
+    dependencies:
+      '@gerrit0/mini-shiki': 3.4.2
+      lunr: 2.3.9
+      markdown-it: 14.1.0
+      minimatch: 9.0.5
+      typescript: 5.8.3
+      yaml: 2.8.0
+
   typescript-eslint@8.16.0(eslint@9.15.0(jiti@2.4.2))(typescript@5.8.3):
     dependencies:
       '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.15.0(jiti@2.4.2))(typescript@5.8.3)
@@ -9634,6 +9788,8 @@ snapshots:
 
   typescript@5.8.3: {}
 
+  uc.micro@2.1.0: {}
+
   undici-types@6.19.8: {}
 
   undici-types@6.20.0: {}
@@ -9804,8 +9960,9 @@ snapshots:
 
   yaml@1.10.2: {}
 
-  yaml@2.7.0:
-    optional: true
+  yaml@2.7.0: {}
+
+  yaml@2.8.0: {}
 
   yargs-parser@21.1.1: {}
 

+ 11 - 0
turbo.json

@@ -21,6 +21,17 @@
       "dependsOn": ["build", "test"],
       "passThroughEnv": ["GH_TOKEN", "NPM_TOKEN", "PUBLISH_TAG"]
     },
+    "compile:docs": {
+      "dependsOn": ["^compile:typedefs"],
+      "inputs": [
+        "$TURBO_DEFAULT$",
+        "tsconfig.*",
+        "$TURBO_ROOT$/typedoc.json",
+        "$TURBO_ROOT$/typedoc.plugin.mjs",
+        "src/**"
+      ],
+      "outputs": [".docs/**"]
+    },
     "compile:js": {
       "dependsOn": ["^compile:js"],
       "inputs": ["$TURBO_DEFAULT$", "tsconfig.*", "src/**", "../build-scripts/*.ts"],

+ 25 - 0
typedoc.json

@@ -0,0 +1,25 @@
+{
+  "$schema": "https://typedoc.org/schema.json",
+  "out": "docs/content/api",
+  "plugin": ["typedoc-plugin-markdown", "typedoc-plugin-frontmatter", "./typedoc.plugin.mjs"],
+  "excludeExternals": true,
+  "disableSources": true,
+  "readme": "none",
+  "hidePageHeader": true,
+  "hideBreadcrumbs": true,
+  "hideGroupHeadings": true,
+  "hidePageTitle": true,
+  "expandObjects": true,
+  "expandParameters": false,
+  "blockTagsPreserveOrder": ["@example", "@deprecated"],
+  "useCodeBlocks": true,
+  "indexFormat": "table",
+  "parametersFormat": "table",
+  "classPropertiesFormat": "table",
+  "enumMembersFormat": "table",
+  "typeDeclarationFormat": "table",
+  "propertyMembersFormat": "table",
+  "interfacePropertiesFormat": "table",
+  "fileExtension": ".mdx",
+  "entryFileName": "index"
+}

+ 71 - 0
typedoc.plugin.mjs

@@ -0,0 +1,71 @@
+// @ts-check
+import { dirname, resolve } from "node:path";
+import { cwd } from "node:process";
+import * as td from "typedoc-plugin-markdown";
+
+import { typedocPageIndexData } from "./docs/typedoc-data.mjs";
+
+// Extract the package directory name to be used as the part of the markdown links
+let dirs = cwd().split("/packages/");
+
+/** @param {td.MarkdownApplication} app */
+export function load(app) {
+  // For multi-export packages, put each docs into a different directory
+  if (!app.options.getValue("out").endsWith("/.docs")) {
+    // console.warn("[app]", app.options.getValue("out"));
+    dirs = app.options.getValue("out").split("/.docs/");
+  }
+
+  const apiDirName = dirs[dirs.length - 1];
+
+  // Set Markdown frontmatter for each page
+  app.renderer.on(td.MarkdownPageEvent.BEGIN, (page) => {
+    page.frontmatter = {
+      title: page.model.name,
+    };
+
+    // Slice in the index data (when desired)
+    if (page.url == "index.mdx" && typedocPageIndexData[apiDirName]?.frontmatter) {
+      page.frontmatter = {
+        ...page.frontmatter,
+        ...typedocPageIndexData[apiDirName].frontmatter,
+      };
+    }
+  });
+
+  // Rewrite all of the internal links
+  // - root relative for compatibility with Next.js
+  // - strip the .mdx extension
+  app.renderer.on(td.MarkdownPageEvent.END, (page) => {
+    if (!page.contents) return;
+
+    // Slice in the index data (when desired)
+    if (page.url == "index.mdx" && typedocPageIndexData[apiDirName]?.contents) {
+      page.contents = insertAfterFrontmatter(page.contents, typedocPageIndexData[apiDirName].contents);
+    }
+
+    page.contents = page.contents.replace(/\(((?:[^\/\)]+\/)*[^\/\)]+)\.mdx\)/gm, (_, path) => {
+      // Remove trailing /index routes
+      if (path.endsWith("/index")) path = path.replace(/\/index$/gi, "");
+
+      const rootRelativeUrl = resolve(`/api/${apiDirName}`, dirname(page.url), path);
+      return `(${rootRelativeUrl})`;
+    });
+  });
+}
+
+function insertAfterFrontmatter(markdownText, textToInsert) {
+  if (!markdownText.startsWith("---")) {
+    return textToInsert + markdownText;
+  }
+
+  const secondDelimiter = markdownText.indexOf("\n---\n", 4);
+  if (secondDelimiter === -1) {
+    return textToInsert + markdownText;
+  }
+
+  const frontmatter = markdownText.substring(0, secondDelimiter + 5);
+  const content = markdownText.substring(secondDelimiter + 5);
+
+  return frontmatter + `\n${textToInsert}\n` + content;
+}