From 6c6ea65c6202c55ad28ffd66f0b262b07c2ecc94 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Fri, 3 Apr 2026 18:14:16 -0700 Subject: [PATCH] Replace component with typedoc-generated API reference pages Replace the custom component (using ts-morph at build time) with typedoc + typedoc-plugin-markdown to generate dedicated .mdx pages for each exported function, class, interface, and type alias. This gives every API symbol its own URL, making the docs more useful for AI agents reading raw markdown and enabling better cross-linking. - Add typedoc, typedoc-plugin-markdown, and typedoc-plugin-frontmatter - Create per-package typedoc configs for all 7 documented packages - Add typedoc-formatter.mjs plugin for frontmatter injection and MDX escaping - Add copy-api-docs.sh to copy generated output into Fumadocs content tree - Update 31 hand-written MDX files to replace with links to generated pages - Remove docs/lib/tsdoc/ (ts-morph based component) and its dependencies - Add turbo task and build pipeline integration --- .gitignore | 4 + docs/.gitignore | 3 + docs/app/[lang]/docs/[[...slug]]/page.tsx | 2 - .../docs/api-reference/vitest/meta.json | 4 + .../workflow-ai/durable-agent.mdx | 84 +-- .../docs/api-reference/workflow-ai/meta.json | 4 + .../workflow-ai/workflow-chat-transport.mdx | 12 +- .../workflow-api/get-hook-by-token.mdx | 16 +- .../api-reference/workflow-api/get-run.mdx | 38 +- .../api-reference/workflow-api/get-world.mdx | 9 +- .../docs/api-reference/workflow-api/meta.json | 4 + .../workflow-api/resume-hook.mdx | 16 +- .../workflow-api/resume-webhook.mdx | 7 +- .../docs/api-reference/workflow-api/start.mdx | 20 +- .../workflow-errors/entity-conflict-error.mdx | 9 +- .../workflow-errors/hook-not-found-error.mdx | 11 +- .../api-reference/workflow-errors/meta.json | 4 +- .../workflow-errors/run-expired-error.mdx | 9 +- .../step-not-registered-error.mdx | 11 +- .../workflow-errors/throttle-error.mdx | 11 +- .../workflow-errors/too-early-error.mdx | 11 +- .../workflow-not-registered-error.mdx | 11 +- .../workflow-run-cancelled-error.mdx | 11 +- .../workflow-run-failed-error.mdx | 13 +- .../workflow-run-not-found-error.mdx | 11 +- .../workflow-errors/workflow-world-error.mdx | 17 +- .../api-reference/workflow-next/meta.json | 4 + .../api-reference/workflow-serde/meta.json | 8 +- .../workflow-serde/workflow-deserialize.mdx | 12 +- .../workflow-serde/workflow-serialize.mdx | 11 +- .../api-reference/workflow/create-hook.mdx | 28 +- .../api-reference/workflow/create-webhook.mdx | 14 +- .../api-reference/workflow/define-hook.mdx | 25 +- .../api-reference/workflow/fatal-error.mdx | 12 +- .../docs/api-reference/workflow/fetch.mdx | 14 +- .../workflow/get-step-metadata.mdx | 13 +- .../workflow/get-workflow-metadata.mdx | 13 +- .../api-reference/workflow/get-writable.mdx | 14 +- .../docs/api-reference/workflow/meta.json | 8 +- .../workflow/retryable-error.mdx | 17 +- .../docs/api-reference/workflow/sleep.mdx | 7 +- docs/copy-api-docs.sh | 51 ++ docs/lib/tsdoc/base.ts | 501 ------------------ docs/lib/tsdoc/index.ts | 3 - docs/lib/tsdoc/tsdoc.tsx | 435 --------------- docs/lib/tsdoc/types.ts | 43 -- docs/package.json | 4 +- package.json | 4 + packages/ai/package.json | 1 + packages/ai/typedoc.json | 8 + packages/errors/package.json | 3 +- packages/errors/typedoc.json | 8 + packages/next/package.json | 1 + packages/next/typedoc.json | 8 + packages/serde/package.json | 3 +- packages/serde/typedoc.json | 8 + packages/vitest/package.json | 3 +- packages/vitest/typedoc.json | 8 + packages/workflow/package.json | 1 + packages/workflow/typedoc-api.json | 8 + packages/workflow/typedoc.json | 8 + pnpm-lock.yaml | 205 ++++--- turbo.json | 11 + typedoc-formatter.mjs | 95 ++++ typedoc.json | 29 + 65 files changed, 488 insertions(+), 1513 deletions(-) create mode 100644 docs/content/docs/api-reference/vitest/meta.json create mode 100644 docs/content/docs/api-reference/workflow-ai/meta.json create mode 100644 docs/content/docs/api-reference/workflow-api/meta.json create mode 100644 docs/content/docs/api-reference/workflow-next/meta.json create mode 100755 docs/copy-api-docs.sh delete mode 100644 docs/lib/tsdoc/base.ts delete mode 100644 docs/lib/tsdoc/index.ts delete mode 100644 docs/lib/tsdoc/tsdoc.tsx delete mode 100644 docs/lib/tsdoc/types.ts create mode 100644 packages/ai/typedoc.json create mode 100644 packages/errors/typedoc.json create mode 100644 packages/next/typedoc.json create mode 100644 packages/serde/typedoc.json create mode 100644 packages/vitest/typedoc.json create mode 100644 packages/workflow/typedoc-api.json create mode 100644 packages/workflow/typedoc.json create mode 100644 typedoc-formatter.mjs create mode 100644 typedoc.json diff --git a/.gitignore b/.gitignore index ee38f9164c..f2a6a29bf9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,10 @@ packages/swc-plugin-workflow/build-hash.json .DS_Store +# TypeDoc generated output +/packages/*/typedoc-out +/packages/*/typedoc-out-api + # Generated manifest files copied to static asset directories by builders workbench/nextjs-*/public/.well-known/workflow workbench/sveltekit/static/.well-known/workflow diff --git a/docs/.gitignore b/docs/.gitignore index 0805f529d5..f14bdfe123 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -30,6 +30,9 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts +# typedoc-generated API reference pages +/content/docs/api-reference/*/api/ + # pagefind _pagefind/ diff --git a/docs/app/[lang]/docs/[[...slug]]/page.tsx b/docs/app/[lang]/docs/[[...slug]]/page.tsx index e1bca864c3..d6d2e2c9ba 100644 --- a/docs/app/[lang]/docs/[[...slug]]/page.tsx +++ b/docs/app/[lang]/docs/[[...slug]]/page.tsx @@ -22,7 +22,6 @@ import * as AccordionComponents from '@/components/ui/accordion'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { getLLMText, getPageImage, source } from '@/lib/geistdocs/source'; -import { TSDoc } from '@/lib/tsdoc'; // No-op component for world MDX files rendered outside /worlds/ context // These pages redirect to /worlds/[id] but still get statically generated @@ -70,7 +69,6 @@ const Page = async ({ params }: PageProps<'/[lang]/docs/[[...slug]]'>) => { AgentTraces, FluidComputeCallout, Badge, - TSDoc, Step, Steps, ...AccordionComponents, diff --git a/docs/content/docs/api-reference/vitest/meta.json b/docs/content/docs/api-reference/vitest/meta.json new file mode 100644 index 0000000000..74e2343cc3 --- /dev/null +++ b/docs/content/docs/api-reference/vitest/meta.json @@ -0,0 +1,4 @@ +{ + "title": "@workflow/vitest", + "pages": ["...", "--- Generated API Reference ---", "...api"] +} diff --git a/docs/content/docs/api-reference/workflow-ai/durable-agent.mdx b/docs/content/docs/api-reference/workflow-ai/durable-agent.mdx index ba9de4375b..8021340c4e 100644 --- a/docs/content/docs/api-reference/workflow-ai/durable-agent.mdx +++ b/docs/content/docs/api-reference/workflow-ai/durable-agent.mdx @@ -63,77 +63,45 @@ async function myAgent() { ### Class - +> See the full API reference for [`DurableAgent`](/docs/api-reference/workflow-ai/api/agent/durable-agent/classes/DurableAgent). ### DurableAgentOptions - +> See the full API reference for [`DurableAgentOptions`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/DurableAgentOptions). ### DurableAgentStreamOptions - +> See the full API reference for [`DurableAgentStreamOptions`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/DurableAgentStreamOptions). ### DurableAgentStreamResult The result returned from the `stream()` method: - +> See the full API reference for [`DurableAgentStreamResult`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/DurableAgentStreamResult). ### GenerationSettings Settings that control model generation behavior. These can be set on the constructor or overridden per-stream call: - +> See the full API reference for [`GenerationSettings`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/GenerationSettings). ### PrepareStepInfo Information passed to the `prepareStep` callback: - +> See the full API reference for [`PrepareStepInfo`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/PrepareStepInfo). ### PrepareStepResult Return type from the `prepareStep` callback: - +> See the full API reference for [`PrepareStepResult`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/PrepareStepResult). ### TelemetrySettings Configuration for observability and telemetry: - +> See the full API reference for [`TelemetrySettings`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/TelemetrySettings). ### Callbacks @@ -141,31 +109,19 @@ export default TelemetrySettings;`} Called when streaming completes: - +> See the full API reference for [`StreamTextOnFinishCallback`](/docs/api-reference/workflow-ai/api/agent/durable-agent/type-aliases/StreamTextOnFinishCallback). #### StreamTextOnErrorCallback Called when an error occurs: - +> See the full API reference for [`StreamTextOnErrorCallback`](/docs/api-reference/workflow-ai/api/agent/durable-agent/type-aliases/StreamTextOnErrorCallback). #### StreamTextOnAbortCallback Called when the operation is aborted: - +> See the full API reference for [`StreamTextOnAbortCallback`](/docs/api-reference/workflow-ai/api/agent/durable-agent/type-aliases/StreamTextOnAbortCallback). ### Advanced Types @@ -173,31 +129,19 @@ export default StreamTextOnAbortCallback;`} Function to repair malformed tool calls: - +> See the full API reference for [`ToolCallRepairFunction`](/docs/api-reference/workflow-ai/api/agent/durable-agent/type-aliases/ToolCallRepairFunction). #### StreamTextTransform Transform applied to the stream: - +> See the full API reference for [`StreamTextTransform`](/docs/api-reference/workflow-ai/api/agent/durable-agent/type-aliases/StreamTextTransform). #### OutputSpecification Specification for structured output parsing: - +> See the full API reference for [`OutputSpecification`](/docs/api-reference/workflow-ai/api/agent/durable-agent/interfaces/OutputSpecification). ## Key Features diff --git a/docs/content/docs/api-reference/workflow-ai/meta.json b/docs/content/docs/api-reference/workflow-ai/meta.json new file mode 100644 index 0000000000..a3005a0fcb --- /dev/null +++ b/docs/content/docs/api-reference/workflow-ai/meta.json @@ -0,0 +1,4 @@ +{ + "title": "@workflow/ai", + "pages": ["...", "--- Generated API Reference ---", "...api"] +} diff --git a/docs/content/docs/api-reference/workflow-ai/workflow-chat-transport.mdx b/docs/content/docs/api-reference/workflow-ai/workflow-chat-transport.mdx index bde3bd1d58..974fa7ebc0 100644 --- a/docs/content/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +++ b/docs/content/docs/api-reference/workflow-ai/workflow-chat-transport.mdx @@ -42,19 +42,11 @@ export default function Chat() { ### Class - +> See the full API reference for [`WorkflowChatTransport`](/docs/api-reference/workflow-ai/api/index/classes/WorkflowChatTransport). ### WorkflowChatTransportOptions - +> See the full API reference for [`WorkflowChatTransportOptions`](/docs/api-reference/workflow-ai/api/index/interfaces/WorkflowChatTransportOptions). ## Key Features diff --git a/docs/content/docs/api-reference/workflow-api/get-hook-by-token.mdx b/docs/content/docs/api-reference/workflow-api/get-hook-by-token.mdx index 028c9b4047..170b568d54 100644 --- a/docs/content/docs/api-reference/workflow-api/get-hook-by-token.mdx +++ b/docs/content/docs/api-reference/workflow-api/get-hook-by-token.mdx @@ -27,23 +27,11 @@ export async function POST(request: Request) { ### Parameters - +> See the full API reference for [`getHookByToken`](/docs/api-reference/workflow-api/api/functions/getHookByToken). ### Returns -Returns a `Promise` that resolves to: - - +Returns a `Promise` that resolves to the hook object with run and metadata information. ## Examples diff --git a/docs/content/docs/api-reference/workflow-api/get-run.mdx b/docs/content/docs/api-reference/workflow-api/get-run.mdx index 47be7831de..da8e80ff35 100644 --- a/docs/content/docs/api-reference/workflow-api/get-run.mdx +++ b/docs/content/docs/api-reference/workflow-api/get-run.mdx @@ -21,59 +21,33 @@ const run = getRun("my-run-id"); ### Parameters - +> See the full API reference for [`getRun`](/docs/api-reference/workflow-api/api/functions/getRun). ### Returns Returns a `Run` object: - +> See the full API reference for [`Run`](/docs/api-reference/workflow-api/api/classes/Run). #### WorkflowReadableStream `run.getReadable()` returns a `WorkflowReadableStream` — a standard `ReadableStream` extended with a `getTailIndex()` helper: - +> See the full API reference for [`WorkflowReadableStream`](/docs/api-reference/workflow-api/api/type-aliases/WorkflowReadableStream). `getTailIndex()` returns the index of the last known chunk (0-based), or `-1` when no chunks have been written. This is useful when building [reconnection endpoints](/docs/ai/resumable-streams) that need to inform clients where the stream starts. #### WorkflowReadableStreamOptions - +> See the full API reference for [`WorkflowReadableStreamOptions`](/docs/api-reference/workflow-api/api/interfaces/WorkflowReadableStreamOptions). #### StopSleepOptions - +> See the full API reference for [`StopSleepOptions`](/docs/api-reference/workflow-api/api/interfaces/StopSleepOptions). #### StopSleepResult - +> See the full API reference for [`StopSleepResult`](/docs/api-reference/workflow-api/api/interfaces/StopSleepResult). ## Examples diff --git a/docs/content/docs/api-reference/workflow-api/get-world.mdx b/docs/content/docs/api-reference/workflow-api/get-world.mdx index 69c3396b26..5f7aacfce3 100644 --- a/docs/content/docs/api-reference/workflow-api/get-world.mdx +++ b/docs/content/docs/api-reference/workflow-api/get-world.mdx @@ -25,14 +25,7 @@ This function does not accept any parameters. ### Returns -Returns a `World` object: - - +Returns the [`World`](/docs/deploying/building-a-world) instance. ## World SDK diff --git a/docs/content/docs/api-reference/workflow-api/meta.json b/docs/content/docs/api-reference/workflow-api/meta.json new file mode 100644 index 0000000000..467bc369b1 --- /dev/null +++ b/docs/content/docs/api-reference/workflow-api/meta.json @@ -0,0 +1,4 @@ +{ + "title": "workflow/api", + "pages": ["...", "--- Generated API Reference ---", "...api"] +} diff --git a/docs/content/docs/api-reference/workflow-api/resume-hook.mdx b/docs/content/docs/api-reference/workflow-api/resume-hook.mdx index e98b8c092f..9c28ed24c8 100644 --- a/docs/content/docs/api-reference/workflow-api/resume-hook.mdx +++ b/docs/content/docs/api-reference/workflow-api/resume-hook.mdx @@ -38,23 +38,11 @@ export async function POST(request: Request) { ### Parameters - +> See the full API reference for [`resumeHook`](/docs/api-reference/workflow-api/api/functions/resumeHook). ### Returns -Returns a `Promise` that resolves to: - - +Returns a `Promise` that resolves to the hook object with run and metadata information. ## Examples diff --git a/docs/content/docs/api-reference/workflow-api/resume-webhook.mdx b/docs/content/docs/api-reference/workflow-api/resume-webhook.mdx index b4ee9f2008..fdf5a93088 100644 --- a/docs/content/docs/api-reference/workflow-api/resume-webhook.mdx +++ b/docs/content/docs/api-reference/workflow-api/resume-webhook.mdx @@ -41,12 +41,7 @@ export async function POST(request: Request) { ### Parameters - +> See the full API reference for [`resumeWebhook`](/docs/api-reference/workflow-api/api/functions/resumeWebhook). ### Returns diff --git a/docs/content/docs/api-reference/workflow-api/start.mdx b/docs/content/docs/api-reference/workflow-api/start.mdx index 293ba56741..b5ba7c1032 100644 --- a/docs/content/docs/api-reference/workflow-api/start.mdx +++ b/docs/content/docs/api-reference/workflow-api/start.mdx @@ -20,31 +20,17 @@ const run = await start(myWorkflow); // [!code highlight] ### Parameters - +> See the full API reference for [`start`](/docs/api-reference/workflow-api/api/functions/start). #### StartOptions - +> See the full API reference for [`StartOptions`](/docs/api-reference/workflow-api/api/type-aliases/StartOptions). ### Returns Returns a `Run` object: - +> See the full API reference for [`Run`](/docs/api-reference/workflow-api/api/classes/Run). Learn more about [`WorkflowReadableStreamOptions`](/docs/api-reference/workflow-api/get-run#workflowreadablestreamoptions). diff --git a/docs/content/docs/api-reference/workflow-errors/entity-conflict-error.mdx b/docs/content/docs/api-reference/workflow-errors/entity-conflict-error.mdx index d249089853..2a3eed9834 100644 --- a/docs/content/docs/api-reference/workflow-errors/entity-conflict-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/entity-conflict-error.mdx @@ -35,14 +35,7 @@ try { ### Properties - +> See the full API reference for [`EntityConflictError`](/docs/api-reference/workflow-errors/api/classes/EntityConflictError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/hook-not-found-error.mdx b/docs/content/docs/api-reference/workflow-errors/hook-not-found-error.mdx index 20dd65095d..10280fdf41 100644 --- a/docs/content/docs/api-reference/workflow-errors/hook-not-found-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/hook-not-found-error.mdx @@ -33,16 +33,7 @@ try { ### Properties - +> See the full API reference for [`HookNotFoundError`](/docs/api-reference/workflow-errors/api/classes/HookNotFoundError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/meta.json b/docs/content/docs/api-reference/workflow-errors/meta.json index 690112dffa..aef37e535a 100644 --- a/docs/content/docs/api-reference/workflow-errors/meta.json +++ b/docs/content/docs/api-reference/workflow-errors/meta.json @@ -11,6 +11,8 @@ "throttle-error", "entity-conflict-error", "run-expired-error", - "too-early-error" + "too-early-error", + "--- Generated API Reference ---", + "...api" ] } diff --git a/docs/content/docs/api-reference/workflow-errors/run-expired-error.mdx b/docs/content/docs/api-reference/workflow-errors/run-expired-error.mdx index aa9e6eb4a7..7befdc8927 100644 --- a/docs/content/docs/api-reference/workflow-errors/run-expired-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/run-expired-error.mdx @@ -33,14 +33,7 @@ try { ### Properties - +> See the full API reference for [`RunExpiredError`](/docs/api-reference/workflow-errors/api/classes/RunExpiredError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/step-not-registered-error.mdx b/docs/content/docs/api-reference/workflow-errors/step-not-registered-error.mdx index a4c4ec355b..f60f33e7bc 100644 --- a/docs/content/docs/api-reference/workflow-errors/step-not-registered-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/step-not-registered-error.mdx @@ -25,16 +25,7 @@ if (StepNotRegisteredError.is(error)) { // [!code highlight] ### Properties - +> See the full API reference for [`StepNotRegisteredError`](/docs/api-reference/workflow-errors/api/classes/StepNotRegisteredError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/throttle-error.mdx b/docs/content/docs/api-reference/workflow-errors/throttle-error.mdx index 4d7bc7596c..dff6c96007 100644 --- a/docs/content/docs/api-reference/workflow-errors/throttle-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/throttle-error.mdx @@ -35,16 +35,7 @@ try { ### Properties - +> See the full API reference for [`ThrottleError`](/docs/api-reference/workflow-errors/api/classes/ThrottleError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/too-early-error.mdx b/docs/content/docs/api-reference/workflow-errors/too-early-error.mdx index e333cbd0b7..252b0cb92b 100644 --- a/docs/content/docs/api-reference/workflow-errors/too-early-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/too-early-error.mdx @@ -35,16 +35,7 @@ try { ### Properties - +> See the full API reference for [`TooEarlyError`](/docs/api-reference/workflow-errors/api/classes/TooEarlyError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx b/docs/content/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx index b2eee91ead..d72be414a0 100644 --- a/docs/content/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx @@ -25,16 +25,7 @@ if (WorkflowNotRegisteredError.is(error)) { // [!code highlight] ### Properties - +> See the full API reference for [`WorkflowNotRegisteredError`](/docs/api-reference/workflow-errors/api/classes/WorkflowNotRegisteredError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx b/docs/content/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx index 3c3a6bc5ed..da15333bd5 100644 --- a/docs/content/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx @@ -29,16 +29,7 @@ try { ### Properties - +> See the full API reference for [`WorkflowRunCancelledError`](/docs/api-reference/workflow-errors/api/classes/WorkflowRunCancelledError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx b/docs/content/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx index f4136b4a65..dabb554f53 100644 --- a/docs/content/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx @@ -33,18 +33,7 @@ try { ### Properties - +> See the full API reference for [`WorkflowRunFailedError`](/docs/api-reference/workflow-errors/api/classes/WorkflowRunFailedError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx b/docs/content/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx index b27d7321c7..1c0c7e7d85 100644 --- a/docs/content/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx @@ -29,16 +29,7 @@ try { ### Properties - +> See the full API reference for [`WorkflowRunNotFoundError`](/docs/api-reference/workflow-errors/api/classes/WorkflowRunNotFoundError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-errors/workflow-world-error.mdx b/docs/content/docs/api-reference/workflow-errors/workflow-world-error.mdx index ac12e75ef3..3a040ad55c 100644 --- a/docs/content/docs/api-reference/workflow-errors/workflow-world-error.mdx +++ b/docs/content/docs/api-reference/workflow-errors/workflow-world-error.mdx @@ -37,22 +37,7 @@ try { ### Properties - +> See the full API reference for [`WorkflowWorldError`](/docs/api-reference/workflow-errors/api/classes/WorkflowWorldError). ### Static Methods diff --git a/docs/content/docs/api-reference/workflow-next/meta.json b/docs/content/docs/api-reference/workflow-next/meta.json new file mode 100644 index 0000000000..0fffea2425 --- /dev/null +++ b/docs/content/docs/api-reference/workflow-next/meta.json @@ -0,0 +1,4 @@ +{ + "title": "workflow/next", + "pages": ["...", "--- Generated API Reference ---", "...api"] +} diff --git a/docs/content/docs/api-reference/workflow-serde/meta.json b/docs/content/docs/api-reference/workflow-serde/meta.json index 856b0fc93e..c1dc88f107 100644 --- a/docs/content/docs/api-reference/workflow-serde/meta.json +++ b/docs/content/docs/api-reference/workflow-serde/meta.json @@ -1,3 +1,9 @@ { - "pages": ["...", "workflow-serialize", "workflow-deserialize"] + "pages": [ + "...", + "workflow-serialize", + "workflow-deserialize", + "--- Generated API Reference ---", + "...api" + ] } diff --git a/docs/content/docs/api-reference/workflow-serde/workflow-deserialize.mdx b/docs/content/docs/api-reference/workflow-serde/workflow-deserialize.mdx index 6b2e84846c..dc2f2208b3 100644 --- a/docs/content/docs/api-reference/workflow-serde/workflow-deserialize.mdx +++ b/docs/content/docs/api-reference/workflow-serde/workflow-deserialize.mdx @@ -34,17 +34,7 @@ static [WORKFLOW_DESERIALIZE](data: SerializableData): T ### Parameters - +> See the full API reference for [`WORKFLOW_DESERIALIZE`](/docs/api-reference/workflow-serde/api/variables/WORKFLOW_DESERIALIZE). ### Returns diff --git a/docs/content/docs/api-reference/workflow-serde/workflow-serialize.mdx b/docs/content/docs/api-reference/workflow-serde/workflow-serialize.mdx index 5c70c95e1d..53edc5537f 100644 --- a/docs/content/docs/api-reference/workflow-serde/workflow-serialize.mdx +++ b/docs/content/docs/api-reference/workflow-serde/workflow-serialize.mdx @@ -34,16 +34,7 @@ static [WORKFLOW_SERIALIZE](instance: T): SerializableData ### Parameters - +> See the full API reference for [`WORKFLOW_SERIALIZE`](/docs/api-reference/workflow-serde/api/variables/WORKFLOW_SERIALIZE). ### Returns diff --git a/docs/content/docs/api-reference/workflow/create-hook.mdx b/docs/content/docs/api-reference/workflow/create-hook.mdx index 00e5afccd1..51c51372e8 100644 --- a/docs/content/docs/api-reference/workflow/create-hook.mdx +++ b/docs/content/docs/api-reference/workflow/create-hook.mdx @@ -29,39 +29,19 @@ export async function hookWorkflow() { ### Parameters - +> See the full API reference for [`createHook`](/docs/api-reference/workflow/api/functions/createHook). #### HookOptions - +> See the full API reference for [`HookOptions`](/docs/api-reference/workflow/api/interfaces/HookOptions). ### Returns - +> See the full API reference for [`createHook`](/docs/api-reference/workflow/api/functions/createHook). #### Hook - +> See the full API reference for [`Hook`](/docs/api-reference/workflow/api/interfaces/Hook). The returned `Hook` object also implements `AsyncIterable`, which allows you to iterate over incoming payloads using `for await...of` syntax. diff --git a/docs/content/docs/api-reference/workflow/create-webhook.mdx b/docs/content/docs/api-reference/workflow/create-webhook.mdx index a6d56151b4..c0fe1f6be9 100644 --- a/docs/content/docs/api-reference/workflow/create-webhook.mdx +++ b/docs/content/docs/api-reference/workflow/create-webhook.mdx @@ -35,21 +35,11 @@ export async function webhookWorkflow() { ### Parameters - +> See the full API reference for [`createWebhook`](/docs/api-reference/workflow/api/functions/createWebhook). ### Returns - +> See the full API reference for [`createWebhook`](/docs/api-reference/workflow/api/functions/createWebhook). The returned `Webhook` object has: diff --git a/docs/content/docs/api-reference/workflow/define-hook.mdx b/docs/content/docs/api-reference/workflow/define-hook.mdx index ad0d697f30..34a1782585 100644 --- a/docs/content/docs/api-reference/workflow/define-hook.mdx +++ b/docs/content/docs/api-reference/workflow/define-hook.mdx @@ -37,32 +37,11 @@ export async function nameWorkflow() { ### Parameters - +> See the full API reference for [`defineHook`](/docs/api-reference/workflow/api/functions/defineHook). ### Returns - { - /** - -* Creates a new hook with the defined payload type. - */ - create: (options?: HookOptions) => Hook; - - /** - -* Resumes a hook by sending a payload with the defined type. - */ - resume: (token: string, payload: T) => Promise; -} -export default DefineHook;`} -/> +> See the full API reference for [`TypedHook`](/docs/api-reference/workflow/api/interfaces/TypedHook). ## Examples diff --git a/docs/content/docs/api-reference/workflow/fatal-error.mdx b/docs/content/docs/api-reference/workflow/fatal-error.mdx index 8c31e1e611..a373e8ce71 100644 --- a/docs/content/docs/api-reference/workflow/fatal-error.mdx +++ b/docs/content/docs/api-reference/workflow/fatal-error.mdx @@ -31,14 +31,4 @@ async function fallibleStep() { ### Parameters - +> See the full API reference for [`FatalError`](/docs/api-reference/workflow/api/classes/FatalError). diff --git a/docs/content/docs/api-reference/workflow/fetch.mdx b/docs/content/docs/api-reference/workflow/fetch.mdx index e1d18f44ac..cc5879e337 100644 --- a/docs/content/docs/api-reference/workflow/fetch.mdx +++ b/docs/content/docs/api-reference/workflow/fetch.mdx @@ -35,23 +35,13 @@ async function apiWorkflow() { Accepts the same arguments as web [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) - +> See the full API reference for [`fetch`](/docs/api-reference/workflow/api/functions/fetch). ### Returns Returns the same response as web [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) - +> See the full API reference for [`fetch`](/docs/api-reference/workflow/api/functions/fetch). ## Examples diff --git a/docs/content/docs/api-reference/workflow/get-step-metadata.mdx b/docs/content/docs/api-reference/workflow/get-step-metadata.mdx index 27cdfaa754..53a1421c16 100644 --- a/docs/content/docs/api-reference/workflow/get-step-metadata.mdx +++ b/docs/content/docs/api-reference/workflow/get-step-metadata.mdx @@ -65,17 +65,8 @@ async function chargeUser(userId: string, amount: number) { ### Parameters - +> See the full API reference for [`getStepMetadata`](/docs/api-reference/workflow/api/functions/getStepMetadata). ### Returns - +> See the full API reference for [`StepMetadata`](/docs/api-reference/workflow/api/interfaces/StepMetadata). diff --git a/docs/content/docs/api-reference/workflow/get-workflow-metadata.mdx b/docs/content/docs/api-reference/workflow/get-workflow-metadata.mdx index 23b3573a2d..1ead506145 100644 --- a/docs/content/docs/api-reference/workflow/get-workflow-metadata.mdx +++ b/docs/content/docs/api-reference/workflow/get-workflow-metadata.mdx @@ -33,17 +33,8 @@ async function testWorkflow() { ### Parameters - +> See the full API reference for [`getWorkflowMetadata`](/docs/api-reference/workflow/api/functions/getWorkflowMetadata). ### Returns - +> See the full API reference for [`WorkflowMetadata`](/docs/api-reference/workflow/api/interfaces/WorkflowMetadata). diff --git a/docs/content/docs/api-reference/workflow/get-writable.mdx b/docs/content/docs/api-reference/workflow/get-writable.mdx index a82257bcd4..6503a12f3b 100644 --- a/docs/content/docs/api-reference/workflow/get-writable.mdx +++ b/docs/content/docs/api-reference/workflow/get-writable.mdx @@ -51,21 +51,11 @@ async function writeToStream(writable: WritableStream) { ### Parameters - +> See the full API reference for [`getWritable`](/docs/api-reference/workflow/api/functions/getWritable). ### Returns - +> See the full API reference for [`getWritable`](/docs/api-reference/workflow/api/functions/getWritable). Returns a `WritableStream` where `W` is the type of data you plan to write to the stream. diff --git a/docs/content/docs/api-reference/workflow/meta.json b/docs/content/docs/api-reference/workflow/meta.json index b695df1dca..17630ac1cb 100644 --- a/docs/content/docs/api-reference/workflow/meta.json +++ b/docs/content/docs/api-reference/workflow/meta.json @@ -1,3 +1,9 @@ { - "pages": ["...", "fatal-error", "retryable-error"] + "pages": [ + "...", + "fatal-error", + "retryable-error", + "--- Generated API Reference ---", + "...api" + ] } diff --git a/docs/content/docs/api-reference/workflow/retryable-error.mdx b/docs/content/docs/api-reference/workflow/retryable-error.mdx index 7783e4c981..53193b64f1 100644 --- a/docs/content/docs/api-reference/workflow/retryable-error.mdx +++ b/docs/content/docs/api-reference/workflow/retryable-error.mdx @@ -35,24 +35,11 @@ The difference between `Error` and `RetryableError` may not be entirely obvious, ### Parameters - +> See the full API reference for [`RetryableError`](/docs/api-reference/workflow/api/classes/RetryableError). #### RetryableErrorOptions - +> See the full API reference for [`RetryableErrorOptions`](/docs/api-reference/workflow/api/interfaces/RetryableErrorOptions). ## Examples diff --git a/docs/content/docs/api-reference/workflow/sleep.mdx b/docs/content/docs/api-reference/workflow/sleep.mdx index 86d7b14f9e..275f93d79e 100644 --- a/docs/content/docs/api-reference/workflow/sleep.mdx +++ b/docs/content/docs/api-reference/workflow/sleep.mdx @@ -30,12 +30,7 @@ async function testWorkflow() { ### Parameters - +> See the full API reference for [`sleep`](/docs/api-reference/workflow/api/functions/sleep). ## Examples diff --git a/docs/copy-api-docs.sh b/docs/copy-api-docs.sh new file mode 100755 index 0000000000..2dbf001177 --- /dev/null +++ b/docs/copy-api-docs.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Copies typedoc-generated API docs from each package into the docs content directory. +# Each package's output goes into an `api/` subdirectory under its API reference section. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +CONTENT_DIR="$SCRIPT_DIR/content/docs/api-reference" + +copy_docs() { + local src="$1" dest="$2" + if [ ! -d "$src" ]; then + echo " [skip] $src (not found)" + return + fi + rm -rf "$dest" + mkdir -p "$dest" + cp -r "$src"/* "$dest" + echo " [copy] $src -> $dest" +} + +echo "Copying typedoc-generated API docs..." + +# workflow (main package) +copy_docs "$ROOT_DIR/packages/workflow/typedoc-out" "$CONTENT_DIR/workflow/api" + +# workflow/api +copy_docs "$ROOT_DIR/packages/workflow/typedoc-out-api" "$CONTENT_DIR/workflow-api/api" + +# workflow/errors +copy_docs "$ROOT_DIR/packages/errors/typedoc-out" "$CONTENT_DIR/workflow-errors/api" + +# @workflow/serde +copy_docs "$ROOT_DIR/packages/serde/typedoc-out" "$CONTENT_DIR/workflow-serde/api" + +# workflow/next +copy_docs "$ROOT_DIR/packages/next/typedoc-out" "$CONTENT_DIR/workflow-next/api" + +# @workflow/ai +copy_docs "$ROOT_DIR/packages/ai/typedoc-out" "$CONTENT_DIR/workflow-ai/api" + +# @workflow/vitest +copy_docs "$ROOT_DIR/packages/vitest/typedoc-out" "$CONTENT_DIR/vitest/api" + +# Add meta.json to type-aliases directories for clean sidebar labels +find "$CONTENT_DIR" -type d -name "type-aliases" | while read -r dir; do + echo '{ "title": "Type Aliases" }' > "$dir/meta.json" +done + +echo "Done." diff --git a/docs/lib/tsdoc/base.ts b/docs/lib/tsdoc/base.ts deleted file mode 100644 index 7b5393f484..0000000000 --- a/docs/lib/tsdoc/base.ts +++ /dev/null @@ -1,501 +0,0 @@ -import path from 'node:path'; -import type { - ExportedDeclarations, - Node as TsNode, - Symbol as TsSymbol, - Type, -} from 'ts-morph'; -import { Project, SyntaxKind, ts } from 'ts-morph'; -import type { - BaseArgs, - GeneratedDefinition, - GeneratedFunction, - GeneratedType, - Tags, - TypeField, -} from './types'; - -const DEFAULT_FILENAME = '$.ts'; - -const project = new Project({ - compilerOptions: { - exactOptionalPropertyTypes: true, - strictNullChecks: true, - moduleResolution: ts.ModuleResolutionKind.Bundler, - esModuleInterop: true, - allowSyntheticDefaultImports: true, - baseUrl: process.cwd(), - paths: { - '@workflow/core/*': ['./packages/core/src/*'], - }, - }, -}); - -const IGNORED_TYPES = new Set([ - 'Date', - 'RegExp', - 'ReactElement', - 'Element', - 'CSSProperties', -]); - -let compilerObject: ts.TypeChecker; - -/** - * Finds and returns the export declaration for the specified export name - */ -function findExportDeclaration( - sourceFile: ReturnType, - exportName: string -): ExportedDeclarations { - const output: ExportedDeclarations[] = []; - for (const [key, declaration] of sourceFile.getExportedDeclarations()) { - if (key === exportName) output.push(...declaration); - } - - let declaration = output[0]; - if (!declaration) { - // Try to handle re-exports by looking for the actual function - const exportAssignments = sourceFile.getExportAssignments(); - const defaultExport = exportAssignments.find( - (exp) => exp.isExportEquals() === false - ); - - if (defaultExport && exportName === 'default') { - const expression = defaultExport.getExpression(); - if (expression) { - // Try to resolve the symbol from the expression - const symbol = expression.getSymbol(); - if (symbol) { - const declarations = symbol.getDeclarations(); - if (declarations.length > 0) { - declaration = declarations[0] as ExportedDeclarations; - } - } - } - } - - if (!declaration) { - throw new Error( - `Can't find "${exportName}" declaration. This might be a re-exported function from another module. Try providing the full function definition instead of re-exporting.` - ); - } - } - - return declaration; -} - -/** - * Generates a documentation definition for a given code snippet and export name. - */ -export function generateDefinition({ - code, - exportName = 'default', - flattened = false, -}: BaseArgs): GeneratedDefinition & (GeneratedType | GeneratedFunction) { - compilerObject ??= project.getTypeChecker().compilerObject; - - const sourceFile = project.createSourceFile(DEFAULT_FILENAME, code, { - overwrite: true, - }); - - const declaration = findExportDeclaration(sourceFile, exportName); - - const declarationFilePath = declaration.getSourceFile().getFilePath(); - const filePath = path.relative(process.cwd(), declarationFilePath); - const symbol = declaration.getSymbolOrThrow(); - const { comment, tags } = getCommentAndTags(declaration); - const description = ts.displayPartsToString(comment); - - if (tags.returns && typeof tags.returns === 'string') { - tags.returns = replaceJsDocLinks(tags.returns); - } - - const definition: GeneratedDefinition = { - ...(filePath !== DEFAULT_FILENAME && { filePath }), - name: symbol.getName(), - ...(description && { description }), - ...(Object.keys(tags).length && { tags }), - }; - - const declarationType = declaration.getType(); - const callSignatures = declarationType.getCallSignatures(); - const isFunction = callSignatures.length > 0; - - if (!isFunction) { - const entries = declarationType - .getProperties() - .filter((prop) => { - const propName = prop.getName(); - // Filter out Symbol properties and internal methods - if (propName.startsWith('__') || propName.includes('@')) { - return false; - } - // Filter out 'then' method for Promise-like objects - if (propName === 'then') { - return false; - } - return true; - }) - .flatMap((prop) => - getDocEntry({ - symbol: prop, - declaration, - flattened, - }) - ) - .filter((entry) => !entry.tags || !('internal' in entry.tags)); - - if (!entries.length) { - const typeName = declarationType.getText(); - if (typeName === 'any') { - throw new Error( - 'Your type is resolved as "any", it seems like you have an issue in "generateDefinition.code" argument.' - ); - } - throw new Error( - `No properties found, check if your type "${typeName}" exist.` - ); - } - - return { - ...definition, - entries, - }; - } - - return { - ...definition, - signatures: callSignatures.map((signature) => { - const params = signature.getParameters(); - - // Get JSDoc tags from the signature for @param descriptions - const signatureDecl = signature.getDeclaration(); - const signatureTags = - 'getJsDocs' in signatureDecl - ? signatureDecl - .getJsDocs() - .flatMap((jsDoc: any) => - jsDoc - .getTags() - .filter( - (tag: any) => - tag.getTagName() === 'param' || - tag.getTagName() === 'throws' - ) - ) - : []; - - const typeParams = params.flatMap((param) => { - const baseEntry = getDocEntry({ - symbol: param, - declaration, - flattened, - }); - - // Try to find @param description from signature JSDoc - const paramName = param.getName(); - const paramTag = signatureTags.find((tag: any) => { - const tagText = tag.getText(); - return tagText.includes(paramName); - }); - - if (paramTag && !Array.isArray(baseEntry)) { - let tagText = paramTag.getText(); - tagText = tagText.replace(/^\s*\*\s*/, ''); - const match = tagText.match( - new RegExp(`${paramName}\\s*-\\s*(.+?)(?:\\s*\\*)?$`, 's') - ); - if (match) { - baseEntry.description = replaceJsDocLinks( - match[1] - .replace(/\s*\*\s*$/, '') - .replace(/^\s*\*\s*/gm, '') - .trim() - ); - } - } - - return baseEntry; - }); - - const returnType = signature - .getDeclaration() - .getSignature() - .getReturnType(); - - let flattenedReturnType: GeneratedFunction['signatures'][number]['returns'] = - flattened && shouldFlattenType(returnType) - ? returnType.getProperties().flatMap((childProp) => - getDocEntry({ - symbol: childProp, - declaration, - flattened, - }) - ) - : []; - - if (!flattenedReturnType.length) { - flattenedReturnType = { - type: getFormattedText(returnType), - }; - } - - return { - params: typeParams, - returns: flattenedReturnType, - throws: tags.throws as string[] | undefined, - }; - }), - }; -} - -/** - * Gets the comment and tags for a given declaration. - */ -function getCommentAndTags(declaration: ExportedDeclarations): { - comment: ts.SymbolDisplayPart[]; - tags: Tags; -} { - const symbol = declaration.getSymbolOrThrow(); - const comment = symbol.compilerSymbol.getDocumentationComment(compilerObject); - - if (!comment.length) { - const aliasSymbol = declaration.getType().getAliasSymbol(); - if (aliasSymbol) { - return { - comment: - aliasSymbol.compilerSymbol.getDocumentationComment(compilerObject), - tags: getTags(aliasSymbol), - }; - } - } - - return { - comment, - tags: getTags(symbol), - }; -} - -/** - * Gets a documentation entry for a given symbol. - */ -function getDocEntry({ - symbol, - declaration, - flattened, - prefix = '', -}: { - symbol: TsSymbol; - declaration: ExportedDeclarations; - flattened: boolean; - prefix?: string; -}): TypeField | TypeField[] { - const originalSubType = project - .getTypeChecker() - .getTypeOfSymbolAtLocation(symbol, declaration); - const valueDeclaration = symbol.getValueDeclaration(); - const isFunctionParameter = - valueDeclaration && valueDeclaration.getKind() === SyntaxKind.Parameter; - - const subType = isFunctionParameter - ? originalSubType.getNonNullableType() - : originalSubType; - - if (flattened && shouldFlattenType(subType)) { - return subType.getProperties().flatMap((childProp) => { - const childPrefix = isFunctionParameter - ? symbol.getName().replace(/^_+/, '') - : symbol.getName(); - const newPrefix = - typeof +childPrefix === 'number' && !Number.isNaN(+childPrefix) - ? `[${childPrefix}] ${originalSubType.isNullable() ? '?' : ''}` - : childPrefix; - return getDocEntry({ - symbol: childProp, - declaration, - flattened, - prefix: prexify(prefix, newPrefix), - }); - }); - } - - const tags = getTags(symbol); - const name = symbol.getName(); - const typeDescription = replaceJsDocLinks( - ts.displayPartsToString( - symbol.compilerSymbol.getDocumentationComment(compilerObject) - ) - ).replace(/^- /, ''); - - const isOptional = isFunctionParameter - ? (valueDeclaration.asKind(SyntaxKind.Parameter)?.isOptional() ?? false) - : symbol.isOptional(); - - const typeName = getTypeName({ - tags, - symbol, - subType, - valueDeclaration, - }); - - return { - name: prexify(prefix, name), - type: typeName, - ...(typeDescription && { description: typeDescription }), - ...(Object.keys(tags).length && { tags }), - ...(isOptional && { optional: isOptional }), - }; -} - -function getTypeName({ - tags, - symbol, - subType, - valueDeclaration, -}: { - tags: Tags; - symbol: TsSymbol; - subType: Type; - valueDeclaration: TsNode | undefined; -}) { - const aliasSymbol = subType.getAliasSymbol(); - const subTypeTags = aliasSymbol ? getTags(aliasSymbol) : {}; - const remarksValue = tags.remarks || subTypeTags.remarks; - const typeName = - typeof remarksValue === 'string' - ? remarksValue.match(/^`(?.+)`/)?.groups?.name - : undefined; - - if (typeName) { - return typeName; - } - - const declarationNode = symbol - .getDeclarations() - .find( - (d) => - ts.isPropertySignature(d.compilerNode) || ts.isParameter(d.compilerNode) - ); - const typeNode = - declarationNode?.asKind(SyntaxKind.PropertySignature) ?? - declarationNode?.asKind(SyntaxKind.Parameter); - const t = typeNode?.getTypeNode()?.getText(); - - const useTypeNode = - t && - (t.startsWith('Partial<') || - ['React.ReactNode', 'React.ReactElement'].includes(t)); - - if (useTypeNode) { - return t; - } - - const isInline = 'inline' in tags || 'inline' in subTypeTags; - - if (!isInline) { - const typeOf = valueDeclaration?.getType() ?? symbol.getDeclaredType(); - return typeOf.isUnknown() ? 'unknown' : getFormattedText(subType); - } - - const [signature] = subType.getCallSignatures(); - const isFunction = !!signature; - - if (isFunction) { - const params = signature.getParameters().map((param) => { - const paramDecl = param.getDeclarations()[0]!; - const paramType = project - .getTypeChecker() - .getTypeOfSymbolAtLocation(param, paramDecl); - const inlineParamAlias = paramType.getNonNullableType().getAliasSymbol(); - const paramTags = inlineParamAlias && getTags(inlineParamAlias); - - const paramTypeStr = - paramTags && 'inline' in paramTags - ? inlineParamAlias - .getDeclarations()[0]! - .asKindOrThrow(SyntaxKind.TypeAliasDeclaration) - .getTypeNodeOrThrow() - .getText() - : getFormattedText(paramType); - const optional = paramDecl - .asKindOrThrow(SyntaxKind.Parameter) - .isOptional(); - - return `${param.getName()}${optional ? '?' : ''}: ${paramTypeStr}`; - }); - - return `(${params.join(', ')}) => ${getFormattedText(signature.getReturnType())}`; - } - - const [aliasDecl] = aliasSymbol!.getDeclarations(); - if (!aliasDecl) { - throw new Error("Can't find alias declaration for type."); - } - const inlineNode = aliasDecl - .asKindOrThrow(SyntaxKind.TypeAliasDeclaration) - .getTypeNodeOrThrow(); - return inlineNode.getText(); -} - -function prexify(prefix: string, name: string): string { - return prefix ? [prefix, name].join('.') : name; -} - -function shouldFlattenType(t: Type): boolean { - if ( - !t.isObject() || - t.isArray() || - t.isTuple() || - t.getCallSignatures().length > 0 || - t.getText() === '{}' || - !t.getProperties().length - ) { - return false; - } - - try { - const baseName = t.getSymbolOrThrow().getName(); - if (IGNORED_TYPES.has(baseName)) return false; - return t.isInterface() || baseName === '__type' || baseName === '__object'; - } catch { - console.error(`Symbol "${t.getText()}" isn't found.`); - return false; - } -} - -function getTags(prop: TsSymbol): Tags { - const tags: Record = Object.create(null); - for (const tag of prop.getJsDocTags()) { - const tagName = tag.getName(); - const tagValue = ts.displayPartsToString(tag.getText()); - switch (tagName) { - case 'throws': - if (!tags.throws) { - tags.throws = []; - } - (tags.throws as string[]).push(tagValue); - break; - case 'then': - continue; - default: - if (tagName in tags) { - tags[tagName] += `\n${tagValue}`; - } else { - tags[tagName] = tagValue; - } - } - } - return tags; -} - -function getFormattedText(t: Type): string { - return t.getText( - undefined, - ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope - ); -} - -function replaceJsDocLinks(md: string): string { - return md.replaceAll(/{@link (?[^}]*)}/g, '$1'); -} diff --git a/docs/lib/tsdoc/index.ts b/docs/lib/tsdoc/index.ts deleted file mode 100644 index ae6efd1f72..0000000000 --- a/docs/lib/tsdoc/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { generateDefinition } from './base'; -export { TSDoc } from './tsdoc'; -export type * from './types'; diff --git a/docs/lib/tsdoc/tsdoc.tsx b/docs/lib/tsdoc/tsdoc.tsx deleted file mode 100644 index 6642fb48b4..0000000000 --- a/docs/lib/tsdoc/tsdoc.tsx +++ /dev/null @@ -1,435 +0,0 @@ -import cn from 'clsx'; -import Slugger from 'github-slugger'; -import Link from 'next/link'; -import type { FC, ReactElement, ReactNode } from 'react'; -import { Callout } from '@/components/geistdocs/callout'; -import { generateDefinition } from './base'; -import type { GeneratedFunction, TypeField } from './types'; - -type TSDocProps = { - definition: string; - typeLinkMap?: Record; - noParametersContent?: ReactNode; - showSections: ('parameters' | 'returns' | 'throws')[]; -}; - -const classes = { - card: cn('rounded-sm transition-colors'), - anchor: cn( - 'absolute top-2 right-2 text-lg font-black', - 'before:content-["#"] hover:text-foreground', - 'px-2 py-1 opacity-0 group-hover:opacity-100 transition-opacity' - ), -}; - -export const TSDoc: FC = ({ - definition: rawDefinition, - typeLinkMap = {}, - noParametersContent = ( -

- This function does not accept any parameters. -

- ), - showSections = ['parameters', 'returns'], -}) => { - const definition = generateDefinition({ - code: rawDefinition, - }); - const showParameters = showSections.includes('parameters'); - const showReturns = showSections.includes('returns'); - const showThrows = showSections.includes('throws'); - if ('entries' in definition) { - return ( - - ); - } - - const { signatures } = definition; - const withSignatures = signatures.length > 1; - - if (!withSignatures) { - return ( - - ); - } - - return ( -
- This function has multiple signatures. - {signatures.map((signature, index) => ( -
-

Signature {index + 1}

- -
- ))} -
- ); -}; - -function FunctionSignature({ - signature, - index = '', - showParameters = true, - showReturns = true, - showThrows = true, - noParametersContent = ( -

- This function does not accept any parameters. -

- ), - typeLinkMap = {}, -}: { - signature: GeneratedFunction['signatures'][number]; - index?: string | number; - showParameters?: boolean; - showReturns?: boolean; - showThrows?: boolean; - noParametersContent?: ReactNode; - typeLinkMap?: TSDocProps['typeLinkMap']; -}) { - const slugger = new Slugger(); - const unnamedReturnId = `returns${index}`; - - return ( -
- {showParameters && ( -
- {signature.params.length ? ( - - ) : ( - noParametersContent - )} -
- )} - {showReturns && ( -
- {Array.isArray(signature.returns) ? ( -
- - - - - - - - - - {signature.returns.map((prop) => { - const id = slugger.slug(prop.name); - return ( - - - - - - ); - })} - -
NameTypeDescription
- {linkify(prop.description || '', typeLinkMap)} -
-
- ) : ( -
- - {linkify(signature.returns.type, typeLinkMap)} - -
- )} -
- )} - {showThrows && ( -
- {signature.throws?.length ? ( - - - - - - - - - {signature.throws.map((throwsItem, index) => { - const match = throwsItem.match(/^`?([^`\s]+)`?\s*-\s*(.+)$/); - const type = match ? match[1] : throwsItem; - const description = match ? match[2] : ''; - const id = `throws-${index}`; - - return ( - - - - - ); - })} - -
TypeDescription
- - {linkify(type, typeLinkMap)} - - - {description && ( -

- {linkify(description, typeLinkMap)} -

- )} -
- ) : ( -

- This function does not throw any errors. -

- )} -
- )} -
- ); -} - -const Row: FC<{ - children: ReactNode; - id: string; -}> = ({ children, id }) => { - return ( - - {children} - - ); -}; - -const NameCell: FC<{ - name: string; - optional?: boolean; -}> = ({ name, optional }) => { - return ( - - {name && ( - - {name} - - )} - - ); -}; - -const TypeCell: FC<{ - type: string; - typeLinkMap: TSDocProps['typeLinkMap']; -}> = ({ type, typeLinkMap }) => { - return ( - {linkify(type, typeLinkMap)} - ); -}; - -const FieldsTable: FC<{ - fields: TypeField[]; - typeLinkMap: TSDocProps['typeLinkMap']; -}> = ({ fields, typeLinkMap }) => { - const slugger = new Slugger(); - - return ( - - - - - - - - - - {fields.map((field) => { - const id = slugger.slug(field.name); - const tags = field.tags ?? {}; - const description = [ - field.description || tags.description, - tags.deprecated && `**Deprecated**: ${tags.deprecated}`, - ] - .filter(Boolean) - .join('\n'); - - return ( - - - - - - ); - })} - -
NameTypeDescription
- {linkify(description, typeLinkMap)} -
- ); -}; - -function linkify( - text: string, - typeLinkMap: TSDocProps['typeLinkMap'] = {} -): ReactNode { - // Combined regex to match markdown links and inline code - const markdownRegex = /(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)/g; - - // Split the text by markdown elements - const parts: (string | ReactElement)[] = []; - let lastIndex = 0; - let match: RegExpExecArray | null; - - // Reset regex state to ensure consistent behavior across calls - markdownRegex.lastIndex = 0; - match = markdownRegex.exec(text); - while (match !== null) { - // Add text before the match - if (match.index > lastIndex) { - parts.push(text.substring(lastIndex, match.index)); - } - - if (match[1]) { - // It's a markdown link: [text](url) - const linkText = match[2]; - const linkUrl = match[3]; - - // Check if the link text contains inline code - if (linkText.includes('`')) { - const codeParts = linkText.split(/`([^`]+)`/); - const linkContent = codeParts - .map((part, i) => { - if (i % 2 === 1) { - // It's code - return ( - - {part} - - ); - } - return part; - }) - .filter(Boolean); - - parts.push( - - {linkContent} - - ); - } else { - parts.push( - - {linkText} - - ); - } - } else if (match[4]) { - // It's inline code: `code` - const codeText = match[5]; - parts.push( - - {codeText} - - ); - } - - lastIndex = match.index + match[0].length; - match = markdownRegex.exec(text); - } - - // Add any remaining text - if (lastIndex < text.length) { - parts.push(text.substring(lastIndex)); - } - - // If no markdown links were found, use the original logic for type links - if (parts.length === 0) { - const result: (string | ReactElement)[] = []; - const chunks = text.match(/(\w+|\W+)/g) || []; - - for (const chunk of chunks) { - const href = Object.hasOwn(typeLinkMap, chunk) - ? typeLinkMap[chunk] - : undefined; - if (href) { - result.push( - - {chunk} - - ); - continue; - } - if (typeof result.at(-1) === 'string') { - result[result.length - 1] += chunk; - continue; - } - result.push(chunk); - } - return result; - } - - // Process remaining text parts for type links - return parts.flatMap((part, index) => { - if (typeof part !== 'string') return part; - - // Apply type link mapping to text parts - const chunks = part.match(/(\w+|\W+)/g) || []; - const processedChunks: (string | ReactElement)[] = []; - - for (const chunk of chunks) { - const href = Object.hasOwn(typeLinkMap, chunk) - ? typeLinkMap[chunk] - : undefined; - if (href) { - processedChunks.push( - - {chunk} - - ); - } else { - if (typeof processedChunks[processedChunks.length - 1] === 'string') { - processedChunks[processedChunks.length - 1] += chunk; - } else { - processedChunks.push(chunk); - } - } - } - - return processedChunks.length === 1 ? processedChunks[0] : processedChunks; - }); -} diff --git a/docs/lib/tsdoc/types.ts b/docs/lib/tsdoc/types.ts deleted file mode 100644 index d4acce4284..0000000000 --- a/docs/lib/tsdoc/types.ts +++ /dev/null @@ -1,43 +0,0 @@ -export type Tags = Record; - -export type ReturnField = { - type: string; -}; - -export type GeneratedDefinition = { - filePath?: string; - name: string; - description?: string; - tags?: Tags; -}; - -export type GeneratedFunction = { - signatures: { - params: TypeField[]; - returns: TypeField[] | ReturnField; - throws?: string[]; - }[]; -}; - -export type GeneratedType = { - entries: TypeField[]; -}; - -export type TypeField = { - name: string; - type: string; - description?: string; - optional?: boolean; - tags?: Tags; -}; - -export type ThrowField = { - type: string; - description?: string; -}; - -export type BaseArgs = { - code: string; - exportName?: string; - flattened?: boolean; -}; diff --git a/docs/package.json b/docs/package.json index 3879797b6e..a9cd47814d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,7 +8,7 @@ "dev": "next dev --turbo", "start": "next start", "postinstall": "fumadocs-mdx", - "prebuild": "node scripts/pack.ts", + "prebuild": "node scripts/pack.ts && ./copy-api-docs.sh", "lint": "biome check", "lint:links": "bun ./scripts/lint.ts", "format": "biome format --write", @@ -56,7 +56,6 @@ "feed": "5.1.0", "fumadocs-core": "16.2.2", "fumadocs-mdx": "14.0.4", - "fumadocs-typescript": "^4.0.13", "fumadocs-ui": "16.2.2", "github-slugger": "^2.0.0", "input-otp": "^1.4.2", @@ -93,7 +92,6 @@ "bun": "^1.3.0", "postcss": "^8.5.6", "tailwindcss": "^4.1.17", - "ts-morph": "^27.0.0", "tw-animate-css": "^1.4.0", "typescript": "catalog:" } diff --git a/package.json b/package.json index 7cf5becf35..c41fa32fff 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "husky": "^9.1.7", "lint-staged": "^16.2.7", "turbo": "^2.8.11", + "typedoc": "0.28.18", + "typedoc-plugin-frontmatter": "1.3.1", + "typedoc-plugin-markdown": "^4.11.0", "typescript": "catalog:", "vitest": "catalog:" }, @@ -38,6 +41,7 @@ "test:docs": "pnpm --filter @workflow/docs-typecheck test:docs", "bench": "vitest bench packages/core/e2e/bench.bench.ts", "bench:local": "DEPLOYMENT_URL=http://localhost:3000 APP_NAME=nextjs-turbopack vitest bench packages/core/e2e/bench.bench.ts", + "typedoc": "turbo typedoc", "lint": "biome check", "format": "biome format --write", "changeset": "changeset", diff --git a/packages/ai/package.json b/packages/ai/package.json index 89bc11404b..fd534b7c55 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -63,6 +63,7 @@ "build": "tsc", "clean": "tsc --build --clean && rm -rf docs ||:", "test": "vitest run", + "typedoc": "typedoc", "prepack": "mkdir -p docs && cp -r ../../docs/content/docs/ai ./docs/ && cp -r ../../docs/content/docs/api-reference/workflow-ai ./docs/", "postpack": "rm -rf docs" }, diff --git a/packages/ai/typedoc.json b/packages/ai/typedoc.json new file mode 100644 index 0000000000..00f30852e1 --- /dev/null +++ b/packages/ai/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "@workflow/ai", + "entryPoints": ["./src/index.ts", "./src/agent/durable-agent.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/packages/errors/package.json b/packages/errors/package.json index 7caec02b44..9f0689a57b 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -26,7 +26,8 @@ "build": "tsc", "dev": "tsc --watch", "clean": "tsc --build --clean && rm -rf dist", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "typedoc": "typedoc" }, "devDependencies": { "@types/ms": "2.1.0", diff --git a/packages/errors/typedoc.json b/packages/errors/typedoc.json new file mode 100644 index 0000000000..1c06ac273e --- /dev/null +++ b/packages/errors/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "workflow/errors", + "entryPoints": ["./src/index.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/packages/next/package.json b/packages/next/package.json index 0d01c0bb84..0ad40b5800 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -29,6 +29,7 @@ "build": "tsc", "dev": "tsc --watch", "clean": "tsc --build --clean && rm -rf dist docs", + "typedoc": "typedoc", "prepack": "mkdir -p docs && cp ../../docs/content/docs/getting-started/next.mdx ./docs/ && cp -r ../../docs/content/docs/api-reference/workflow-next ./docs/api-reference", "postpack": "rm -rf docs" }, diff --git a/packages/next/typedoc.json b/packages/next/typedoc.json new file mode 100644 index 0000000000..44b4eb8c5f --- /dev/null +++ b/packages/next/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "workflow/next", + "entryPoints": ["./src/index.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/packages/serde/package.json b/packages/serde/package.json index 49f2f823e8..61c36908ce 100644 --- a/packages/serde/package.json +++ b/packages/serde/package.json @@ -26,7 +26,8 @@ "build": "tsc", "dev": "tsc --watch", "clean": "tsc --build --clean && rm -rf dist", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "typedoc": "typedoc" }, "devDependencies": { "@types/node": "catalog:", diff --git a/packages/serde/typedoc.json b/packages/serde/typedoc.json new file mode 100644 index 0000000000..1682271497 --- /dev/null +++ b/packages/serde/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "@workflow/serde", + "entryPoints": ["./src/index.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 7536f71b6a..83a4e438dd 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -26,7 +26,8 @@ "scripts": { "build": "tsc", "clean": "tsc --build --clean && rm -rf dist ||:", - "dev": "tsc --watch" + "dev": "tsc --watch", + "typedoc": "typedoc" }, "dependencies": { "@workflow/builders": "workspace:*", diff --git a/packages/vitest/typedoc.json b/packages/vitest/typedoc.json new file mode 100644 index 0000000000..a8186495f8 --- /dev/null +++ b/packages/vitest/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "@workflow/vitest", + "entryPoints": ["./src/index.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 98475563ae..b6d353cc4f 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -61,6 +61,7 @@ "dev": "tsc --watch", "test": "vitest run src", "typecheck": "tsc --noEmit", + "typedoc": "typedoc && typedoc --options typedoc-api.json", "prepack": "cp -r ../../docs/content/docs ./docs", "postpack": "rm -rf docs" }, diff --git a/packages/workflow/typedoc-api.json b/packages/workflow/typedoc-api.json new file mode 100644 index 0000000000..6a99c564cf --- /dev/null +++ b/packages/workflow/typedoc-api.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "workflow/api", + "entryPoints": ["./src/api.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out-api" +} diff --git a/packages/workflow/typedoc.json b/packages/workflow/typedoc.json new file mode 100644 index 0000000000..523f3d718c --- /dev/null +++ b/packages/workflow/typedoc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": ["../../typedoc.json"], + "name": "workflow", + "entryPoints": ["./src/index.ts"], + "tsconfig": "./tsconfig.json", + "out": "typedoc-out" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47678829b6..ca47312189 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,6 +87,15 @@ importers: turbo: specifier: ^2.8.11 version: 2.8.12 + typedoc: + specifier: 0.28.18 + version: 0.28.18(typescript@5.9.3) + typedoc-plugin-frontmatter: + specifier: 1.3.1 + version: 1.3.1(typedoc-plugin-markdown@4.11.0(typedoc@0.28.18(typescript@5.9.3))) + typedoc-plugin-markdown: + specifier: ^4.11.0 + version: 4.11.0(typedoc@0.28.18(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 @@ -216,9 +225,6 @@ importers: fumadocs-mdx: specifier: 14.0.4 version: 14.0.4(fumadocs-core@16.2.2(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.3)) - fumadocs-typescript: - specifier: ^4.0.13 - version: 4.0.13(@types/react@19.1.13)(fumadocs-core@16.2.2(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3))(fumadocs-ui@16.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18))(typescript@5.9.3) fumadocs-ui: specifier: 16.2.2 version: 16.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) @@ -310,9 +316,6 @@ importers: tailwindcss: specifier: ^4.1.17 version: 4.1.18 - ts-morph: - specifier: ^27.0.0 - version: 27.0.0 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -3789,6 +3792,9 @@ packages: '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + '@gerrit0/mini-shiki@3.23.0': + resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} + '@graphile/logger@0.2.0': resolution: {integrity: sha512-jjcWBokl9eb1gVJ85QmoaQ73CQ52xAaOCF29ukRbYNl6lY+ts0ErTaDYOBlejcbUs2OpaiqYLO5uDhyLFzWw4w==} @@ -7057,6 +7063,9 @@ packages: '@shikijs/engine-oniguruma@3.21.0': resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==} + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + '@shikijs/engine-oniguruma@4.0.0': resolution: {integrity: sha512-KXmq4b6Xw16+4+rz5M4NZMoe/tzs5kTOMSJz8+LCyxSrwmxwTBAM/ab85iSO2Gw79E47HkW4B9HPHUXhrNOivw==} engines: {node: '>=20'} @@ -7068,6 +7077,9 @@ packages: '@shikijs/langs@3.21.0': resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==} + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + '@shikijs/langs@4.0.0': resolution: {integrity: sha512-dSAT6fBcnOcYZQMWZO8+OmzUKKm+OO0As/qZ3TXLiSy0JsCTEYz1TaX7TDupnYLz7dr0oF2DOTEgPocx1D3aFw==} engines: {node: '>=20'} @@ -7090,6 +7102,9 @@ packages: '@shikijs/themes@3.21.0': resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==} + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + '@shikijs/themes@4.0.0': resolution: {integrity: sha512-xe42kvxOXan5ouXxULez6qwDNUJkoP6kicfg0wKuJBkeIaHLxZBZa2gEGYutL1q27DQZ5+XoR6caVX+E/aNR5A==} engines: {node: '>=20'} @@ -7104,6 +7119,9 @@ packages: '@shikijs/types@3.21.0': resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==} + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + '@shikijs/types@4.0.0': resolution: {integrity: sha512-LCnfBTtQKNtJyc1qMShZr2dJt1uxNI6pI0/YTc2DSNET91aUvnMGHUHsucVCC5AJVcv5XyBqk2NgYRwd20EjbA==} engines: {node: '>=20'} @@ -7685,12 +7703,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@ts-morph/common@0.28.0': - resolution: {integrity: sha512-4w6X/oFmvXcwux6y6ExfM/xSqMHw20cYwFJH+BlYrtGa6nwY9qGq8GXnUs1sVYeF2o/KT3S8hAH6sKBI3VOkBg==} - - '@ts-morph/common@0.28.1': - resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} - '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -9124,9 +9136,6 @@ packages: cobe@0.6.5: resolution: {integrity: sha512-MA8bu81EFY6JjQpj+FovEuhyJ25khx2Q7Lh+ot/UkCJe5yKyDgzdc6u2lGZIOmsZTXK6Itg1i4lQZIJZbPWnAg==} - code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - codem-isoboxer@0.3.10: resolution: {integrity: sha512-eNk3TRV+xQMJ1PEj0FQGY8KD4m0GPxT487XJ+Iftm7mVa9WpPFDMWqPt+46buiP5j5Wzqe5oMIhqBcAeKfygSA==} @@ -10488,19 +10497,6 @@ packages: vite: optional: true - fumadocs-typescript@4.0.13: - resolution: {integrity: sha512-zmpmqsS2DZeH2wn17X0V5NOQ77mqnjlKjyUcgjNQk/vDPQkdFRxuoLNNBFnGGMizoKMQ30hjRgdGukVSVDgy+g==} - peerDependencies: - '@types/react': '*' - fumadocs-core: ^15.7.0 || ^16.0.0 - fumadocs-ui: ^15.7.0 || ^16.0.0 - typescript: '*' - peerDependenciesMeta: - '@types/react': - optional: true - fumadocs-ui: - optional: true - fumadocs-ui@16.2.2: resolution: {integrity: sha512-qYvPbVRMMFiuzrsmvGYpEj/cT5XyGzvwrrRklrHPMegywY+jxQ0TUeRKHzQgxkkTl0MDPnejRbHHAfafz01/TQ==} peerDependencies: @@ -11439,6 +11435,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==} + lint-staged@16.2.7: resolution: {integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==} engines: {node: '>=20.17'} @@ -11565,6 +11564,9 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -11596,6 +11598,10 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + markdown-it@14.1.1: + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} + hasBin: true + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -11678,6 +11684,9 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-chrome@4.14.0: resolution: {integrity: sha512-IEdFb4blyF15vLvQzLIn6USJBv7Kf2ne+TfLQKBYI5Z0f9VEBVZz5MKy4Uhi0iA9lStl2S9ENIujJRuJIa5OiA==} @@ -13041,6 +13050,10 @@ packages: pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + 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'} @@ -14252,12 +14265,6 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} - ts-morph@27.0.0: - resolution: {integrity: sha512-xcqelpTR5PCuZMs54qp9DE3t7tPgA2v/P1/qdW4ke5b3Y5liTGTYj6a/twT35EQW/H5okRqp1UOqwNlgg0K0eQ==} - - ts-morph@27.0.2: - resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} - tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} @@ -14360,6 +14367,24 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typedoc-plugin-frontmatter@1.3.1: + resolution: {integrity: sha512-wXKnhpiOuG3lY9GGKiKcXNrhKbPYm/jA5wbzGE/kKdwlSu8++ZbEuKA0K2dvIna3F+5EQrv+3AeObHkS1QP7JA==} + peerDependencies: + typedoc-plugin-markdown: '>=4.9.0' + + typedoc-plugin-markdown@4.11.0: + resolution: {integrity: sha512-2iunh2ALyfyh204OF7h2u0kuQ84xB3jFZtFyUr01nThJkLvR8oGGSSDlyt2gyO4kXhvUxDcVbO0y43+qX+wFbw==} + engines: {node: '>= 18'} + peerDependencies: + typedoc: 0.28.x + + typedoc@0.28.18: + resolution: {integrity: sha512-NTWTUOFRQ9+SGKKTuWKUioUkjxNwtS3JDRPVKZAXGHZy2wCA8bdv2iJiyeePn0xkmK+TCCqZFT0X7+2+FLjngA==} + 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 || 5.9.x || 6.0.x + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -14369,6 +14394,9 @@ packages: resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} @@ -16996,6 +17024,14 @@ snapshots: dependencies: tslib: 2.8.1 + '@gerrit0/mini-shiki@3.23.0': + dependencies: + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@graphile/logger@0.2.0': {} '@grpc/grpc-js@1.14.0': @@ -21305,6 +21341,11 @@ snapshots: '@shikijs/types': 3.21.0 '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@4.0.0': dependencies: '@shikijs/types': 4.0.0 @@ -21319,6 +21360,10 @@ snapshots: dependencies: '@shikijs/types': 3.21.0 + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/langs@4.0.0': dependencies: '@shikijs/types': 4.0.0 @@ -21352,6 +21397,10 @@ snapshots: dependencies: '@shikijs/types': 3.21.0 + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/themes@4.0.0': dependencies: '@shikijs/types': 4.0.0 @@ -21370,6 +21419,11 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + '@shikijs/types@4.0.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 @@ -22142,18 +22196,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@ts-morph/common@0.28.0': - dependencies: - minimatch: 10.2.4 - path-browserify: 1.0.1 - tinyglobby: 0.2.15 - - '@ts-morph/common@0.28.1': - dependencies: - minimatch: 10.2.4 - path-browserify: 1.0.1 - tinyglobby: 0.2.15 - '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -24083,8 +24125,6 @@ snapshots: dependencies: phenomenon: 1.6.0 - code-block-writer@13.0.3: {} - codem-isoboxer@0.3.10: {} collapse-white-space@2.1.0: {} @@ -24718,7 +24758,7 @@ snapshots: docker-compose@1.3.1: dependencies: - yaml: 2.8.1 + yaml: 2.8.3 docker-modem@5.0.6: dependencies: @@ -25608,24 +25648,6 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-typescript@4.0.13(@types/react@19.1.13)(fumadocs-core@16.2.2(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3))(fumadocs-ui@16.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18))(typescript@5.9.3): - dependencies: - estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.2.2(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) - hast-util-to-estree: 3.1.3 - hast-util-to-jsx-runtime: 2.3.6 - remark: 15.0.1 - remark-rehype: 11.1.2 - tinyglobby: 0.2.15 - ts-morph: 27.0.2 - typescript: 5.9.3 - unist-util-visit: 5.0.0 - optionalDependencies: - '@types/react': 19.1.13 - fumadocs-ui: 16.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) - transitivePeerDependencies: - - supports-color - fumadocs-ui@16.2.2(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(lucide-react@0.555.0(react@19.2.3))(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router@7.13.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18): dependencies: '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -26632,6 +26654,10 @@ snapshots: lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + lint-staged@16.2.7: dependencies: commander: 14.0.3 @@ -26773,6 +26799,8 @@ snapshots: dependencies: react: 19.1.0 + lunr@2.3.9: {} + lz-string@1.5.0: {} magic-regexp@0.10.0: @@ -26815,6 +26843,15 @@ snapshots: markdown-extensions@2.0.0: {} + markdown-it@14.1.1: + 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 + markdown-table@3.0.4: {} marked@14.0.0: {} @@ -27010,6 +27047,8 @@ snapshots: mdn-data@2.12.2: {} + mdurl@2.0.0: {} + media-chrome@4.14.0(react@19.2.3): dependencies: ce-la-react: 0.3.2(react@19.2.3) @@ -29315,6 +29354,8 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 + punycode.js@2.3.1: {} + punycode@2.3.1: {} qs@6.14.1: @@ -31045,16 +31086,6 @@ snapshots: ts-dedent@2.2.0: {} - ts-morph@27.0.0: - dependencies: - '@ts-morph/common': 0.28.0 - code-block-writer: 13.0.3 - - ts-morph@27.0.2: - dependencies: - '@ts-morph/common': 0.28.1 - code-block-writer: 13.0.3 - tsconfck@3.1.6(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -31148,10 +31179,30 @@ snapshots: typedarray@0.0.6: {} + typedoc-plugin-frontmatter@1.3.1(typedoc-plugin-markdown@4.11.0(typedoc@0.28.18(typescript@5.9.3))): + dependencies: + typedoc-plugin-markdown: 4.11.0(typedoc@0.28.18(typescript@5.9.3)) + yaml: 2.8.3 + + typedoc-plugin-markdown@4.11.0(typedoc@0.28.18(typescript@5.9.3)): + dependencies: + typedoc: 0.28.18(typescript@5.9.3) + + typedoc@0.28.18(typescript@5.9.3): + dependencies: + '@gerrit0/mini-shiki': 3.23.0 + lunr: 2.3.9 + markdown-it: 14.1.1 + minimatch: 10.2.4 + typescript: 5.9.3 + yaml: 2.8.3 + typescript@5.9.3: {} ua-parser-js@1.0.41: {} + uc.micro@2.1.0: {} + ufo@1.6.1: {} ufo@1.6.3: {} diff --git a/turbo.json b/turbo.json index 56d6764952..159847385c 100644 --- a/turbo.json +++ b/turbo.json @@ -31,6 +31,17 @@ "dependsOn": ["^build"], "cache": false }, + "typedoc": { + "dependsOn": ["^build"], + "inputs": [ + "src/**", + "typedoc.json", + "typedoc-api.json", + "$TURBO_ROOT$/typedoc.json", + "$TURBO_ROOT$/typedoc-formatter.mjs" + ], + "outputs": ["typedoc-out/**", "typedoc-out-api/**"] + }, "clean": { "cache": false } diff --git a/typedoc-formatter.mjs b/typedoc-formatter.mjs new file mode 100644 index 0000000000..775b3860c9 --- /dev/null +++ b/typedoc-formatter.mjs @@ -0,0 +1,95 @@ +// @ts-check +import { MarkdownPageEvent } from 'typedoc-plugin-markdown'; + +/** + * Custom TypeDoc plugin that: + * 1. Injects `title` and `type` frontmatter for each generated page + * 2. Strips `.mdx` extensions from links (Fumadocs uses clean URLs) + * 3. Escapes remaining unescaped curly braces in inline code spans + * that `sanitizeComments` missed (e.g., JSDoc @example in table cells) + * + * @param {import('typedoc-plugin-markdown').MarkdownApplication} app + */ +export function load(app) { + // Set "title" frontmatter for each page + app.renderer.on( + MarkdownPageEvent.BEGIN, + /** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */ + (page) => { + page.frontmatter = { + ...page.frontmatter, + title: page.model.name, + type: 'reference', + }; + } + ); + + // Post-process page contents + app.renderer.on( + MarkdownPageEvent.END, + /** @param {import('typedoc-plugin-markdown').MarkdownPageEvent} page */ + (page) => { + if (!page.contents) return; + + // Strip `.mdx` extensions from links + page.contents = page.contents.replace(/\.mdx/g, ''); + + // Escape any remaining unescaped { and } in the content. + // The `sanitizeComments` typedoc option handles most cases, but + // misses curly braces inside inline code spans within table cells + // (e.g., JSDoc @example blocks rendered inline in property tables). + // MDX treats unescaped `{...}` as JSX expressions which breaks the build. + page.contents = escapeRemainingBraces(page.contents); + } + ); +} + +/** + * Escapes any `{` and `}` that are not already escaped (i.e., not preceded + * by a backslash) and are not inside fenced code blocks. This catches + * cases that `sanitizeComments` misses. + * + * @param {string} content + * @returns {string} + */ +function escapeRemainingBraces(content) { + const lines = content.split('\n'); + let inFencedCodeBlock = false; + const result = []; + + for (const line of lines) { + // Track fenced code block boundaries + if (/^```/.test(line.trimStart())) { + inFencedCodeBlock = !inFencedCodeBlock; + result.push(line); + continue; + } + + if (inFencedCodeBlock) { + result.push(line); + continue; + } + + // Replace unescaped { and } (not preceded by \) + // Preserve anchor tags by temporarily replacing them + const anchors = []; + let processed = line.replace(/]*>[\s\S]*?<\/a>/g, (match) => { + anchors.push(match); + return `__TYPEDOC_ANCHOR_${anchors.length - 1}__`; + }); + + // Escape unescaped braces (not already preceded by backslash) + processed = processed + .replace(/(?