--- url: 'https://mokup.icebreaker.top/ai.md' --- # AI Prompting Use these prompt templates to generate Mokup response bodies from DTS or OpenAPI specs. They are designed to return JSON only so the output can be saved directly as `*.get.json`. ## Best practices * Always specify the exact target type or `{method} {path} {status} {contentType}`. * Set `{count}` for arrays and keep `{seed}` stable to get reproducible results. * If the model returns invalid JSON or mismatched structure, run the repair prompt. * Keep outputs as response bodies only (not full Mokup rule objects). ## Templates * [DTS prompt templates](./prompt-templates-dts) * [OpenAPI prompt templates](./prompt-templates-openapi) * [llms.txt](./llms-txt) --- --- url: 'https://mokup.icebreaker.top/zh/ai.md' --- # AI 提示词 本章节提供从 DTS 或 OpenAPI 生成 Mokup 响应 body 的提示词模板,输出为纯 JSON,可直接保存为 `*.get.json`。 ## 最佳实践 * 明确指定目标类型或 `{method} {path} {status} {contentType}`。 * 数组场景设置 `{count}`,并固定 `{seed}` 以保证可复现。 * 输出 JSON 不合法或结构不匹配时,直接使用修复模板。 * 只生成响应 body,不要输出 Mokup 规则对象。 ## 模板 * [DTS 提示词模板](./prompt-templates-dts) * [OpenAPI 提示词模板](./prompt-templates-openapi) * [llms.txt](./llms-txt) --- --- url: 'https://mokup.icebreaker.top/core-concepts/bundle.md' --- # Bundle `mokupBundle` is the runtime-ready object that packages everything needed to execute routes outside of Mokup's dev server. You will see it emitted as `.mokup/mokup.bundle.mjs` (CLI build) or provided as a virtual module (`virtual:mokup-bundle`) in Vite. ## Why do we need mokupBundle? Mokup's dev server can read files and execute handlers directly, but most production runtimes cannot. Workers and serverless platforms block filesystem access and expect a single module entry. `mokupBundle` packages the manifest and handler module references into one object so the runtime can execute the same routes without local file imports. ## What is inside? A bundle contains three fields: * `manifest`: route definitions, responses, and metadata. * `moduleMap`: a map from module id to module exports for handler modules. * `moduleBase`: the base path used when resolving handler modules. Only `manifest` is required. `moduleMap` and `moduleBase` are needed when your runtime cannot load local files directly (Workers, serverless, or other sandboxed environments). ## manifest The manifest is the source of truth for route matching. Each route entry contains HTTP method, URL template, tokens, and the response definition (`json`, `text`, `binary`, or `module`). If you need the exact structure, see the [Manifest](./manifest) page. ## moduleMap `moduleMap` is a lookup table used when a route response is defined as `type: "module"`. Each entry maps a module id to the module's exports so the runtime can call a handler without using filesystem imports. You typically get this from: * `.mokup/mokup.bundle.mjs` (CLI build output) * `virtual:mokup-bundle` (Vite runtime in Worker mode) ## moduleBase `moduleBase` is the base path used to resolve module ids when the runtime needs to import handlers. In a CLI bundle it defaults to `./` because handler modules live under `.mokup/mokup-handlers`. When you provide a custom bundle, make sure `moduleBase` matches the directory or URL where the handler modules are hosted. ## Producing a bundle There are three common ways to generate a bundle: * CLI: `mokup build` writes `.mokup/mokup.bundle.mjs`. * Vite: import `virtual:mokup-bundle` when using `mokup/vite`. * Cross-platform: `buildBundleModule` from `mokup/bundle` returns source for a bundle module without touching the filesystem. ## When can I omit moduleMap or moduleBase? If none of your routes use module-based handlers (for example, all responses are static JSON or text), you can pass only the manifest. If you are running in Node and can load files directly, you may omit `moduleMap` and `moduleBase` as long as the runtime can import the handlers. ## Common pitfalls * Missing `moduleMap` in Worker runtimes causes module handlers to fail. * Incorrect `moduleBase` leads to unresolved handler imports. ## Minimal usage Recommended: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from './.mokup/mokup.bundle.mjs' const handler = createFetchHandler(mokupBundle) ``` Also valid (explicit fields): ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from './.mokup/mokup.bundle.mjs' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` --- --- url: 'https://mokup.icebreaker.top/zh/core-concepts/bundle.md' --- # Bundle `mokupBundle` 是用于运行时执行的打包对象,通常来自 CLI 产物 `.mokup/mokup.bundle.mjs`,或在 Vite 中通过虚拟模块 `virtual:mokup-bundle` 获取。 ## 为什么需要 mokupBundle? 在开发环境中,Mokup 可以直接读取文件并执行处理器,但多数生产运行时无法 访问本地文件系统。Worker 与 Serverless 更倾向于单一入口模块。 `mokupBundle` 把 manifest 与处理器模块引用打包在一起,使这些环境也能执行 相同的路由逻辑。 ## 包含哪些内容? Bundle 有三个字段: * `manifest`:路由定义、响应与元数据。 * `moduleMap`:处理器模块 id 到模块导出的映射。 * `moduleBase`:处理器模块解析所需的基准路径。 其中只有 `manifest` 是必需的。`moduleMap` 与 `moduleBase` 用于 Worker / Serverless 等无法直接加载本地文件的环境。 ## manifest manifest 是路由匹配的核心数据,每条路由包含 HTTP 方法、URL 模板、tokens 以及响应定义(`json`、`text`、`binary` 或 `module`)。 结构细节请参考 [Manifest](./manifest)。 ## moduleMap 当响应类型为 `type: "module"` 时,需要用 `moduleMap` 解析处理器模块。它把 模块 id 映射到模块导出对象,避免运行时直接访问文件系统。 常见来源包括: * `.mokup/mokup.bundle.mjs`(CLI 构建输出) * `virtual:mokup-bundle`(Vite 的 Worker 运行模式) ## moduleBase `moduleBase` 用于拼接处理器模块路径。CLI bundle 中默认是 `./`,因为 handler 模块输出在 `.mokup/mokup-handlers` 下。 如果你自己生成 bundle,请确保 `moduleBase` 与实际部署路径一致。 ## Bundle 生成方式 常见方式有三种: * CLI:`mokup build` 生成 `.mokup/mokup.bundle.mjs`。 * Vite:`mokup/vite` 下直接 `import 'virtual:mokup-bundle'`。 * 跨平台:`mokup/bundle` 的 `buildBundleModule` 直接生成模块源码。 ## 何时可以省略 moduleMap 或 moduleBase? 如果所有路由都是静态响应(如 JSON / 文本),可以只传 `manifest`。 在 Node 环境中,只要运行时能直接导入处理器文件,也可以省略 `moduleMap` 与 `moduleBase`。 ## 常见问题 * Worker 环境缺少 `moduleMap` 会导致模块处理器无法执行。 * `moduleBase` 配置错误会导致处理器模块无法解析。 ## 最小使用示例 推荐写法: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from './.mokup/mokup.bundle.mjs' const handler = createFetchHandler(mokupBundle) ``` 也可以显式传入字段: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from './.mokup/mokup.bundle.mjs' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` --- --- url: 'https://mokup.icebreaker.top/reference/cli.md' --- # CLI `mokup` provides both `build` and `serve` commands. ## Build Generate `.mokup` outputs for server adapters and workers. Use cases: * Generate a bundle for Worker or server runtime deployment. * Prebuild mock artifacts for CI/CD without running a dev server. Demo: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ### Build options | Option | Description | | ----------------- | ----------------------------------------- | | `--dir, -d` | Mock directory (repeatable) | | `--out, -o` | Output directory (default: `.mokup`) | | `--prefix` | URL prefix | | `--include` | Include regex (repeatable) | | `--exclude` | Exclude regex (repeatable) | | `--ignore-prefix` | Ignore path segment prefixes (repeatable) | | `--no-handlers` | Skip handler output | ## Serve Start a standalone mock server from a directory. Use cases: * Spin up a local mock API server without a frontend. * Validate mock routes with curl or integration tests. Demo: ::: code-group ```bash [pnpm] pnpm exec mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [npm] npm exec mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [yarn] yarn mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [bun] bunx mokup serve --dir mock --prefix /api --port 3000 ``` ::: ### Serve options | Option | Description | | ----------------- | ----------------------------------------- | | `--dir, -d` | Mock directory (repeatable) | | `--prefix` | URL prefix | | `--include` | Include regex (repeatable) | | `--exclude` | Exclude regex (repeatable) | | `--ignore-prefix` | Ignore path segment prefixes (repeatable) | | `--host` | Hostname (default: `localhost`) | | `--port` | Port (default: `8080`) | | `--no-watch` | Disable file watching | | `--no-playground` | Disable Playground | | `--no-log` | Disable logging | ## API If you prefer programmatic usage, import `buildManifest`: Use cases: * Generate manifests inside Node scripts or build pipelines. * Integrate Mokup with custom tooling or frameworks. Demo: ```ts import { buildManifest } from 'mokup/cli' await buildManifest({ dir: 'mock', outDir: '.mokup', }) ``` ### Bundle helper (cross-platform) Generate a bundle module source string without touching the filesystem: Use cases: * Build a bundle string in environments without filesystem access. * Control how module import IDs are emitted for custom runtimes. Demo: ```ts import type { RouteTable } from 'mokup/bundle' import { buildBundleModule } from 'mokup/bundle' const routes: RouteTable = [] const source = buildBundleModule({ routes, root: '/project', resolveModulePath: file => `/virtual/${file}`, }) ``` `routes` uses the same resolved route shape as `scanRoutes` (from `mokup/vite`). Use `resolveModulePath` to control how module import ids are emitted outside of Vite. ## Notes * Multiple `--dir` values are merged into one manifest. * `mokup.bundle.mjs` is the recommended runtime entry. * `mokup serve` mirrors the standalone server behavior. --- --- url: 'https://mokup.icebreaker.top/zh/reference/cli.md' --- # CLI `mokup` 提供 `build` 与 `serve` 两个命令。 ## Build 生成供服务端适配器与 Worker 使用的 `.mokup` 构建产物。 使用场景: * 为 Worker 或服务端运行时生成 bundle。 * 在 CI/CD 中预构建 mock 产物。 示例: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ### Build 选项 | 参数 | 说明 | | ----------------- | -------------------------- | | `--dir, -d` | mock 目录(可重复) | | `--out, -o` | 输出目录(默认 `.mokup`) | | `--prefix` | 路由前缀 | | `--include` | 仅包含匹配的正则(可重复) | | `--exclude` | 排除匹配的正则(可重复) | | `--ignore-prefix` | 忽略路径段前缀(可重复) | | `--no-handlers` | 不生成函数处理器 | ## Serve 从目录直接启动独立的 mock 服务。 使用场景: * 不依赖前端应用,快速起一个 mock API 服务。 * 用于本地联调或接口测试。 示例: ::: code-group ```bash [pnpm] pnpm exec mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [npm] npm exec mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [yarn] yarn mokup serve --dir mock --prefix /api --port 3000 ``` ```bash [bun] bunx mokup serve --dir mock --prefix /api --port 3000 ``` ::: ### Serve 选项 | 参数 | 说明 | | ----------------- | -------------------------- | | `--dir, -d` | mock 目录(可重复) | | `--prefix` | 路由前缀 | | `--include` | 仅包含匹配的正则(可重复) | | `--exclude` | 排除匹配的正则(可重复) | | `--ignore-prefix` | 忽略路径段前缀(可重复) | | `--host` | 主机名(默认 `localhost`) | | `--port` | 端口(默认 `8080`) | | `--no-watch` | 关闭文件监听 | | `--no-playground` | 关闭 Playground | | `--no-log` | 关闭日志输出 | ## API 如果更喜欢编程式用法,可直接调用 `buildManifest`: 使用场景: * 在 Node 脚本或构建管线中生成 manifest。 * 集成到自定义工具链。 示例: ```ts import { buildManifest } from 'mokup/cli' await buildManifest({ dir: 'mock', outDir: '.mokup', }) ``` ### Bundle helper(跨平台) 生成 bundle 模块源码字符串,不依赖文件系统: 使用场景: * 在无文件系统的环境中生成 bundle 源码。 * 自定义模块 import 路径输出。 示例: ```ts import type { RouteTable } from 'mokup/bundle' import { buildBundleModule } from 'mokup/bundle' const routes: RouteTable = [] const source = buildBundleModule({ routes, root: '/project', resolveModulePath: file => `/virtual/${file}`, }) ``` `routes` 的结构与 `scanRoutes`(来自 `mokup/vite`)的返回值一致。若不在 Vite 环境内构建,请用 `resolveModulePath` 控制输出的 import 路径。 ## 说明 * `--dir` 可多次传入,但会在同一份 manifest 中合并。 * 生成的 `mokup.bundle.mjs` 适合在 Worker 或 Node 运行时直接导入。 * `mokup serve` 与内置服务直启行为一致。 --- --- url: 'https://mokup.icebreaker.top/getting-started/cloudflare.md' --- # Cloudflare Run Mokup on Cloudflare Workers or Pages. Workers are best for API workloads, while Pages works well for static sites plus Functions. ## Choose an entry * `mokup/server/worker`: smallest wrapper, best for direct Worker usage. * `mokup/server/fetch`: manual fetch integration for custom routing. ## Workers quick start When you use `mokup/vite`, import the virtual bundle generated by Vite: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` ### Wrangler config ```jsonc { "name": "mokup-worker", "main": "worker/src/index.ts", "compatibility_date": "2025-01-15" } ``` ### Build & deploy ```bash vite build wrangler deploy ``` ## Pages Functions quick start Place a function such as `functions/[[path]].ts`, then call the same fetch handler: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) export const onRequest: PagesFunction = async ({ request }) => { return (await handler(request)) ?? new Response('Not Found', { status: 404 }) } ``` Also valid (explicit fields): ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` Build your site and deploy with Pages: ```bash vite build wrangler pages deploy dist ``` ## More details For more deployment-specific options, see [Cloudflare Worker](../deploy/cloudflare-worker). --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/cloudflare.md' --- # Cloudflare Mokup 可以运行在 Cloudflare Workers 或 Pages 上。Workers 适合 API 请求, Pages 更适合静态站点 + Functions 的组合。 ## 入口选择 * `mokup/server/worker`: 最简 Worker 包装,适合直接部署。 * `mokup/server/fetch`: 手动组装 fetch 处理器,方便自定义路由。 ## Workers 快速开始 使用 `mokup/vite` 时,可以直接导入 Vite 生成的虚拟 bundle: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` ### Wrangler 配置 ```jsonc { "name": "mokup-worker", "main": "worker/src/index.ts", "compatibility_date": "2025-01-15" } ``` ### 构建与部署 ```bash vite build wrangler deploy ``` ## Pages Functions 快速开始 在 `functions/[[path]].ts` 中添加函数入口,然后复用同一套 fetch handler: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) export const onRequest: PagesFunction = async ({ request }) => { return (await handler(request)) ?? new Response('Not Found', { status: 404 }) } ``` 也可以显式传入字段: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` 构建站点并部署到 Pages: ```bash vite build wrangler pages deploy dist ``` ## 更多细节 更多部署细节请参考 [Cloudflare Worker](../deploy/cloudflare-worker)。 --- --- url: 'https://mokup.icebreaker.top/deploy/cloudflare-worker.md' --- # Cloudflare Worker > Looking for a quick start? See [Getting Started - Cloudflare](../getting-started/cloudflare). Run Mokup in Workers with `mokup/server/fetch`. The `mokup/server/worker` entry is still available for a more compact Worker wrapper. ## 1. Worker entry (virtual bundle) When you use `mokup/vite`, import the virtual bundle generated by Vite: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` ## 2. Wrangler config ```jsonc { "name": "web-mokup-worker", "main": "worker/src/index.ts", "compatibility_date": "2025-01-15" } ``` ## 3. Build & deploy ```bash vite dev vite build wrangler deploy ``` No extra `moduleBase` or `moduleMap` wiring is required when using the bundle. Here is a plain fetch example without any framework: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) export default { fetch: async (request: Request) => { const url = new URL(request.url) if (url.pathname === '/health') { return new Response('ok') } return (await handler(request)) ?? new Response('Not Found', { status: 404 }) }, } ``` Also valid (explicit fields): ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` You can also combine Mokup with Hono routes (install `hono` if needed): ```ts import { Hono } from 'hono' import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) const app = new Hono() app.get('/health', c => c.text('ok')) app.use('*', async (c, next) => { const response = await handler(c.req.raw) if (response) { return response } return next() }) export default app ``` --- --- url: 'https://mokup.icebreaker.top/zh/deploy/cloudflare-worker.md' --- # Cloudflare Worker > 快速上手请参考 [入门指南 - Cloudflare](../getting-started/cloudflare)。 Mokup 可以直接运行在 Worker 中,推荐使用 `mokup/server/fetch`。如需更简洁的 Worker 封装,也可使用 `mokup/server/worker`。 ## 1. Worker 入口(虚拟 bundle) 使用 `mokup/vite` 时,可以直接导入 Vite 生成的虚拟模块: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` ## 2. Wrangler 配置 `wrangler.jsonc` 示例: ```jsonc { "name": "web-mokup-worker", "main": "worker/src/index.ts", "compatibility_date": "2025-01-15" } ``` ## 3. 构建与部署 ```bash vite dev vite build wrangler deploy ``` 使用虚拟 bundle 时无需手动传入 `moduleBase` 或 `moduleMap`。 下面是一个不使用任何框架的 fetch 示例: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) export default { fetch: async (request: Request) => { const url = new URL(request.url) if (url.pathname === '/health') { return new Response('ok') } return (await handler(request)) ?? new Response('Not Found', { status: 404 }) }, } ``` 也可以显式传入字段: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` 也可以与 Hono 路由结合(未安装请先添加 `hono`): ```ts import { Hono } from 'hono' import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) const app = new Hono() app.get('/health', c => c.text('ok')) app.use('*', async (c, next) => { const response = await handler(c.req.raw) if (response) { return response } return next() }) export default app ``` --- --- url: 'https://mokup.icebreaker.top/platform/cross-runtime-js.md' --- # Cross-Runtime JavaScript Guide Write code once, run it in Service Workers, Cloudflare Workers, and Node.js by keeping your core logic on Web-standard APIs and isolating runtime differences in thin adapters. ## Goals and boundaries Your goal is a shared request handler that: * uses `Request`, `Response`, `URL`, and `Headers` * avoids direct filesystem access * treats environment bindings as optional * keeps runtime-specific glue in one place What this guide is not: a framework-specific adapter. The examples show the smallest portable core you can embed in any runtime. ## Runtime differences at a glance Service Worker: * no filesystem * event-driven via `fetch` event * `caches` is available in browsers Cloudflare Worker: * no filesystem * module or service-worker syntax * bindings come from `env` Node.js (>=18): * `fetch`/`Request`/`Response` available * filesystem exists, but avoid for portability * environment variables via `process.env` ## Compatibility checklist * Use `globalThis` and Web APIs (`fetch`, `Request`, `Response`, `Headers`, `URL`). * Avoid `Buffer` and `fs` in shared logic; prefer `ArrayBuffer`/`Uint8Array`. * Keep JSON/text handling explicit (`response.json()`, `response.text()`). * Treat `env` as optional and feature-detect missing bindings. * Do not rely on `window`/`document`/DOM in shared modules. * Avoid Node-only globals (`__dirname`, `process`), or guard them. * Keep modules ESM to match Worker runtimes. ## Minimal cross-runtime handler ```ts export type RuntimeEnv = Record export async function handleRequest( request: Request, env?: RuntimeEnv, ): Promise { const url = new URL(request.url) if (url.pathname === '/health') { return new Response('ok') } const apiBase = env?.API_BASE ?? 'https://example.com' const upstream = new URL(url.pathname, apiBase) const response = await fetch(upstream.toString(), { method: request.method, headers: request.headers, body: request.method === 'GET' ? undefined : request.body, }) return response } ``` ## Service Worker adapter ```ts import { handleRequest } from './shared' globalThis.addEventListener('fetch', (event) => { event.respondWith(handleRequest(event.request)) }) ``` ## Cloudflare Worker adapter ```ts import { handleRequest } from './shared' export default { fetch(request: Request, env: Record) { return handleRequest(request, env) }, } ``` ## Node.js adapter (>=18) ```ts import { createServer } from 'node:http' import { handleRequest } from './shared' const server = createServer(async (req, res) => { const request = new Request(`http://localhost${req.url}`, { method: req.method, headers: req.headers as Record, }) const response = await handleRequest(request, process.env) res.writeHead(response.status, Object.fromEntries(response.headers)) res.end(await response.text()) }) server.listen(3000) ``` ## Common pitfalls * Passing Node `IncomingMessage` directly into the shared handler. * Assuming `process.env` exists in Workers. * Using `Buffer` in shared logic without guards. * Relying on `window` or DOM APIs for parsing URLs or storage. --- --- url: 'https://mokup.icebreaker.top/ai/prompt-templates-dts.md' --- # DTS Prompt Templates These templates generate response bodies only. The output should be valid JSON that can be saved directly as `*.get.json`. ## Placeholders * `{spec}`: Your DTS source * `{target}`: `TypeName` or a type alias description * `{count}`: Array length (for example, 10) * `{seed}`: Random seed (string) * `{locale}`: Locale for text fields (for example, `zh-CN` or `en-US`) * `{optional_ratio}`: Probability for optional fields (0~1) * `{null_ratio}`: Probability for nullable fields to be null (0~1) *** ## Approach 1: One-step + Repair (Recommended) ### Main generation template ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Task: Generate random mock data for {target} from the DTS spec below, as a Mokup response body. Input spec: {spec} Hard requirements: 1) Output must be valid JSON (no comments, no trailing commas, no extra text). 2) Output structure must match the {target} type definition. 3) If the target is an array or list, output length must be {count}. 4) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 5) Pick enum values randomly; generate realistic values for common formats (email/uuid/date-time/url/ipv4/ipv6). 6) Keep field order the same as the type declaration order. 7) Use {seed} as the random seed for stable output. 8) Use {locale} for names, addresses, phone numbers, and free-text fields. 9) If JSDoc examples or hints exist, prefer them but do not copy verbatim. Output JSON only. ``` ### Repair template (when output is invalid JSON or mismatched) ```text You are a JSON repair tool. Output the fixed JSON only. No explanations, no Markdown. Goal: Fix the "current output" so it becomes a valid JSON response body for {target} in the DTS spec. Input spec: {spec} Current output: {broken_json} Repair rules: 1) Output valid JSON only. 2) Structure must match the {target} type definition. 3) Do not add fields that are not in the type. 4) Keep field order the same as the type declaration order. Output JSON only. ``` *** ## Approach 2: Two-step (Normalize schema, then generate JSON) ### Step 1: Normalize to a compact schema ```text You are a schema normalization tool. Output JSON only. No explanations, no Markdown. Task: Normalize {target} in the DTS spec into a compact JSON schema that preserves structure, required/optional, nullable, enums, formats, and ranges. Input spec: {spec} Output format: { "type": "object|array|string|number|integer|boolean", "properties": { ... }, "required": [ ... ], "nullable": true|false, "enum": [ ... ], "format": "email|uuid|date-time|url|ipv4|ipv6|...", "items": { ... }, "min": number, "max": number } Output JSON only. ``` ### Step 2: Generate from normalized schema ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Task: Generate random mock data from the normalized schema as a Mokup response body. Normalized schema: {normalized_schema} Rules: 1) Output must be valid JSON. 2) If schema is an array, length must be {count}. 3) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 4) Pick enum values randomly; generate realistic values for formats. 5) Use {seed} as the random seed for stable output. 6) Use {locale} for names, addresses, phone numbers, and free-text fields. Output JSON only. ``` *** ## Approach 3: Few-shot (Stable but longer) ### Few-shot template ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Example 1: Input spec: type User = { id: string name: string age?: number role: 'admin' | 'user' } Target: User Output: {"id":"user_9f3","name":"Li Wei","age":24,"role":"admin"} Example 2: Input spec: type Paging = { total: number items: T[] } type Product = { id: number; title: string; price: number } Target: Paging Output: {"total":2,"items":[{"id":101,"title":"Canvas Bag","price":129},{"id":102,"title":"Desk Lamp","price":89}]} Now process: Task: Generate random mock data for {target} from the DTS spec below, as a Mokup response body. Input spec: {spec} Hard requirements: 1) Output must be valid JSON (no comments, no trailing commas, no extra text). 2) Output structure must match the {target} type definition. 3) If the target is an array or list, output length must be {count}. 4) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 5) Pick enum values randomly; generate realistic values for common formats (email/uuid/date-time/url/ipv4/ipv6). 6) Use {seed} as the random seed for stable output. 7) Use {locale} for names, addresses, phone numbers, and free-text fields. Output JSON only. ``` *** ## Usage tips * Provide the full type definitions around `{target}` to avoid ambiguity. * If the result fails JSON parsing or type validation, use the repair template immediately. --- --- url: 'https://mokup.icebreaker.top/zh/ai/prompt-templates-dts.md' --- # DTS 提示词模板 这些模板只生成响应 body。输出必须是合法 JSON,可直接保存为 `*.get.json`。 ## 占位符说明 * `{spec}`: 你的 dts 原文 * `{target}`: `TypeName` 或类型别名描述 * `{count}`: 数组条数(例如 10) * `{seed}`: 随机种子(字符串即可) * `{locale}`: 文本内容语言区域(例如 `zh-CN` 或 `en-US`) * `{optional_ratio}`: 可选字段取值概率(0~1,例如 0.7) * `{null_ratio}`: 可空字段为 null 的概率(0~1,例如 0.1) *** ## 方案一:单步模板 + 兜底修复模板(推荐) ### 主生成模板 ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 任务:根据 dts 规范为 {target} 生成随机 mock 数据,作为 Mokup 的响应 body。 输入规范: {spec} 硬性要求: 1) 输出必须是合法 JSON(不允许注释、尾逗号或多余文本)。 2) 输出结构必须匹配 {target} 的类型定义。 3) 若是数组或 list,输出数组长度为 {count}。 4) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 5) 对枚举值随机选取;对常见格式(email/uuid/date-time/url/ipv4/ipv6)生成合理值。 6) 保持字段顺序与类型声明顺序一致。 7) 使用 {seed} 作为随机种子,确保相同输入能得到稳定结果。 8) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 9) 若类型中提供 JSDoc 示例或注释提示,优先参考但不要完全拷贝。 只输出 JSON。 ``` ### 兜底修复模板(当模型输出非 JSON 或结构不匹配时) ```text 你是一个 JSON 修复器。只输出修复后的 JSON,不要解释、不要 Markdown。 目标:把“当前输出”修复成符合 dts 中 {target} 的合法 JSON 响应 body。 输入规范: {spec} 当前输出: {broken_json} 修复要求: 1) 只输出合法 JSON。 2) 结构必须匹配 {target} 的类型定义。 3) 不要引入类型中不存在的字段。 4) 保持字段顺序与类型声明顺序一致。 只输出 JSON。 ``` *** ## 方案二:两步模板(先规范化 schema,再生成 JSON) ### 第一步:规范化 schema ```text 你是一个 schema 规范化工具。只输出 JSON,不要解释、不要 Markdown。 任务:将 dts 中的 {target} 规范化为一个简洁 JSON schema(只保留类型结构、必填、可空、枚举、format、范围信息)。 输入规范: {spec} 输出格式要求: { "type": "object|array|string|number|integer|boolean", "properties": { ... }, "required": [ ... ], "nullable": true|false, "enum": [ ... ], "format": "email|uuid|date-time|url|ipv4|ipv6|...", "items": { ... }, "min": number, "max": number } 只输出 JSON。 ``` ### 第二步:从规范化 schema 生成响应 body ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 任务:根据“规范化 schema”生成随机 mock 数据,作为 Mokup 的响应 body。 规范化 schema: {normalized_schema} 生成规则: 1) 输出必须是合法 JSON。 2) 若 schema 为数组,长度为 {count}。 3) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 4) 对枚举值随机选取;对 format 生成合理值。 5) 使用 {seed} 作为随机种子,确保稳定输出。 6) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 只输出 JSON。 ``` *** ## 方案三:少量 few-shot 示例模板(稳定但更长) ### Few-shot(dts 模板) ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 示例 1: 输入规范: type User = { id: string name: string age?: number role: 'admin' | 'user' } 目标:User 输出: {"id":"user_9f3","name":"Li Wei","age":24,"role":"admin"} 示例 2: 输入规范: type Paging = { total: number items: T[] } type Product = { id: number; title: string; price: number } 目标:Paging 输出: {"total":2,"items":[{"id":101,"title":"Canvas Bag","price":129},{"id":102,"title":"Desk Lamp","price":89}]} 现在开始处理: 任务:根据 dts 规范为 {target} 生成随机 mock 数据,作为 Mokup 的响应 body。 输入规范: {spec} 硬性要求: 1) 输出必须是合法 JSON(不允许注释、尾逗号或多余文本)。 2) 输出结构必须匹配 {target} 的类型定义。 3) 若是数组或 list,输出数组长度为 {count}。 4) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 5) 对枚举值随机选取;对常见格式(email/uuid/date-time/url/ipv4/ipv6)生成合理值。 6) 使用 {seed} 作为随机种子,确保相同输入能得到稳定结果。 7) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 只输出 JSON。 ``` *** ## 使用建议 * dts:优先给出类型定义原文与目标类型名,避免歧义。 * 若结果 JSON 无法通过解析或类型校验,直接使用“兜底修复模板”。 --- --- url: 'https://mokup.icebreaker.top/reference/faq.md' --- # FAQ ## Why do JSON mocks need method suffixes? CLI builds use file names to determine HTTP methods. The Vite plugin defaults `.json/.jsonc` to `GET`, but explicit suffixes are recommended for consistency. ## Worker shows node builtin warnings Use `mokup/server/worker` or `mokup/server/fetch` to avoid pulling Node-only dependencies into Workers. ## Playground shows no data Check that: * The Vite plugin is enabled. * The path is `/__mokup` (or your custom path). * Mock filenames include method suffixes. ## Can JSON contain comments? Yes. Use `.jsonc` for comments and trailing commas. --- --- url: 'https://mokup.icebreaker.top/core-concepts/file-routing.md' --- # File Routing Mokup turns file paths into routes. Key rules: ## Method suffix Files must include an HTTP method suffix (`.get`, `.post`, etc.): ``` mock/users.get.json -> GET /users mock/users.post.ts -> POST /users ``` In the Vite plugin, `.json/.jsonc` files default to `GET` if no suffix is present. For CLI builds, an explicit suffix is recommended. ## REST methods at a glance Common RESTful routes map directly to file names: ``` mock/items/index.get.ts -> GET /items mock/items/index.post.ts -> POST /items mock/items/[id].get.ts -> GET /items/:id mock/items/[id].put.ts -> PUT /items/:id mock/items/[id].patch.ts -> PATCH /items/:id mock/items/[id].delete.ts -> DELETE /items/:id ``` ## index routes `index` maps to the directory root: ``` mock/index.get.json -> GET / mock/users/index.get.ts -> GET /users ``` ## Dynamic params Use brackets: ``` mock/users/[id].get.ts -> GET /users/:id mock/[action]/[id].get.ts -> GET /:action/:id ``` Access via `c.req.param('id')`: ```ts export default { handler: c => ({ id: c.req.param('id') }), } ``` Tip: you can wrap route exports with `defineHandler` for better IntelliSense: ```ts import { defineHandler } from 'mokup' export default defineHandler({ handler: c => ({ id: c.req.param('id') }), }) ``` You can return all params in one shot: ```ts import { defineHandler } from 'mokup' export default defineHandler(c => ({ params: c.req.param(), })) ``` ## Catch-all and optional segments ``` mock/docs/[...slug].get.ts -> /docs/* (at least 1 segment) mock/docs/[[...slug]].get.ts -> /docs (optional) ``` ## JSON and JSONC routes JSON files work as static handlers: ``` mock/status.get.json -> GET /status ``` JSONC supports comments and trailing commas: ``` mock/summary.get.jsonc -> GET /summary ``` Dynamic params also work with JSON routes: ``` mock/[action]/[id].get.json -> GET /:action/:id ``` ## Multi-rule files A file can export an array of rules. Disabled entries are skipped, enabled ones apply: ```ts import type { RouteRule } from 'mokup' const rules: RouteRule[] = [ { enabled: false, handler: () => ({ variant: 'disabled' }) }, { handler: () => ({ variant: 'active' }) }, ] export default rules ``` ## Disabled routes Disable a route by setting `enabled: false`: ```ts import { defineHandler } from 'mokup' export default defineHandler({ enabled: false, handler: () => ({ ok: false }), }) ``` ## Directory config (defineConfig) Use `index.config.ts` to define per-folder config, headers, delays, and middleware: ```ts import { defineConfig, onAfterAll, onBeforeAll } from 'mokup' export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(async (c, next) => { c.header('x-mokup-pre', '1') await next() }) }) onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-mokup-post', '1') }) }) return { delay: 20, headers: { 'x-mokup-demo': 'file-routing' }, } }) ``` ## Ignored files Unsupported extensions (like `.txt`) are ignored. By default, any path segment starting with `.` is ignored: ``` mock/notes.txt -> ignored mock/.draft/notes.get.ts -> ignored by default (.) mock/.ignored/skip.get.ts -> ignored by default (.) ``` You can override the ignore list via `ignorePrefix` (string or array). Setting it replaces the default list, so include `.` if you still want dot segments ignored: ```ts // mock/index.config.ts export default { ignorePrefix: ['.', 'draft-'], } ``` ``` mock/draft-legacy/skip.get.ts -> ignored when ignorePrefix includes "draft-" ``` --- --- url: 'https://mokup.icebreaker.top/core-concepts/handlers.md' --- # Handlers When `handler` is a function, Mokup treats it as an executable handler: ```ts export default { handler: async (c) => { c.status(200) c.header('x-mokup', 'handler') await new Promise(resolve => setTimeout(resolve, 120)) return c.json({ ok: true, params: c.req.param() }) }, } ``` Tip: you can wrap route exports with `defineHandler` for better IntelliSense: ```ts import { defineHandler } from 'mokup' export default defineHandler({ handler: async (c) => { c.status(200) c.header('x-mokup', 'handler') await new Promise(resolve => setTimeout(resolve, 120)) return c.json({ ok: true, params: c.req.param() }) }, }) ``` Or use the function form: ```ts import { defineHandler } from 'mokup' export default defineHandler((c) => { c.header('x-mokup', 'handler') return c.json({ ok: true, params: c.req.param() }) }) ``` Signature: * `c`: Hono `Context` (`c.req.param()`, `c.req.query()`, `c.req.json()`, `c.status()`, `c.header()`) When building with the CLI, handlers are bundled into `.mokup/mokup-handlers` and referenced as `module` responses in the manifest. --- --- url: 'https://mokup.icebreaker.top/advanced/hot-reload.md' --- # Hot Reload & Debug Mokup watches mock directories during Vite dev and refreshes routes automatically. ## Enable/disable watch ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', watch: true, }, }), ], } ``` Set `watch: false` to disable file watching. ## Debug tips * Playground refreshes on route changes (`mokup:routes-changed`). * Ensure file names include method suffixes. * Handler logs appear in Vite dev output. ## Debug mock handlers and middleware Mock handlers and directory middleware run on the Node side of Vite, so use a Node debugger rather than browser DevTools. ### VSCode (recommended) 1. Open the Command Palette and run **Debug: Create JavaScript Debug Terminal**. 2. In that terminal, start your dev command (for example `pnpm dev --filter `). 3. Set breakpoints in `mock/**/*.ts` or `mock/**/index.config.ts`. If you prefer a `launch.json`, use a Node launch config with `pnpm` and enable source maps and child process attach: ```json { "type": "node", "request": "launch", "name": "Vite Dev (mock debug)", "runtimeExecutable": "pnpm", "runtimeArgs": ["dev", "--filter", ""], "cwd": "${workspaceFolder}", "env": { "NODE_OPTIONS": "--enable-source-maps --inspect" }, "autoAttachChildProcesses": true } ``` ### Terminal + Node Inspector ```bash NODE_OPTIONS="--inspect-brk --enable-source-maps" pnpm dev --filter ``` Then attach from VSCode ("Attach to Node") or open `chrome://inspect`. ### Quick sanity checks * Add `debugger;` or `console.log` in the handler to confirm it is being loaded. * Preview builds may not support Vite dev debugging; use `dev` for breakpoints. --- --- url: 'https://mokup.icebreaker.top/getting-started/installation.md' --- # Installation ## Requirements * Node.js 20+ * pnpm (recommended) ## Packages Dev tooling (Vite plugin + CLI): ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: Runtime / server adapters (deploy or middleware): ::: code-group ```bash [pnpm] pnpm add mokup ``` ```bash [npm] npm install mokup ``` ```bash [yarn] yarn add mokup ``` ```bash [bun] bun add mokup ``` ::: If you only need Vite dev mocks, install `mokup` as a dev dependency. --- --- url: 'https://mokup.icebreaker.top/ai/llms-txt.md' --- # llms.txt `llms.txt` is a lightweight, LLM-friendly index of this documentation. It helps tools discover key pages and terms without you pasting the entire site. ## Where it lives This site publishes two LLM-friendly files during the VitePress build: * https://mokup.icebreaker.top/llms.txt * https://mokup.icebreaker.top/llms-full.txt ## How to use it * Paste the URL into your LLM and ask it to read it before answering. * Use it alongside the AI Prompts pages for response generation workflows. ## llms-full.txt `/llms-full.txt` bundles the full documentation into a single file. Use it when you want the model to search the entire docs set. ## Example prompts ```text Read https://mokup.icebreaker.top/llms.txt and list the best pages to learn Cloudflare deployment for Mokup. ``` ```text Use https://mokup.icebreaker.top/llms-full.txt as context and explain how to create a Worker entry with mokup/server/worker. ``` ## When it updates The file is regenerated on each docs build. Update the docs, rebuild, and the `llms.txt` content stays in sync. ## Related * [AI Prompts](./) --- --- url: 'https://mokup.icebreaker.top/zh/ai/llms-txt.md' --- # llms.txt `llms.txt` 是面向 LLM 的轻量索引,帮助模型快速理解文档结构与关键概念, 无需你手动粘贴整站内容。 ## 访问位置 VitePress 构建时会生成两份面向 LLM 的文件: * https://mokup.icebreaker.top/llms.txt * https://mokup.icebreaker.top/llms-full.txt ## 使用方式 * 将链接提供给 LLM,让它先读取再回答问题。 * 结合 AI 提示词页面使用,提升生成响应的准确性。 ## llms-full.txt `/llms-full.txt` 会把全部文档合并成单文件,适合让模型做全量检索。 ## 示例提示词 ```text 阅读 https://mokup.icebreaker.top/llms.txt ,列出学习 Mokup 在 Cloudflare 部署的最佳文档页面。 ``` ```text 使用 https://mokup.icebreaker.top/llms-full.txt 作为上下文,说明如何用 mokup/server/worker 创建 Worker 入口。 ``` ## 更新时机 `llms.txt` 会在每次文档构建时重新生成,更新文档后重新构建即可同步。 ## 相关链接 * [AI 提示词](./) --- --- url: 'https://mokup.icebreaker.top/core-concepts/manifest.md' --- # Manifest CLI builds output `.mokup/mokup.manifest.json` and `.mokup/mokup.manifest.mjs`, describing all routes and rules. Core shape (simplified): ```json { "version": 1, "routes": [ { "method": "GET", "url": "/users", "tokens": [], "score": [], "source": "mock/users.get.json", "response": { "type": "json", "body": [] } } ] } ``` When `response.type` is `module`, it points to a bundled handler: ```json { "type": "module", "module": "./mokup-handlers/mock/users.get.mjs", "ruleIndex": 0 } ``` `mokup.bundle.mjs` combines the manifest and handler module map for easy runtime usage. --- --- url: 'https://mokup.icebreaker.top/zh/core-concepts/manifest.md' --- # Manifest CLI 构建会输出 `.mokup/mokup.manifest.json` 与 `.mokup/mokup.manifest.mjs`,用于描述所有路由与响应规则。 核心结构(简化): ```json { "version": 1, "routes": [ { "method": "GET", "url": "/users", "tokens": [], "score": [], "source": "mock/users.get.json", "response": { "type": "json", "body": [] } } ] } ``` 当 `response.type` 为 `module` 时,表示该规则指向一个函数处理器: ```json { "type": "module", "module": "./mokup-handlers/mock/users.get.mjs", "ruleIndex": 0 } ``` `mokup.bundle.mjs` 会把 manifest 与 handler module map 打包成一个可直接导入的对象,适用于 Worker 或其他运行时。 --- --- url: 'https://mokup.icebreaker.top/reference/manifest-schema.md' --- # Manifest Schema `mokup.manifest.json` core fields: Use cases: * Inspect or validate generated manifests in CI pipelines. * Produce custom manifests for advanced tooling or testing. Demo: ```ts const manifest: Manifest = { version: 1, routes: [ { method: 'GET', url: '/api/ping', response: { type: 'text', body: 'ok' }, }, ], } ``` ```ts interface Manifest { version: 1 routes: ManifestRoute[] } interface ManifestRoute { method: string url: string tokens?: RouteToken[] score?: number[] source?: string status?: number headers?: Record delay?: number middleware?: ManifestModuleRef[] response: ManifestResponse } interface ManifestModuleRef { module: string exportName?: string ruleIndex?: number } ``` `ManifestResponse`: ```ts type ManifestResponse = | { type: 'json', body: unknown } | { type: 'text', body: string } | { type: 'binary', body: string, encoding: 'base64' } | ({ type: 'module' } & ManifestModuleRef) ``` `ruleIndex` selects an entry when a module exports an array of rules or middleware. `module` can be a relative path (CLI output) or a Vite module path (SW build). The CLI generates this for you automatically. --- --- url: 'https://mokup.icebreaker.top/zh/reference/manifest-schema.md' --- # Manifest 结构 `mokup.manifest.json` 的核心字段: 使用场景: * 在 CI 中校验或审阅生成的 manifest。 * 自定义工具或测试场景中生成 manifest。 示例: ```ts const manifest: Manifest = { version: 1, routes: [ { method: 'GET', url: '/api/ping', response: { type: 'text', body: 'ok' }, }, ], } ``` ```ts interface Manifest { version: 1 routes: ManifestRoute[] } interface ManifestRoute { method: string url: string tokens?: RouteToken[] score?: number[] source?: string status?: number headers?: Record delay?: number middleware?: ManifestModuleRef[] response: ManifestResponse } interface ManifestModuleRef { module: string exportName?: string ruleIndex?: number } ``` `ManifestResponse`: ```ts type ManifestResponse = | { type: 'json', body: unknown } | { type: 'text', body: string } | { type: 'binary', body: string, encoding: 'base64' } | ({ type: 'module' } & ManifestModuleRef) ``` `ruleIndex` 用于从模块导出的规则或中间件数组中选择具体条目,`module` 可以是相对路径(CLI 输出)或 Vite 模块路径(SW 构建)。 通常你不需要手写 manifest,CLI 会自动生成。 --- --- url: 'https://mokup.icebreaker.top/advanced/middleware-ordering.md' --- # Middleware Ordering & defineConfig Directory configs can register middleware with `defineConfig`. The middleware stages are `pre`, `normal`, and `post`, mapped to `onBeforeAll`, `app.use`, and `onAfterAll`. ## defineConfig in index.config.ts ```ts import type { MiddlewareHandler } from 'mokup' import { defineConfig, onAfterAll, onBeforeAll } from 'mokup' const requireAuth: MiddlewareHandler = async (c, next) => { const header = c.req.header('authorization') ?? '' if (!header.startsWith('Bearer ')) { c.status(401) return c.json({ ok: false, error: 'missing_auth' }) } await next() } export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(requireAuth) }) app.use(async (c, next) => { c.header('x-mokup-normal', '1') await next() }) onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-mokup-post', '1') }) }) return { headers: { 'x-mokup-example': 'ordering' }, } }) ``` ## defineHandler for route files `defineHandler` provides type hints in JS/TS without extra JSDoc. ```ts import { defineHandler } from 'mokup' export default defineHandler((c) => { return { ok: true, method: c.req.method } }) ``` ```ts import { defineHandler } from 'mokup' export default defineHandler({ enabled: false, handler: async (c) => { return { ok: false, reason: 'disabled-rule', method: c.req.method } }, }) ``` ## Ordering rules * Directory chain runs from root to nested directories. * Stage order per route: `pre` -> `normal` -> `post` -> handler. * Use `await next()` to run logic after later middleware and the handler. ## Nested directory example ``` mock/ index.config.ts nested/ index.config.ts info.get.ts ``` ```ts // mock/index.config.ts import { defineConfig, onBeforeAll } from 'mokup' export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(async (c, next) => { c.header('x-root-pre', '1') await next() }) }) }) ``` ```ts // mock/nested/index.config.ts import { defineConfig, onAfterAll } from 'mokup' export default defineConfig(({ app }) => { onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-nested-post', '1') }) }) }) ``` ## Object configs and legacy middleware * `onBeforeAll/app.use/onAfterAll` require `defineConfig`. * Plain object configs only support directory fields like `headers`, `status`, `delay`, `include`, and `exclude`. * `middleware` is a legacy field and behaves like the `normal` stage. ## Example map (apps/mokup-docs/mock) * `apps/mokup-docs/mock/example-basic`: minimal `onBeforeAll/app.use/onAfterAll` ordering. * `apps/mokup-docs/mock/example-order`: parent + nested ordering chain. * `apps/mokup-docs/mock/example-auth`: auth check in `pre` with headers in `post`. * `apps/mokup-docs/mock/example-metrics`: request duration and request id headers. * `apps/mokup-docs/mock/example-headers`: header overrides by stage. * `apps/mokup-docs/mock/example-errors`: error handling in `post`. * `apps/mokup-docs/mock/example-delay-status`: delay and status behavior. --- --- url: 'https://mokup.icebreaker.top/core-concepts/mock-rules.md' --- # Mock Rules A mock file can export a single rule or an array. Rule fields: * `handler`: response content or handler function (required) * `enabled`: enable/disable the rule (default `true`) * `status`: status code * `headers`: response headers * `delay`: delay in ms ## JSON / JSONC Return JSON directly, comments and trailing commas are supported: ```jsonc { // user profile "id": 1, "name": "Ada" } ``` ## TS/JS Export an object or array: ```ts export default { status: 201, headers: { 'x-mock': 'ok' }, handler: { ok: true }, } ``` Tip: you can wrap TS/JS rules with `defineHandler` for better IntelliSense: ```ts import { defineHandler } from 'mokup' export default defineHandler({ status: 201, headers: { 'x-mock': 'ok' }, handler: { ok: true }, }) ``` Array exports are supported, but each entry still uses file-based routing. ## Faker integration Use `@faker-js/faker` in TS/JS handlers to generate realistic mock data: ```ts import type { RequestHandler } from 'mokup' import { faker } from '@faker-js/faker' const handler: RequestHandler = () => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), createdAt: faker.date.recent({ days: 30 }).toISOString(), }) export default handler ``` Tip: you can also wrap the handler with `defineHandler` for better IntelliSense: ```ts import type { RequestHandler } from 'mokup' import { faker } from '@faker-js/faker' import { defineHandler } from 'mokup' const handler: RequestHandler = () => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), createdAt: faker.date.recent({ days: 30 }).toISOString(), }) export default defineHandler(handler) ``` Optional: seed the generator for reproducible output (place this once in a shared module): ```ts import { faker } from '@faker-js/faker' faker.seed(123) ``` --- --- url: 'https://mokup.icebreaker.top/zh/core-concepts/mock-rules.md' --- # Mock 规则 一个 mock 文件可以导出单条或多条规则。规则字段如下: * `handler`: 响应内容或处理函数(必填) * `enabled`: 启用/禁用该规则(默认 `true`) * `status`: 状态码 * `headers`: 响应头 * `delay`: 延迟毫秒数 ## JSON / JSONC 直接返回 JSON 内容,支持注释与尾逗号: ```jsonc { // user profile "id": 1, "name": "Ada" } ``` ## TS/JS 导出对象或数组: ```ts export default { status: 201, headers: { 'x-mock': 'ok' }, handler: { ok: true }, } ``` 提示:可以使用 `defineHandler` 包裹 TS/JS 规则以获得更好的类型提示: ```ts import { defineHandler } from 'mokup' export default defineHandler({ status: 201, headers: { 'x-mock': 'ok' }, handler: { ok: true }, }) ``` 数组导出仍使用文件路由生成路径,重复路由会提示告警。 ## Faker 集成 在 TS/JS 处理器中使用 `@faker-js/faker` 生成更真实的随机数据: ```ts import type { RequestHandler } from 'mokup' import { faker } from '@faker-js/faker' const handler: RequestHandler = () => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), createdAt: faker.date.recent({ days: 30 }).toISOString(), }) export default handler ``` 提示:也可以用 `defineHandler` 包裹 handler 以获得更好的类型提示: ```ts import type { RequestHandler } from 'mokup' import { faker } from '@faker-js/faker' import { defineHandler } from 'mokup' const handler: RequestHandler = () => ({ id: faker.string.uuid(), name: faker.person.fullName(), email: faker.internet.email(), createdAt: faker.date.recent({ days: 30 }).toISOString(), }) export default defineHandler(handler) ``` 可选:通过 seed 保证可复现的数据(建议放在共享模块中统一设置一次): ```ts import { faker } from '@faker-js/faker' faker.seed(123) ``` --- --- url: 'https://mokup.icebreaker.top/zh.md' description: 面向 Vite、Node 与 Worker 的文件式 HTTP 框架,统一路由与运行时,兼顾 Mock 与真实 API。 --- # Mokup 文档 Mokup 是一套以“文件即路由”为核心的 HTTP 框架,覆盖 Vite 本地开发、Node 中间件与 Worker 部署。你可以先用 mock 验证接口,再逐步演进为真实 API,路由与运行时保持一致。 建议从“快速开始”进入。 --- --- url: 'https://mokup.icebreaker.top/zh/blog/mokup-unified-mock-library.md' --- # Mokup:一个统一运行时的 Mock 库 我写了一个 mock 库,叫 Mokup。起因很简单:我希望一套 mock 路由能在本地开发、 Node 服务、Service Worker、Cloudflare Worker 上都能跑起来。很多 mock 工具在某个 环境里很好用,但一旦切到不同运行时就要重写。Mokup 的目标就是让这件事变得 无感、可复用。 ## Mokup 能做什么 Mokup 是一个基于文件路由的 HTTP mock 库,提供统一的运行时。你只写一次 mock 文件,Mokup 就能在以下环境中运行: * Vite dev 中间件 * 浏览器 Service Worker 模式 * Node 服务端适配器(Express / Koa / Fastify / Hono / Connect) * Worker 运行时(fetch handler 或 Worker entry) 它提供一致的请求处理逻辑、manifest 结构,以及可复用的构建产物。 ## 它是怎么工作的(1 分钟版) 1. 扫描 `mock/` 目录生成路由 manifest。 2. 运行时基于 manifest 做匹配与响应。 3. 各种适配器(Vite/Node/Worker)调用同一套运行时。 4. 在生产或 Worker 环境中,通过 bundle 把 manifest 与处理器一起打包。 一句话:一个路由模型,一个运行时,多种适配方式。 ## 快速开始 先接入 Vite 插件: ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api' }, }), ], } ``` 新增一个 mock 处理器: ```ts // mock/users.get.ts export default { handler: c => c.json([{ id: 1, name: 'Ada' }]), } ``` 提示:可以使用 `defineHandler` 包裹导出以获得更好的类型提示: ```ts import { defineHandler } from 'mokup' export default defineHandler({ handler: c => c.json([{ id: 1, name: 'Ada' }]), }) ``` 启动 Vite 后访问 `/api/users`。同一套路由可以继续在 Node 或 Worker 中复用。 ## 把同一套 mock 部署到 Worker 使用 Vite 的虚拟 bundle 并接入 Worker helper: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` 或者使用 fetch handler: ```ts import { createFetchHandler } from 'mokup/server/fetch' import mokupBundle from 'virtual:mokup-bundle' const handler = createFetchHandler(mokupBundle) export default { fetch: request => handler(request) } ``` ## 为什么有用 * 一套 mock 源码,多端复用 * 浏览器、Node、Worker 都可运行 * 文件路由直观,维护成本低 * Service Worker 模式无需后端代理 * manifest/bundle 产物便于 CI/CD 与部署 ## 适用目标 * 希望本地与预览环境一致的前端团队 * 同时在 Node 与边缘运行时部署的全栈团队 * 需要跨运行时 mock 的工具链作者 * 需要稳定、可复现数据的 QA / DX 流程 ## 从哪里开始 * Vite 用户:`mokup/vite` + Service Worker 模式 * Node 服务:`mokup/server/node` 适配器 * Worker:`mokup/server/worker` 或 `mokup/server/fetch` * CLI:`mokup build` 生成 bundle 产物 如果你希望 mock 可以随着应用一起迁移,从本地开发到边缘部署,那么 Mokup 就是 为此而生。 --- --- url: 'https://mokup.icebreaker.top/advanced/multi-dir-prefix.md' --- # Multi-Dir & Prefix The Vite plugin supports multiple directories and prefixes: ```ts import mokup from 'mokup/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api' }, { dir: 'mock-extra', prefix: '/api-extra' }, { dir: 'mock-ignored', prefix: '/api-ignored', watch: false }, ], }), ], }) ``` Notes: * `dir` accepts a string or array. * `prefix` is normalized with a leading `/`. * All entries are merged into one route table. For CLI builds, generate outputs per directory and combine at runtime if needed. --- --- url: 'https://mokup.icebreaker.top/getting-started/node-api.md' --- # Node.js API Use the Node dev server during development, and fall back to the runtime API for production builds. ## Dev mode (recommended) ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' } }) serve({ fetch: app.fetch, port: 3000 }) ``` You can also pass multiple directories: ```ts const app = await createFetchServer({ entries: [{ dir: 'mock' }, { dir: 'fixtures' }] }) serve({ fetch: app.fetch, port: 3000 }) ``` ## Deno / Bun (fetch handler) ```ts import { createFetchHandler } from 'mokup/server/fetch' const handler = createFetchHandler({ manifest }) const response = await handler(new Request('http://localhost/api/users')) ``` ## Build mode Use build mode when you want a stable bundle for production. ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import { createRuntime } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const runtime = createRuntime({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` ## Handle a request ```ts const result = await runtime.handle({ method: 'GET', path: '/api/users', query: {}, headers: {}, body: undefined, }) if (result) { console.log(result.status, result.headers, result.body) } ``` ## (Optional) Bridge to Node HTTP ```ts import http from 'node:http' http.createServer(async (req, res) => { const url = new URL(req.url ?? '/', 'http://localhost') const result = await runtime.handle({ method: req.method ?? 'GET', path: url.pathname, query: Object.fromEntries(url.searchParams), headers: req.headers, body: undefined, }) if (!result) { res.statusCode = 404 res.end('Not Found') return } res.statusCode = result.status for (const [key, value] of Object.entries(result.headers ?? {})) { res.setHeader(key, value) } res.end(result.body ?? '') }).listen(3000) ``` If you prefer framework middleware, see [Server Middleware](./server-middleware) or the [Server Adapters](/reference/server). --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/node-api.md' --- # Node.js API 开发阶段优先使用 Node dev server,生产构建再使用 runtime API。 ## Dev 模式(推荐) ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' } }) serve({ fetch: app.fetch, port: 3000 }) ``` 也可以传入多个目录: ```ts const app = await createFetchServer({ entries: [{ dir: 'mock' }, { dir: 'fixtures' }] }) serve({ fetch: app.fetch, port: 3000 }) ``` ## Deno / Bun(fetch 入口) ```ts import { createFetchHandler } from 'mokup/server/fetch' const handler = createFetchHandler({ manifest }) const response = await handler(new Request('http://localhost/api/users')) ``` ## Build 模式 如果你需要稳定的生产构建,请使用 build 模式。 ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import { createRuntime } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const runtime = createRuntime({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` ## 处理请求 ```ts const result = await runtime.handle({ method: 'GET', path: '/api/users', query: {}, headers: {}, body: undefined, }) if (result) { console.log(result.status, result.headers, result.body) } ``` ## (可选) 接入 Node HTTP ```ts import http from 'node:http' http.createServer(async (req, res) => { const url = new URL(req.url ?? '/', 'http://localhost') const result = await runtime.handle({ method: req.method ?? 'GET', path: url.pathname, query: Object.fromEntries(url.searchParams), headers: req.headers, body: undefined, }) if (!result) { res.statusCode = 404 res.end('Not Found') return } res.statusCode = result.status for (const [key, value] of Object.entries(result.headers ?? {})) { res.setHeader(key, value) } res.end(result.body ?? '') }).listen(3000) ``` 如果你更偏好框架中间件,请查看 [服务端中间件](./server-middleware) 或 [Server 适配器](/zh/reference/server)。 --- --- url: 'https://mokup.icebreaker.top/ai/prompt-templates-openapi.md' --- # OpenAPI Prompt Templates These templates generate response bodies only. The output should be valid JSON that can be saved directly as `*.get.json`. ## Placeholders * `{spec}`: Your OpenAPI source * `{target}`: `{method} {path} {status} {contentType}`, for example `GET /users 200 application/json` * `{count}`: Array length (for example, 10) * `{seed}`: Random seed (string) * `{locale}`: Locale for text fields (for example, `zh-CN` or `en-US`) * `{optional_ratio}`: Probability for optional fields (0~1) * `{null_ratio}`: Probability for nullable fields to be null (0~1) *** ## Approach 1: One-step + Repair (Recommended) ### Main generation template ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Task: Generate random mock data for {target} from the OpenAPI spec below, as a Mokup response body. Input spec: {spec} Hard requirements: 1) Output must be valid JSON (no comments, no trailing commas, no extra text). 2) Output structure must match the response schema for {target}. 3) If the response is an array or list, output length must be {count}. 4) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 5) Pick enum values randomly; generate realistic values for formats (email/uuid/date-time/url/ipv4/ipv6). 6) Keep field order the same as the schema declaration order. 7) Use {seed} as the random seed for stable output. 8) Use {locale} for names, addresses, phone numbers, and free-text fields. 9) If schema includes example/default, prefer them but do not copy verbatim. Output JSON only. ``` ### Repair template (when output is invalid JSON or mismatched) ```text You are a JSON repair tool. Output the fixed JSON only. No explanations, no Markdown. Goal: Fix the "current output" so it becomes a valid JSON response body for {target} in the OpenAPI spec. Input spec: {spec} Current output: {broken_json} Repair rules: 1) Output valid JSON only. 2) Structure must match the response schema for {target}. 3) Do not add fields that are not in the schema. 4) Keep field order the same as the schema declaration order. Output JSON only. ``` *** ## Approach 2: Two-step (Normalize schema, then generate JSON) ### Step 1: Normalize to a compact schema ```text You are a schema normalization tool. Output JSON only. No explanations, no Markdown. Task: Normalize {target} in the OpenAPI spec into a compact JSON schema that preserves structure, required/optional, nullable, enums, formats, and ranges. Input spec: {spec} Output format: { "type": "object|array|string|number|integer|boolean", "properties": { ... }, "required": [ ... ], "nullable": true|false, "enum": [ ... ], "format": "email|uuid|date-time|url|ipv4|ipv6|...", "items": { ... }, "min": number, "max": number } Output JSON only. ``` ### Step 2: Generate from normalized schema ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Task: Generate random mock data from the normalized schema as a Mokup response body. Normalized schema: {normalized_schema} Rules: 1) Output must be valid JSON. 2) If schema is an array, length must be {count}. 3) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 4) Pick enum values randomly; generate realistic values for formats. 5) Use {seed} as the random seed for stable output. 6) Use {locale} for names, addresses, phone numbers, and free-text fields. Output JSON only. ``` *** ## Approach 3: Few-shot (Stable but longer) ### Few-shot template ```text You are a strict JSON generator. Output JSON only. No explanations, no Markdown. Example 1: Input spec: paths: /users: get: responses: "200": content: application/json: schema: type: array items: type: object properties: id: { type: integer } email: { type: string, format: email } required: [id, email] Target: GET /users 200 application/json Output: [{"id":101,"email":"sam@example.com"},{"id":102,"email":"ava@example.com"}] Example 2: Input spec: paths: /orders/{id}: get: responses: "200": content: application/json: schema: type: object properties: id: { type: string, format: uuid } amount: { type: number } status: { type: string, enum: [paid, pending] } required: [id, amount, status] Target: GET /orders/{id} 200 application/json Output: {"id":"d2a4f7b2-7ed8-4e1a-9fd5-0c1b8c8f8b6a","amount":129.5,"status":"paid"} Now process: Task: Generate random mock data for {target} from the OpenAPI spec below, as a Mokup response body. Input spec: {spec} Hard requirements: 1) Output must be valid JSON (no comments, no trailing commas, no extra text). 2) Output structure must match the response schema for {target}. 3) If the response is an array or list, output length must be {count}. 4) Optional fields appear with probability {optional_ratio}; nullable fields are null with probability {null_ratio}. 5) Pick enum values randomly; generate realistic values for formats (email/uuid/date-time/url/ipv4/ipv6). 6) Use {seed} as the random seed for stable output. 7) Use {locale} for names, addresses, phone numbers, and free-text fields. Output JSON only. ``` *** ## Usage tips * Always specify `{method} {path} {status} {contentType}` to disambiguate the response. * If the result fails JSON parsing or schema validation, use the repair template immediately. --- --- url: 'https://mokup.icebreaker.top/zh/ai/prompt-templates-openapi.md' --- # OpenAPI 提示词模板 这些模板只生成响应 body。输出必须是合法 JSON,可直接保存为 `*.get.json`。 ## 占位符说明 * `{spec}`: 你的 OpenAPI 原文 * `{target}`: `{method} {path} {status} {contentType}`,例如 `GET /users 200 application/json` * `{count}`: 数组条数(例如 10) * `{seed}`: 随机种子(字符串即可) * `{locale}`: 文本内容语言区域(例如 `zh-CN` 或 `en-US`) * `{optional_ratio}`: 可选字段取值概率(0~1,例如 0.7) * `{null_ratio}`: 可空字段为 null 的概率(0~1,例如 0.1) *** ## 方案一:单步模板 + 兜底修复模板(推荐) ### 主生成模板 ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 任务:根据 OpenAPI 规范为 {target} 生成随机 mock 数据,作为 Mokup 的响应 body。 输入规范: {spec} 硬性要求: 1) 输出必须是合法 JSON(不允许注释、尾逗号或多余文本)。 2) 输出结构必须匹配 {target} 的响应 schema。 3) 若是数组或 list,输出数组长度为 {count}。 4) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 5) 对枚举值随机选取;对 format(email/uuid/date-time/url/ipv4/ipv6)生成合理值。 6) 保持字段顺序与 schema 中的声明顺序一致。 7) 使用 {seed} 作为随机种子,确保相同输入能得到稳定结果。 8) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 9) 若 schema 中提供 example/default,优先参考但不要完全拷贝。 只输出 JSON。 ``` ### 兜底修复模板(当模型输出非 JSON 或结构不匹配时) ```text 你是一个 JSON 修复器。只输出修复后的 JSON,不要解释、不要 Markdown。 目标:把“当前输出”修复成符合 OpenAPI 中 {target} 的合法 JSON 响应 body。 输入规范: {spec} 当前输出: {broken_json} 修复要求: 1) 只输出合法 JSON。 2) 结构必须匹配 {target} 的响应 schema。 3) 不要引入 schema 中不存在的字段。 4) 保持字段顺序与 schema 中的声明顺序一致。 只输出 JSON。 ``` *** ## 方案二:两步模板(先规范化 schema,再生成 JSON) ### 第一步:规范化 schema ```text 你是一个 schema 规范化工具。只输出 JSON,不要解释、不要 Markdown。 任务:将 OpenAPI 中的 {target} 规范化为一个简洁 JSON schema(只保留类型结构、必填、可空、枚举、format、范围信息)。 输入规范: {spec} 输出格式要求: { "type": "object|array|string|number|integer|boolean", "properties": { ... }, "required": [ ... ], "nullable": true|false, "enum": [ ... ], "format": "email|uuid|date-time|url|ipv4|ipv6|...", "items": { ... }, "min": number, "max": number } 只输出 JSON。 ``` ### 第二步:从规范化 schema 生成响应 body ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 任务:根据“规范化 schema”生成随机 mock 数据,作为 Mokup 的响应 body。 规范化 schema: {normalized_schema} 生成规则: 1) 输出必须是合法 JSON。 2) 若 schema 为数组,长度为 {count}。 3) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 4) 对枚举值随机选取;对 format 生成合理值。 5) 使用 {seed} 作为随机种子,确保稳定输出。 6) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 只输出 JSON。 ``` *** ## 方案三:少量 few-shot 示例模板(稳定但更长) ### Few-shot(OpenAPI 模板) ```text 你是一个严格的 JSON 生成器。只输出 JSON,不要解释、不要 Markdown。 示例 1: 输入规范: paths: /users: get: responses: "200": content: application/json: schema: type: array items: type: object properties: id: { type: integer } email: { type: string, format: email } required: [id, email] 目标:GET /users 200 application/json 输出: [{"id":101,"email":"sam@example.com"},{"id":102,"email":"ava@example.com"}] 示例 2: 输入规范: paths: /orders/{id}: get: responses: "200": content: application/json: schema: type: object properties: id: { type: string, format: uuid } amount: { type: number } status: { type: string, enum: [paid, pending] } required: [id, amount, status] 目标:GET /orders/{id} 200 application/json 输出: {"id":"d2a4f7b2-7ed8-4e1a-9fd5-0c1b8c8f8b6a","amount":129.5,"status":"paid"} 现在开始处理: 任务:根据 OpenAPI 规范为 {target} 生成随机 mock 数据,作为 Mokup 的响应 body。 输入规范: {spec} 硬性要求: 1) 输出必须是合法 JSON(不允许注释、尾逗号或多余文本)。 2) 输出结构必须匹配 {target} 的响应 schema。 3) 若是数组或 list,输出数组长度为 {count}。 4) 可选字段按 {optional_ratio} 的概率出现;nullable 字段按 {null_ratio} 的概率为 null。 5) 对枚举值随机选取;对 format(email/uuid/date-time/url/ipv4/ipv6)生成合理值。 6) 使用 {seed} 作为随机种子,确保相同输入能得到稳定结果。 7) 姓名/地址/电话/文案等文本内容请遵循 {locale}。 只输出 JSON。 ``` *** ## 使用建议 * OpenAPI:尽量指定明确的 `{method} {path} {status} {contentType}`。 * 若结果 JSON 无法通过解析或类型校验,直接使用“兜底修复模板”。 --- --- url: 'https://mokup.icebreaker.top/getting-started/overview.md' --- # Overview Mokup is a file-driven HTTP framework that covers both dev and deployment: * **Dev**: `mokup/vite` intercepts requests in Vite dev and serves route handlers. * **Build**: CLI generates `.mokup` output (manifest + handlers). * **Runtime**: `mokup/runtime` handles matching, delays, headers, and responses. * **Adapters**: `mokup/server/node` provides Node adapters; `mokup/server/fetch` is runtime-agnostic; `mokup/server/worker` targets Workers. Use it when: * You want file-based HTTP routes for internal tools or production APIs. * You need mockable endpoints or fast prototyping. * You want one ruleset across multiple runtimes. --- --- url: 'https://mokup.icebreaker.top/advanced/playground.md' --- # Playground Playground is a built-in UI for browsing and debugging mock APIs. ## Default entry ``` http://localhost:5173/__mokup ``` ## Configure path ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, playground: { path: '/__mokup', enabled: true, }, }), ], } ``` Set `playground: false` to disable it. For static builds, set `playground.build: true` so `vite build` emits the Playground assets and `/__mokup/routes` JSON under the configured path. ## Features * Grouped route listing * Request method/path inspection * Live refresh on file changes --- --- url: 'https://mokup.icebreaker.top/zh/advanced/playground.md' --- # Playground Playground 是一个内置的可视化面板,用于浏览和调试当前已加载的 mock 接口。 ## 默认入口 ``` http://localhost:5173/__mokup ``` ## 配置入口 ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, playground: { path: '/__mokup', enabled: true, }, }), ], } ``` 当 `playground: false` 时将禁用。 需要静态部署时,可设置 `playground.build: true`,在 `vite build` 中输出 Playground 资源与 `/__mokup/routes` JSON 到对应路径。 ## 功能 * 按目录/分组展示路由 * 查看请求方法、路径与响应类型 * 与 Vite 热更新联动,文件变更会刷新路由 --- --- url: 'https://mokup.icebreaker.top/getting-started/quick-start.md' --- # Quick Start Pick the setup that matches your stack. All flows share the same mock files. ## 1. Create a mock file Create `mock/users.get.json`: ```json { "id": 1, "name": "Ada" } ``` ## 2. Choose a path * [Vite plugin](./vite) — recommended for Vite dev and preview. * [Webpack plugin](./webpack) — integrates with webpack-dev-server. * [Hono adapter](/reference/server#hono) — run mocks in any Hono runtime. * [Cloudflare Worker](/deploy/cloudflare-worker) — deploy with the Worker entry. * [Node.js API](../node-api) — call the runtime directly in Node. * [Server middleware](../server-middleware) — plug into existing servers. Playground defaults to `/__mokup` in Vite dev. --- --- url: 'https://mokup.icebreaker.top/reference/runtime.md' --- # Runtime API `mokup/runtime` is the core matcher and response executor. ## createRuntime Use cases: * Build a custom fetch adapter or test harness by calling `runtime.handle` directly. * Run Mokup in environments that do not use Hono (SSR, Workers, integration tests). Demo: ```ts import { createRuntime } from 'mokup/runtime' const runtime = createRuntime({ manifest }) const result = await runtime.handle({ method: 'GET', path: '/users', query: {}, headers: {}, body: undefined, }) ``` ## Direct usage with CLI bundle Use the CLI bundle to load a manifest and handler module map in your runtime: Use cases: * Run mocks in production runtimes without filesystem access. * Share a prebuilt bundle across multiple deployments. Demo: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import { createRuntime } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const runtime = createRuntime({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` If you are not bundling handlers, you can omit `moduleMap` and `moduleBase`. ## createRuntimeApp Build a Hono app from the manifest (useful for Service Worker or custom fetch handlers). Use cases: * Build a fetch handler backed by Mokup routes for Workers or Service Workers. * Integrate with a Hono-compatible runtime without writing adapters. Demo: ```ts import { createRuntimeApp } from 'mokup/runtime' const app = await createRuntimeApp({ manifest }) const response = await app.fetch(new Request('http://localhost/api/users')) ``` ## Service Worker helper `mokup/runtime` also re-exports `handle` from `hono/service-worker`. Use cases: * Wire a Hono app into the Service Worker `fetch` event. * Keep worker glue code minimal while using Mokup routes. Demo: ```ts import { createRuntimeApp, handle } from 'mokup/runtime' const app = await createRuntimeApp({ manifest }) globalThis.addEventListener('fetch', handle(app)) ``` ## RuntimeOptions Use cases: * Pass a lazy manifest loader to defer bundle loading. * Provide `moduleMap`/`moduleBase` when your runtime cannot import local files. ```ts export interface RuntimeOptions { manifest: Manifest | (() => Promise) moduleBase?: string | URL moduleMap?: Record> } ``` `moduleMap` is required for Worker-like environments where you cannot import local files directly. Demo: ```ts import type { RuntimeOptions } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const options: RuntimeOptions = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` --- --- url: 'https://mokup.icebreaker.top/zh/reference/runtime.md' --- # Runtime API `mokup/runtime` 是路由匹配与响应处理的核心。 ## createRuntime 使用场景: * 在自定义运行时或测试环境中直接调用 `runtime.handle`。 * 需要在非 Hono 环境中运行 mock(SSR、Worker、集成测试)。 示例: ```ts import { createRuntime } from 'mokup/runtime' const runtime = createRuntime({ manifest }) const result = await runtime.handle({ method: 'GET', path: '/users', query: {}, headers: {}, body: undefined, }) ``` ## 直接使用 CLI bundle 可直接加载 CLI 生成的 bundle: 使用场景: * 在生产或受限运行时中复用预构建 bundle。 * 多个部署共享同一份构建产物。 示例: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import { createRuntime } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const runtime = createRuntime({ manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, }) ``` 如果没有打包函数处理器,可以省略 `moduleMap` 与 `moduleBase`。 ## createRuntimeApp 根据 manifest 构建 Hono app(适用于 Service Worker 或自定义 fetch 处理)。 使用场景: * 在 Worker / Service Worker 中使用 Hono 风格的 fetch 入口。 * 在任意支持 Hono 的运行时复用 Mokup 路由。 示例: ```ts import { createRuntimeApp } from 'mokup/runtime' const app = await createRuntimeApp({ manifest }) const response = await app.fetch(new Request('http://localhost/api/users')) ``` ## Service Worker helper `mokup/runtime` 也会导出 `hono/service-worker` 的 `handle`。 使用场景: * 在 Service Worker 的 `fetch` 事件中挂载 Hono app。 * 用最少的胶水代码运行 Mokup。 示例: ```ts import { createRuntimeApp, handle } from 'mokup/runtime' const app = await createRuntimeApp({ manifest }) globalThis.addEventListener('fetch', handle(app)) ``` ## RuntimeOptions 使用场景: * 延迟加载 manifest(按需读取或动态 import)。 * Worker 等无法直接 import 本地文件时传入 `moduleMap`/`moduleBase`。 ```ts export interface RuntimeOptions { manifest: Manifest | (() => Promise) moduleBase?: string | URL moduleMap?: Record> } ``` `moduleMap` 用于 Worker 等无法动态 import 本地文件的场景,通常由 `mokup.bundle.mjs` 生成。 示例: ```ts import type { RuntimeOptions } from 'mokup/runtime' import mokupBundle from './.mokup/mokup.bundle.mjs' const options: RuntimeOptions = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` --- --- url: 'https://mokup.icebreaker.top/reference/server.md' --- # Server Adapters `mokup/server/node` bundles the Node adapters and dev server helpers. Use `mokup/server/worker` for Workers or `mokup/server/fetch` for runtime-agnostic fetch handlers. ## Fetch server (Node) Use cases: * Start a standalone mock server from local mock files. * Embed a fetch-capable mock server inside another Node process. Demo: ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' }, playground: false, }) serve({ fetch: app.fetch, port: 3000 }) ``` You can call `app.fetch` directly: Use cases: * Call the mock server from tests or other server logic without binding a port. Demo: ```ts const response = await app.fetch(new Request('http://localhost/api/users')) ``` ## Options Use cases: * Provide `moduleMap`/`moduleBase` when running in a sandboxed runtime. * Choose `onNotFound: 'response'` for a standalone fetch handler that should return 404. ```ts export interface ServerOptions { manifest: Manifest | (() => Promise) moduleBase?: string | URL moduleMap?: Record> onNotFound?: 'next' | 'response' } ``` `onNotFound` defaults to `'next'`. Use `'response'` to return 404 instead of falling through. The Hono adapter runs anywhere Hono can run. Use the Worker entry for Cloudflare Workers. Demo: ```ts import type { ServerOptions } from 'mokup/server' import mokupBundle from './.mokup/mokup.bundle.mjs' const options: ServerOptions = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, onNotFound: 'response', } ``` ## Prepare manifest You can load the CLI build output directly: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import mokupBundle from './.mokup/mokup.bundle.mjs' const options = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` Pass `options` to any adapter below. For brevity, the examples use `manifest` directly. ## Express Use cases: * Add mock routes into an existing Express app. * Reuse the same manifest across multiple Express instances. Demo: ```ts import { createExpressMiddleware } from 'mokup/server/node' app.use(createExpressMiddleware({ manifest })) ``` ## Connect Use cases: * Add mocks to a Connect-based stack or legacy middleware chain. Demo: ```ts import { createConnectMiddleware } from 'mokup/server/node' app.use(createConnectMiddleware({ manifest })) ``` ## Koa Use cases: * Inject mock routes into a Koa server without rewriting handlers. Demo: ```ts import { createKoaMiddleware } from 'mokup/server/node' app.use(createKoaMiddleware({ manifest })) ``` ## Hono Use cases: * Mount Mokup inside a Hono app running on Node or edge runtimes. Demo: ```ts import { createHonoMiddleware } from 'mokup/server/node' app.use(createHonoMiddleware({ manifest })) ``` ## Fastify Use cases: * Plug Mokup into a Fastify server with the standard plugin API. Demo: ```ts import { createFastifyPlugin } from 'mokup/server/node' await app.register(createFastifyPlugin({ manifest })) ``` ## Fetch / Worker Use cases: * Build a runtime-agnostic fetch handler (Workers, edge, custom servers). * Combine Mokup routes with your own routing logic. Demo: ```ts import { createFetchHandler } from 'mokup/server/fetch' const handler = createFetchHandler({ manifest }) const response = await handler(new Request('https://example.com/api')) ``` ## Worker entry For Workers (including Cloudflare Workers), use the helper entry. It wraps `createFetchHandler` from `mokup/server/fetch` and returns a 404 response when the handler yields `null`: Use cases: * Deploy Mokup to Cloudflare Workers with the smallest entry file. * Avoid manual 404 handling when the mock handler returns `null`. Demo: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` --- --- url: 'https://mokup.icebreaker.top/getting-started/server-middleware.md' --- # Server Middleware Use middleware when you already have a server app and want to inject mokup routes. If you just need a dev server with hot reload, use `createFetchServer`. ## Dev server (recommended) ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' } }) serve({ fetch: app.fetch, port: 3000 }) ``` ## Middleware integration (build mode) Use build mode when you want a stable bundle for production. ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import mokupBundle from './.mokup/mokup.bundle.mjs' const options = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` ## Register the middleware ### Express ```ts import express from 'express' import { createExpressMiddleware } from 'mokup/server/node' const app = express() app.use(createExpressMiddleware(options)) ``` ### Koa ```ts import Koa from 'koa' import { createKoaMiddleware } from 'mokup/server/node' const app = new Koa() app.use(createKoaMiddleware(options)) ``` ### Fastify ```ts import Fastify from 'fastify' import { createFastifyPlugin } from 'mokup/server/node' const app = Fastify() await app.register(createFastifyPlugin(options)) ``` ### Hono ```ts import { Hono } from 'hono' import { createHonoMiddleware } from 'mokup/server/node' const app = new Hono() app.use(createHonoMiddleware(options)) ``` See the [Server Adapters](/reference/server) for more frameworks and options. --- --- url: 'https://mokup.icebreaker.top/zh/reference/server.md' --- # Server 适配器 `mokup/server/node` 提供 Node 适配器与开发服务器。Worker 请使用 `mokup/server/worker`,运行时无关的 fetch 入口请使用 `mokup/server/fetch`。 ## Fetch 入口(Node) 使用场景: * 从本地 mock 目录快速启动独立的 mock 服务。 * 在 Node 进程中嵌入一个 fetch 风格的 mock 服务。 示例: ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' }, playground: false, }) serve({ fetch: app.fetch, port: 3000 }) ``` 可以直接调用 `app.fetch`: 使用场景: * 在测试或服务内部逻辑中直接请求 mock 服务。 示例: ```ts const response = await app.fetch(new Request('http://localhost/api/users')) ``` ## 选项 使用场景: * 在受限运行时中传入 `moduleMap`/`moduleBase`。 * 使用 `onNotFound: 'response'` 让 handler 直接返回 404。 ```ts export interface ServerOptions { manifest: Manifest | (() => Promise) moduleBase?: string | URL moduleMap?: Record> onNotFound?: 'next' | 'response' } ``` `onNotFound` 默认是 `'next'`,设为 `'response'` 会直接返回 404。 Hono 适配器可在 Hono 支持的运行时中使用,Cloudflare Worker 请使用专用入口。 示例: ```ts import type { ServerOptions } from 'mokup/server' import mokupBundle from './.mokup/mokup.bundle.mjs' const options: ServerOptions = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, onNotFound: 'response', } ``` ## 准备 manifest 可直接加载 CLI 生成的 bundle: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import mokupBundle from './.mokup/mokup.bundle.mjs' const options = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` 上面的 `options` 可直接传给任意适配器,下方示例为简洁起见直接传 `manifest`。 ## Express 使用场景: * 在现有 Express 服务中挂载 mock 路由。 示例: ```ts import { createExpressMiddleware } from 'mokup/server/node' app.use(createExpressMiddleware({ manifest })) ``` ## Connect 使用场景: * 兼容 Connect/legacy middleware 栈。 示例: ```ts import { createConnectMiddleware } from 'mokup/server/node' app.use(createConnectMiddleware({ manifest })) ``` ## Koa 使用场景: * 给 Koa 服务注入 mock 路由。 示例: ```ts import { createKoaMiddleware } from 'mokup/server/node' app.use(createKoaMiddleware({ manifest })) ``` ## Hono 使用场景: * 在 Hono App 中插入 Mokup mock 路由。 示例: ```ts import { createHonoMiddleware } from 'mokup/server/node' app.use(createHonoMiddleware({ manifest })) ``` ## Fastify 使用场景: * 通过 Fastify 插件方式接入 Mokup。 示例: ```ts import { createFastifyPlugin } from 'mokup/server/node' await app.register(createFastifyPlugin({ manifest })) ``` ## Fetch / Worker 使用场景: * 运行时无关的 fetch 处理(Worker、边缘运行时或自定义服务)。 * 与你自己的路由系统组合使用。 示例: ```ts import { createFetchHandler } from 'mokup/server/fetch' const handler = createFetchHandler({ manifest }) const response = await handler(new Request('https://example.com/api')) ``` ## Worker 入口 Worker 环境(含 Cloudflare Workers)请使用 helper 入口,它基于 `mokup/server/fetch` 的 `createFetchHandler` 封装,并在 handler 返回 `null` 时统一输出 404: 使用场景: * 以最小入口文件部署到 Cloudflare Workers。 * 自动处理 `null` 结果的 404 响应。 示例: ```ts import { createMokupWorker } from 'mokup/server/worker' import mokupBundle from 'virtual:mokup-bundle' export default createMokupWorker(mokupBundle) ``` --- --- url: 'https://mokup.icebreaker.top/deploy/vite-build.md' --- # Vite Build Output Generate deployable artifacts with the CLI: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: Output structure: ``` .mokup/ mokup.manifest.json mokup.manifest.mjs mokup.manifest.d.mts mokup.bundle.mjs mokup.bundle.d.ts mokup.bundle.d.mts mokup-handlers/ (optional) ``` `mokup.bundle.mjs` is the easiest entry for Workers or custom runtimes. ## Service Worker build When you set `mode: 'sw'` in the Vite plugin, the service worker script is emitted during `vite build` (default `/mokup-sw.js`). The plugin also injects a registration snippet unless `sw.register` is `false`. ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', }, }, }), ], } ``` This is ideal for static hosting because mock requests are handled in the browser. If you also ship the playground, set `playground: { build: true }` so `vite build` emits the Playground assets and `/__mokup/routes`. As an alternative, you can generate `/__mokup/routes` with `mokup build` or a small script and publish it alongside the site. Notes: * `sw.basePath` controls which requests the SW intercepts. If omitted, it inherits the entry `prefix`. --- --- url: 'https://mokup.icebreaker.top/reference/vite-plugin.md' --- # Vite Plugin Use `mokup/vite` as the Vite plugin entry. ## Install ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## Usage Use cases: * Mock APIs in Vite dev without standing up a separate server. * Keep mock routes close to frontend code with hot reload support. Demo: ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, }), ], } ``` ## Options ### Plugin options | Option | Type | Description | | ------------ | ----------------------------------------------------------------- | ----------------------------------- | | `entries` | `VitePluginOptions / VitePluginOptions[]` | Mock entry configs | | `playground` | `boolean / { path?: string; enabled?: boolean; build?: boolean }` | Playground config | | `runtime` | `'node' / 'worker'` | Dev runtime target (default `node`) | ### Entry options | Option | Type | Description | | -------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | `dir` | `string / string[] / (root) => string / string[]` | Mock directory | | `prefix` | `string` | URL prefix | | `include` | `RegExp / RegExp[]` | Include files | | `exclude` | `RegExp / RegExp[]` | Exclude files | | `ignorePrefix` | `string / string[]` | Ignore path segment prefixes (default `.`) | | `watch` | `boolean` | Watch file changes | | `log` | `boolean` | Enable logging | | `mode` | `'server' / 'sw'` | Mock runtime mode | | `sw` | `{ path?: string; scope?: string; register?: boolean; unregister?: boolean; fallback?: boolean; basePath?: string / string[] }` | Service worker options (SW mode only) | Use `runtime: 'worker'` to skip Vite dev middleware and let a Worker handle mock requests instead: Use cases: * Run mocks in a Worker runtime while still using Vite for bundling. * Validate Worker deployment behavior during local development. Demo: ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ runtime: 'worker', entries: { dir: 'mock', prefix: '/api' }, }), ], } ``` ## Routing notes * `index` files collapse to the directory root; use `mock/api/index/index.get.ts` for `/api/index`. * Any directory or file segment starting with `ignorePrefix` is ignored (default `.`). * `index.config.ts` can override `headers/status/delay/enabled/ignorePrefix/include/exclude` and add directory middleware. * TS/JS mocks can disable a single route with `enabled: false`. ## Directory config Use `index.config.ts` in any directory to customize matching and defaults: Use cases: * Apply shared headers/status/delay to all routes under a directory. * Add middleware once per directory instead of per-file. Demo: ```ts import type { RouteDirectoryConfig } from 'mokup' const config: RouteDirectoryConfig = { enabled: true, headers: { 'x-mokup-scope': 'api' }, status: 200, delay: 120, ignorePrefix: ['.', '_'], include: /users/, exclude: /skip/, middleware: [ async (c, next) => { c.header('x-mokup', 'dir') return await next() }, ], } export default config ``` Tip: you can use `defineConfig` for better IntelliSense: ```ts import { defineConfig } from 'mokup' export default defineConfig({ enabled: true, headers: { 'x-mokup-scope': 'api' }, status: 200, delay: 120, ignorePrefix: ['.', '_'], include: /users/, exclude: /skip/, middleware: [ async (c, next) => { c.header('x-mokup', 'dir') return await next() }, ], }) ``` Configs cascade from root to leaf. Headers merge, and middleware chains are appended in order. ## Service Worker mode Set `mode: 'sw'` to generate a service worker that runs mock handlers in the browser. The plugin serves the SW in dev/preview and emits it during build at `sw.path` (default `/mokup-sw.js`, scope defaults to `/`). Registration is injected automatically unless `sw.register` is `false`. Use cases: * Mock APIs in the browser without a dev server proxy. * Test request flows with offline/cache behavior. ### Basic Demo: ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', }, }), ], } ``` ### Custom path/scope + manual registration Demo (plugin config): ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', register: false, }, }, }), ], } ``` Use cases: * Register the SW manually for multi-app pages or conditional bootstrapping. * Keep SW registration out of the plugin for custom UX flows. Demo (manual register): ```ts import { registerMokupServiceWorker } from 'mokup/sw' registerMokupServiceWorker({ path: '/mokup-sw.js', scope: '/', }) ``` ### Mixed entries Use cases: * Serve some routes from SW while keeping internal APIs on the dev server. Demo: ```ts export default { plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api', mode: 'sw', sw: { fallback: false } }, { dir: 'mock-server', prefix: '/internal', mode: 'server' }, ], }), ], } ``` Notes: * `sw.fallback` defaults to `true`. Set it to `false` to disable server middleware for that entry. * `sw.basePath` controls which requests the SW intercepts. If omitted, it inherits the entry `prefix`. Empty prefixes mean the SW can intercept any path. * If multiple entries use SW mode, the first `sw.path`/`sw.scope`/`sw.register`/`sw.unregister` wins; conflicting values are ignored with a warning. ### Intercept scope Use cases: * Limit the SW to a subset of routes while keeping other paths untouched. Demo: ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { basePath: '/api', }, }, }), ], } ``` ### Unregister Use cases: * Clean up existing SW registrations during development. * Ship an uninstall-only build to remove old mocks. Demo (plugin config): ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { unregister: true, }, }, }), ], } ``` Use cases: * Manually unregister from a client-side control flow. Demo (manual unregister): ```ts import { unregisterMokupServiceWorker } from 'mokup/sw' await unregisterMokupServiceWorker({ path: '/mokup-sw.js', scope: '/', }) ``` Notes: * `sw.unregister: true` injects an uninstall script and skips registration. * If there are no SW entries, the plugin auto-injects an uninstall script using the configured `sw.path`/`sw.scope` (or defaults) to clean stale registrations. ## Multi-dir Use cases: * Combine multiple mock directories with different URL prefixes. Demo: ```ts export default { plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api' }, { dir: 'mock-extra', prefix: '/api-extra' }, ], }), ], } ``` --- --- url: 'https://mokup.icebreaker.top/getting-started/quick-start/vite.md' --- # Vite Quick Start ## 1. Install ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 2. Enable the plugin ```ts import mokup from 'mokup/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, }), ], }) ``` ## 3. Start dev server ::: code-group ```bash [pnpm] pnpm dev ``` ```bash [npm] npm run dev ``` ```bash [yarn] yarn dev ``` ```bash [bun] bun run dev ``` ::: ## 4. Verify * `http://localhost:5173/api/users` * `http://localhost:5173/__mokup` ## Next See the full options in [Vite Plugin](/reference/vite-plugin). --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/quick-start/vite.md' --- # Vite 快速开始 ## 1. 安装 ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 2. 启用插件 ```ts import mokup from 'mokup/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, }), ], }) ``` ## 3. 启动开发服务 ::: code-group ```bash [pnpm] pnpm dev ``` ```bash [npm] npm run dev ``` ```bash [yarn] yarn dev ``` ```bash [bun] bun run dev ``` ::: ## 4. 验证 * `http://localhost:5173/api/users` * `http://localhost:5173/__mokup` ## 下一步 完整选项请参考 [Vite 插件](/zh/reference/vite-plugin)。 --- --- url: 'https://mokup.icebreaker.top/zh/reference/vite-plugin.md' --- # Vite 插件 Mokup 的 Vite 插件入口为 `mokup/vite`。 ## 安装 ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 基本用法 使用场景: * 在 Vite dev 中直接 mock API,无需额外服务器。 * 将 mock 路由与前端工程放在同一仓库。 示例: ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', }, }), ], } ``` ## 选项 ### 插件选项 | 选项 | 类型 | 说明 | | ------------ | ----------------------------------------------------------------- | --------------------------- | | `entries` | `VitePluginOptions / VitePluginOptions[]` | mock 目录配置 | | `playground` | `boolean / { path?: string; enabled?: boolean; build?: boolean }` | Playground 配置 | | `runtime` | `'node' / 'worker'` | 开发运行目标(默认 `node`) | ### Entry 选项 | 选项 | 类型 | 说明 | | -------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | | `dir` | `string / string[] / (root) => string / string[]` | mock 目录 | | `prefix` | `string` | 路由前缀 | | `include` | `RegExp / RegExp[]` | 仅包含匹配文件 | | `exclude` | `RegExp / RegExp[]` | 排除匹配文件 | | `ignorePrefix` | `string / string[]` | 忽略路径段前缀(默认 `.`) | | `watch` | `boolean` | 是否监听文件变化 | | `log` | `boolean` | 是否输出日志 | | `mode` | `'server' / 'sw'` | mock 运行模式 | | `sw` | `{ path?: string; scope?: string; register?: boolean; unregister?: boolean; fallback?: boolean; basePath?: string / string[] }` | Service Worker 配置(仅 SW 模式) | 使用 `runtime: 'worker'` 可跳过 Vite dev 中间件,让 Worker 负责 mock 请求: 使用场景: * 在 Worker 运行时调试与部署一致的行为。 * 通过 Vite 完成打包,同时把 mock 交给 Worker 处理。 示例: ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ runtime: 'worker', entries: { dir: 'mock', prefix: '/api' }, }), ], } ``` ## 路由备注 * `index` 文件会合并为目录根路径;若需要 `/api/index`,请使用 `mock/api/index/index.get.ts`。 * 以 `ignorePrefix` 指定前缀开头的目录或文件会被忽略,默认忽略 `.`。 * `index.config.ts` 可覆盖 `headers/status/delay/enabled/ignorePrefix/include/exclude` 并添加目录中间件。 * TS/JS mock 可通过 `enabled: false` 暂停单个接口。 ## 目录配置 在目录内新增 `index.config.ts`,可配置匹配与默认行为: 使用场景: * 给目录下的所有接口统一响应头/状态码/延迟。 * 将目录级中间件集中管理。 示例: ```ts import type { RouteDirectoryConfig } from 'mokup' const config: RouteDirectoryConfig = { enabled: true, headers: { 'x-mokup-scope': 'api' }, status: 200, delay: 120, ignorePrefix: ['.', '_'], include: /users/, exclude: /skip/, middleware: [ async (c, next) => { c.header('x-mokup', 'dir') return await next() }, ], } export default config ``` 提示:可以使用 `defineConfig` 获得更好的类型提示: ```ts import { defineConfig } from 'mokup' export default defineConfig({ enabled: true, headers: { 'x-mokup-scope': 'api' }, status: 200, delay: 120, ignorePrefix: ['.', '_'], include: /users/, exclude: /skip/, middleware: [ async (c, next) => { c.header('x-mokup', 'dir') return await next() }, ], }) ``` 配置会从根目录向下合并。headers 会合并,middleware 按顺序追加。 ## Service Worker 模式 将 `mode` 设置为 `'sw'` 后,mock 会在浏览器的 Service Worker 中运行。插件会在 dev/preview 提供 SW 脚本,并在 build 时输出到 `sw.path`(默认 `/mokup-sw.js`,scope 默认 `/`)。默认会自动注入注册脚本,若不需要可设置 `sw.register: false`。 使用场景: * 在浏览器端直接拦截请求,无需 dev 代理。 * 测试离线或缓存相关的请求行为。 ### 基本用法 示例: ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', }, }), ], } ``` ### 自定义 path/scope + 手动注册 示例(插件配置): ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', register: false, }, }, }), ], } ``` 使用场景: * 需要按条件或路由手动注册 SW。 * 多应用场景下自行控制注册逻辑。 示例(手动注册): ```ts import { registerMokupServiceWorker } from 'mokup/sw' registerMokupServiceWorker({ path: '/mokup-sw.js', scope: '/', }) ``` ### 混合模式 使用场景: * 部分路由走 SW,其他路由仍走 dev server。 示例: ```ts export default { plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api', mode: 'sw', sw: { fallback: false } }, { dir: 'mock-server', prefix: '/internal', mode: 'server' }, ], }), ], } ``` 注意: * `sw.fallback` 默认是 `true`。设为 `false` 表示不再注册 server 中间件。 * `sw.basePath` 用于控制 SW 只拦截哪些路径。如果未设置,会继承对应 entry 的 `prefix`;若 `prefix` 为空,则可能拦截所有路径。 * 多个 SW 配置同时存在时,首个 `sw.path`/`sw.scope`/`sw.register`/`sw.unregister` 生效,其它冲突配置会被忽略并提示告警。 ### 拦截范围 使用场景: * 只拦截指定路径,避免影响其它请求。 示例: ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { basePath: '/api', }, }, }), ], } ``` ### 卸载 使用场景: * 清理历史 SW 注册,避免 mock 残留。 * 仅发布卸载逻辑的构建产物。 示例(插件配置): ```ts export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { unregister: true, }, }, }), ], } ``` 使用场景: * 在客户端流程中手动卸载 SW。 示例(手动卸载): ```ts import { unregisterMokupServiceWorker } from 'mokup/sw' await unregisterMokupServiceWorker({ path: '/mokup-sw.js', scope: '/', }) ``` 注意: * `sw.unregister: true` 会注入卸载脚本并跳过注册。 * 当没有任何 SW entry 时,插件会自动注入卸载脚本,并使用 `sw.path`/`sw.scope`(或默认值)清理旧的注册。 ## 多目录 使用场景: * 多个 mock 目录同时工作并映射到不同前缀。 示例: ```ts export default { plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api' }, { dir: 'mock-extra', prefix: '/api-extra' }, ], }), ], } ``` --- --- url: 'https://mokup.icebreaker.top/zh/deploy/vite-build.md' --- # Vite 构建产物 当需要在构建期生成可部署的 mock 产物时,使用 CLI: ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: 输出目录结构: ``` .mokup/ mokup.manifest.json mokup.manifest.mjs mokup.manifest.d.mts mokup.bundle.mjs mokup.bundle.d.ts mokup.bundle.d.mts mokup-handlers/ (可选) ``` `mokup.bundle.mjs` 是最方便的入口文件,适合在 Worker 或自定义运行时中直接导入。 ## Service Worker 构建 当在 Vite 插件中设置 `mode: 'sw'` 时,Service Worker 脚本会在 `vite build` 期间输出(默认 `/mokup-sw.js`)。插件会自动注入注册脚本,除非设置 `sw.register: false`。 ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', }, }, }), ], } ``` 这种方式适合纯静态部署,因为 mock 请求在浏览器侧处理。如果你还需要 playground,可设置 `playground: { build: true }`,在 `vite build` 时输出 Playground 资源与 `/__mokup/routes`。或者继续用 `mokup build` 或脚本生成 `/__mokup/routes` 并随站点发布。 注意: * `sw.basePath` 用于控制 SW 拦截的请求路径,默认继承 entry 的 `prefix`。 --- --- url: 'https://mokup.icebreaker.top/reference/webpack-plugin.md' --- # Webpack Plugin Use `mokup/webpack` to integrate Mokup with webpack-dev-server and webpack builds. ## Install ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## Usage Use cases: * Add Mokup mocks to webpack-dev-server without changing app code. * Emit SW assets during webpack builds for browser-level mocking. Recommended (with wrapper): ```js const { mokupWebpack } = require('mokup/webpack') const withMokup = mokupWebpack({ entries: { dir: 'mock', prefix: '/api', }, }) module.exports = withMokup({}) ``` Direct plugin (short name): ```js const { createWebpackPlugin } = require('mokup/webpack') module.exports = { plugins: [ createWebpackPlugin({ entries: { dir: 'mock', prefix: '/api', }, }), ], devServer: {}, } ``` ## Service Worker mode Use cases: * Run mock handlers in a browser Service Worker during webpack builds. * Share the same mock directory between dev-server and SW. Demo: ```js const { createWebpackPlugin } = require('mokup/webpack') module.exports = { plugins: [ createWebpackPlugin({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', }, }, }), ], } ``` ## Options Options match the Vite plugin: top-level `entries` + `playground`, plus entry options for each item. See [Vite Plugin](./vite-plugin) for the full list. ## Notes * Dev server support uses `devServer.setupMiddlewares`; ensure `webpack-dev-server` is enabled. * `mokupWebpack(...)` auto-creates a `devServer` object, so you can omit it unless you need custom dev-server settings. * The SW lifecycle script is emitted under your assets directory (default `assets/mokup-sw-lifecycle.js`). With `html-webpack-plugin` it is auto-injected; otherwise include it manually. --- --- url: 'https://mokup.icebreaker.top/getting-started/quick-start/webpack.md' --- # Webpack Quick Start ## 1. Install ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 2. Add the plugin ```js const { mokupWebpack } = require('mokup/webpack') const withMokup = mokupWebpack({ entries: { dir: 'mock', prefix: '/api', }, }) module.exports = withMokup({}) ``` ## 3. Start dev server Run your webpack dev server: ::: code-group ```bash [pnpm] pnpm webpack serve ``` ```bash [npm] npm exec webpack serve ``` ```bash [yarn] yarn webpack serve ``` ```bash [bun] bunx webpack serve ``` ::: ## 4. Verify * `http://localhost:8080/api/users` * `http://localhost:8080/__mokup` ## Next See the full options in [Webpack Plugin](/reference/webpack-plugin). --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/quick-start/webpack.md' --- # Webpack 快速开始 ## 1. 安装 ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 2. 添加插件 ```js const { mokupWebpack } = require('mokup/webpack') const withMokup = mokupWebpack({ entries: { dir: 'mock', prefix: '/api', }, }) module.exports = withMokup({}) ``` ## 3. 启动开发服务 启动 webpack dev server: ::: code-group ```bash [pnpm] pnpm webpack serve ``` ```bash [npm] npm exec webpack serve ``` ```bash [yarn] yarn webpack serve ``` ```bash [bun] bunx webpack serve ``` ::: ## 4. 验证 * `http://localhost:8080/api/users` * `http://localhost:8080/__mokup` ## 下一步 完整选项请参考 [Webpack 插件](/zh/reference/webpack-plugin)。 --- --- url: 'https://mokup.icebreaker.top/zh/reference/webpack-plugin.md' --- # Webpack 插件 使用 `mokup/webpack` 在 webpack-dev-server 与构建产物中接入 Mokup。 ## 安装 ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: ## 基本用法 使用场景: * 在 webpack-dev-server 中接入 Mokup mock。 * 构建时输出 SW 资源用于浏览器侧 mock。 推荐(使用封装函数): ```js const { mokupWebpack } = require('mokup/webpack') const withMokup = mokupWebpack({ entries: { dir: 'mock', prefix: '/api', }, }) module.exports = withMokup({}) ``` 直接使用插件(短名称): ```js const { createWebpackPlugin } = require('mokup/webpack') module.exports = { plugins: [ createWebpackPlugin({ entries: { dir: 'mock', prefix: '/api', }, }), ], devServer: {}, } ``` ## Service Worker 模式 使用场景: * 在浏览器 Service Worker 中运行 mock 处理器。 * dev 与 build 共用同一套 mock 目录。 示例: ```js const { createWebpackPlugin } = require('mokup/webpack') module.exports = { plugins: [ createWebpackPlugin({ entries: { dir: 'mock', prefix: '/api', mode: 'sw', sw: { path: '/mokup-sw.js', scope: '/', }, }, }), ], } ``` ## 选项 选项与 Vite 插件一致:顶层 `entries` + `playground`,以及每个 entry 的配置,详见 [Vite 插件](./vite-plugin)。 ## 注意 * Dev server 会通过 `devServer.setupMiddlewares` 注入中间件,请确保启用了 `webpack-dev-server`。 * `mokupWebpack(...)` 会自动创建 `devServer` 对象,除非需要自定义 dev-server 设置,否则可以省略。 * SW 生命周期脚本会输出到 assets 目录(默认 `assets/mokup-sw-lifecycle.js`)。如果使用 `html-webpack-plugin` 会自动注入,否则需要手动引入。 --- --- url: 'https://mokup.icebreaker.top/zh/advanced/middleware-ordering.md' --- # 中间件顺序与 defineConfig 目录配置可以通过 `defineConfig` 注册中间件。中间件阶段为 `pre`、`normal`、`post`,分别对应 `onBeforeAll`、`app.use`、`onAfterAll`。 ## index.config.ts 使用 defineConfig ```ts import type { MiddlewareHandler } from 'mokup' import { defineConfig, onAfterAll, onBeforeAll } from 'mokup' const requireAuth: MiddlewareHandler = async (c, next) => { const header = c.req.header('authorization') ?? '' if (!header.startsWith('Bearer ')) { c.status(401) return c.json({ ok: false, error: 'missing_auth' }) } await next() } export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(requireAuth) }) app.use(async (c, next) => { c.header('x-mokup-normal', '1') await next() }) onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-mokup-post', '1') }) }) return { headers: { 'x-mokup-example': 'ordering' }, } }) ``` ## defineHandler 用于路由文件 `defineHandler` 可以在 JS/TS 中提供类型提示,无需额外 JSDoc。 ```ts import { defineHandler } from 'mokup' export default defineHandler((c) => { return { ok: true, method: c.req.method } }) ``` ```ts import { defineHandler } from 'mokup' export default defineHandler({ enabled: false, handler: async (c) => { return { ok: false, reason: 'disabled-rule', method: c.req.method } }, }) ``` ## 顺序规则 * 目录链条按从根目录到子目录依次生效。 * 单个路由的阶段顺序:`pre` -> `normal` -> `post` -> handler。 * 使用 `await next()` 可以在后续中间件和 handler 执行后继续处理。 ## 目录嵌套示例 ``` mock/ index.config.ts nested/ index.config.ts info.get.ts ``` ```ts // mock/index.config.ts import { defineConfig, onBeforeAll } from 'mokup' export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(async (c, next) => { c.header('x-root-pre', '1') await next() }) }) }) ``` ```ts // mock/nested/index.config.ts import { defineConfig, onAfterAll } from 'mokup' export default defineConfig(({ app }) => { onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-nested-post', '1') }) }) }) ``` ## object 配置与旧字段 * `onBeforeAll/app.use/onAfterAll` 只能在 `defineConfig` 内使用。 * 直接导出 object 仅支持 `headers`、`status`、`delay`、`include`、`exclude` 等目录字段。 * `middleware` 是旧字段,行为等同于 `normal` 阶段。 ## 示例目录(apps/mokup-docs/mock) * `apps/mokup-docs/mock/example-basic`: 最小化 `onBeforeAll/app.use/onAfterAll` 顺序。 * `apps/mokup-docs/mock/example-order`: 父目录 + 子目录的顺序链。 * `apps/mokup-docs/mock/example-auth`: `pre` 鉴权与 `post` 响应头。 * `apps/mokup-docs/mock/example-metrics`: 请求耗时与 request id 响应头。 * `apps/mokup-docs/mock/example-headers`: 不同阶段的响应头覆盖。 * `apps/mokup-docs/mock/example-errors`: `post` 中处理异常。 * `apps/mokup-docs/mock/example-delay-status`: 延迟与状态码。 --- --- url: 'https://mokup.icebreaker.top/zh/core-concepts/handlers.md' --- # 函数处理器 当 `handler` 是函数时,Mokup 会把它视为可执行的处理器: ```ts export default { handler: async (c) => { c.status(200) c.header('x-mokup', 'handler') await new Promise(resolve => setTimeout(resolve, 120)) return c.json({ ok: true, params: c.req.param() }) }, } ``` 提示:可以使用 `defineHandler` 包裹路由导出以获得更好的类型提示: ```ts import { defineHandler } from 'mokup' export default defineHandler({ handler: async (c) => { c.status(200) c.header('x-mokup', 'handler') await new Promise(resolve => setTimeout(resolve, 120)) return c.json({ ok: true, params: c.req.param() }) }, }) ``` 或者使用函数写法: ```ts import { defineHandler } from 'mokup' export default defineHandler((c) => { c.header('x-mokup', 'handler') return c.json({ ok: true, params: c.req.param() }) }) ``` 函数签名: * `c`: Hono `Context`(`c.req.param()`、`c.req.query()`、`c.req.json()`、`c.status()`、`c.header()`) 在 CLI 构建时,函数处理器会被打包到 `.mokup/mokup-handlers`,并在 manifest 中以 `module` 形式引用。 --- --- url: 'https://mokup.icebreaker.top/zh/advanced/multi-dir-prefix.md' --- # 多目录与前缀 Vite 插件支持多个目录与前缀组合,适合拆分多套 mock: ```ts import mokup from 'mokup/vite' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ mokup({ entries: [ { dir: 'mock', prefix: '/api' }, { dir: 'mock-extra', prefix: '/api-extra' }, { dir: 'mock-ignored', prefix: '/api-ignored', watch: false }, ], }), ], }) ``` 说明: * `dir` 可为单个字符串或数组。 * `prefix` 会自动规范化(确保前导 `/`)。 * 多个目录会被合并到同一个路由表里。 CLI 构建时建议按目录分别生成 `.mokup`,再在运行时自行组合。 --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/installation.md' --- # 安装 ## 前置要求 * Node.js 20+ * pnpm(推荐) ## 安装依赖 开发工具(Vite 插件 + CLI): ::: code-group ```bash [pnpm] pnpm add -D mokup ``` ```bash [npm] npm install -D mokup ``` ```bash [yarn] yarn add -D mokup ``` ```bash [bun] bun add -d mokup ``` ::: 运行时/服务端适配器(部署或中间件): ::: code-group ```bash [pnpm] pnpm add mokup ``` ```bash [npm] npm install mokup ``` ```bash [yarn] yarn add mokup ``` ```bash [bun] bun add mokup ``` ::: 如果你只做本地 Vite 开发,把 `mokup` 装在 devDependencies 即可。 --- --- url: 'https://mokup.icebreaker.top/zh/reference/faq.md' --- # 常见问题 ## 为什么 JSON Mock 需要 method 后缀? CLI 构建使用文件名判断方法,推荐显式使用 `.get/.post` 等后缀。Vite 插件对 `.json/.jsonc` 会默认 `GET`,但仍建议显式声明以保持一致性。 ## Worker 下出现 node 内置模块警告 请确保使用 `mokup/server/worker` 或 `mokup/server/fetch`,避免把 Node 专用依赖打包进 Worker。 ## Playground 里看不到数据 确认: * Vite 插件已启用。 * 访问路径为 `/__mokup`(或你自定义的路径)。 * mock 文件命名正确且有 method 后缀。 ## JSON 能不能写注释? 可以,`.jsonc` 支持注释与尾逗号。 --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/quick-start.md' --- # 快速开始 选择适合你的启动方式,所有流程共用同一套 mock 文件。 ## 1. 创建 mock 文件 在项目根目录创建 `mock/users.get.json`: ```json { "id": 1, "name": "Ada" } ``` ## 2. 选择路径 * [Vite 插件](./vite) — 适合 Vite 开发与预览。 * [Webpack 插件](./webpack) — 集成 webpack-dev-server。 * [Hono 适配器](/zh/reference/server#hono) — 在 Hono 运行时中使用。 * [Cloudflare Worker](/zh/deploy/cloudflare-worker) — 使用 Worker 入口部署。 * [Node.js API](../node-api) — 在 Node 中直接调用 runtime。 * [服务端中间件](../server-middleware) — 接入已有服务端。 Playground 默认在 Vite 开发中启用,路径为 `/__mokup`。 --- --- url: 'https://mokup.icebreaker.top/zh/core-concepts/file-routing.md' --- # 文件路由 Mokup 通过文件路径与文件名生成路由,核心规则如下: ## Method 后缀 文件名必须包含 HTTP 方法后缀(`.get`、`.post` 等): ``` mock/users.get.json -> GET /users mock/users.post.ts -> POST /users ``` 在 Vite 插件中,`.json/.jsonc` 如果没有方法后缀,会默认视为 `GET`;在 CLI 构建时建议显式写方法后缀,避免歧义。 ## 常见 REST 方法速览 常用 RESTful 路由与文件名的对应关系: ``` mock/items/index.get.ts -> GET /items mock/items/index.post.ts -> POST /items mock/items/[id].get.ts -> GET /items/:id mock/items/[id].put.ts -> PUT /items/:id mock/items/[id].patch.ts -> PATCH /items/:id mock/items/[id].delete.ts -> DELETE /items/:id ``` ## index 路由 `index` 会被视为目录根路径: ``` mock/index.get.json -> GET / mock/users/index.get.ts -> GET /users ``` ## 动态参数 使用方括号定义参数: ``` mock/users/[id].get.ts -> GET /users/:id mock/[action]/[id].get.ts -> GET /:action/:id ``` 在处理函数中可以通过 `c.req.param('id')` 访问: ```ts export default { handler: c => ({ id: c.req.param('id') }), } ``` 提示:可以使用 `defineHandler` 包裹路由导出以获得更好的类型提示: ```ts import { defineHandler } from 'mokup' export default defineHandler({ handler: c => ({ id: c.req.param('id') }), }) ``` 也可以一次性返回所有参数: ```ts import { defineHandler } from 'mokup' export default defineHandler(c => ({ params: c.req.param(), })) ``` ## Catch-all 与可选段 ``` mock/docs/[...slug].get.ts -> /docs/* (至少 1 段) mock/docs/[[...slug]].get.ts -> /docs (可选) ``` 这些规则与前端路由常见语法一致,适合做文档类 API 模拟。 ## JSON 与 JSONC 路由 JSON 文件可以作为静态处理器: ``` mock/status.get.json -> GET /status ``` JSONC 支持注释与尾随逗号: ``` mock/summary.get.jsonc -> GET /summary ``` 动态参数同样适用于 JSON 路由: ``` mock/[action]/[id].get.json -> GET /:action/:id ``` ## 多规则文件 一个文件可以导出规则数组。禁用的规则会被跳过,启用的规则会生效: ```ts import type { RouteRule } from 'mokup' const rules: RouteRule[] = [ { enabled: false, handler: () => ({ variant: 'disabled' }) }, { handler: () => ({ variant: 'active' }) }, ] export default rules ``` ## 禁用路由 通过 `enabled: false` 直接禁用路由: ```ts import { defineHandler } from 'mokup' export default defineHandler({ enabled: false, handler: () => ({ ok: false }), }) ``` ## 目录配置(defineConfig) 在目录内放置 `index.config.ts`,可以配置延迟、响应头和中间件: ```ts import { defineConfig, onAfterAll, onBeforeAll } from 'mokup' export default defineConfig(({ app }) => { onBeforeAll(() => { app.use(async (c, next) => { c.header('x-mokup-pre', '1') await next() }) }) onAfterAll(() => { app.use(async (c, next) => { await next() c.header('x-mokup-post', '1') }) }) return { delay: 20, headers: { 'x-mokup-demo': 'file-routing' }, } }) ``` ## 忽略文件 不支持的扩展名(例如 `.txt`)会被忽略。默认会忽略以 `.` 开头的路径段: ``` mock/notes.txt -> 忽略 mock/.draft/notes.get.ts -> 默认忽略(.) mock/.ignored/skip.get.ts -> 默认忽略(.) ``` 你可以通过 `ignorePrefix`(字符串或数组)覆盖忽略列表。设置后会替换默认值,如果仍希望忽略以 `.` 开头的路径段,请保留 `.`: ```ts // mock/index.config.ts export default { ignorePrefix: ['.', 'draft-'], } ``` ``` mock/draft-legacy/skip.get.ts -> 当 ignorePrefix 包含 "draft-" 时忽略 ``` --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/server-middleware.md' --- # 服务端中间件 如果你已有服务端应用,可以直接注入 mokup 中间件。 如果只是需要一个带热更新的开发服务器,直接用 `createFetchServer`。 ## 开发服务器(推荐) ```ts import { createFetchServer, serve } from 'mokup/server/node' const app = await createFetchServer({ entries: { dir: 'mock' } }) serve({ fetch: app.fetch, port: 3000 }) ``` ## 中间件集成(Build 模式) 如果你需要稳定的生产构建,请使用 build 模式。 ::: code-group ```bash [pnpm] pnpm exec mokup build --dir mock --out .mokup ``` ```bash [npm] npm exec mokup build --dir mock --out .mokup ``` ```bash [yarn] yarn mokup build --dir mock --out .mokup ``` ```bash [bun] bunx mokup build --dir mock --out .mokup ``` ::: ```ts import mokupBundle from './.mokup/mokup.bundle.mjs' const options = { manifest: mokupBundle.manifest, moduleMap: mokupBundle.moduleMap, moduleBase: mokupBundle.moduleBase, } ``` ## 注册中间件 ### Express ```ts import express from 'express' import { createExpressMiddleware } from 'mokup/server/node' const app = express() app.use(createExpressMiddleware(options)) ``` ### Koa ```ts import Koa from 'koa' import { createKoaMiddleware } from 'mokup/server/node' const app = new Koa() app.use(createKoaMiddleware(options)) ``` ### Fastify ```ts import Fastify from 'fastify' import { createFastifyPlugin } from 'mokup/server/node' const app = Fastify() await app.register(createFastifyPlugin(options)) ``` ### Hono ```ts import { Hono } from 'hono' import { createHonoMiddleware } from 'mokup/server/node' const app = new Hono() app.use(createHonoMiddleware(options)) ``` 更多适配与参数见 [Server 适配器](/zh/reference/server)。 --- --- url: 'https://mokup.icebreaker.top/zh/advanced/hot-reload.md' --- # 热更新与调试 Mokup 在 Vite dev 中会监听 mock 目录的文件变化,并自动刷新路由表。 ## 开启/关闭监听 ```ts import mokup from 'mokup/vite' export default { plugins: [ mokup({ entries: { dir: 'mock', watch: true, }, }), ], } ``` 若不需要监听(例如预览环境),可设 `watch: false`。 ## 调试建议 * 路由变化后 Playground 会自动刷新(`mokup:routes-changed`)。 * 若某个接口不生效,请先检查文件名是否包含 method 后缀。 * TS 处理器支持 `console.log` 输出,Vite dev 会显示日志。 ## 调试 mock handler 与中间件 mock handler 和目录中间件运行在 Vite 的 Node 侧,请使用 Node 调试器而不是浏览器 DevTools。 ### VSCode(推荐) 1. 打开命令面板执行 **Debug: Create JavaScript Debug Terminal**。 2. 在该终端里启动 dev 命令(例如 `pnpm dev --filter `)。 3. 在 `mock/**/*.ts` 或 `mock/**/index.config.ts` 里打断点。 如需 `launch.json`: ```json { "type": "node", "request": "launch", "name": "Vite Dev (mock debug)", "runtimeExecutable": "pnpm", "runtimeArgs": ["dev", "--filter", ""], "cwd": "${workspaceFolder}", "env": { "NODE_OPTIONS": "--enable-source-maps --inspect" }, "autoAttachChildProcesses": true } ``` ### 终端 + Node Inspector ```bash NODE_OPTIONS="--inspect-brk --enable-source-maps" pnpm dev --filter ``` 然后在 VSCode 使用 “Attach to Node”,或打开 `chrome://inspect` 连接。 ### 快速确认 * 在 handler 内加入 `debugger;` 或 `console.log`,确认是否被加载。 * 预览环境可能无法使用 Vite dev 调试,建议用 `dev` 命令。 --- --- url: 'https://mokup.icebreaker.top/zh/platform/cross-runtime-js.md' --- # 跨运行时 JavaScript 指南 用一份代码同时运行在 Service Worker、Cloudflare Worker 与 Node.js 的关键是: 核心逻辑只依赖 Web 标准 API,把运行时差异放在很薄的适配层里。 ## 目标与边界 目标是构建一个通用的请求处理器: * 使用 `Request`/`Response`/`URL`/`Headers` * 不直接访问文件系统 * 环境变量可选(有就用,没有也能跑) * 运行时特性集中在适配层 本指南不绑定任何框架,只提供可移植的最小模板。 ## 运行时差异速览 Service Worker: * 无文件系统 * 通过 `fetch` 事件驱动 * 浏览器内可用 `caches` Cloudflare Worker: * 无文件系统 * 支持模块或 Service Worker 语法 * 绑定通过 `env` 注入 Node.js(>=18): * 支持 `fetch`/`Request`/`Response` * 有文件系统,但跨平台逻辑中尽量避免 * 环境变量通过 `process.env` ## 兼容性清单 * 使用 `globalThis` 与 Web API(`fetch`、`Request`、`Response`、`Headers`、`URL`)。 * 避免在共享逻辑里使用 `Buffer`/`fs`,用 `ArrayBuffer`/`Uint8Array` 替代。 * 明确处理 JSON/文本(`response.json()`、`response.text()`)。 * `env` 作为可选输入,做能力检测。 * 不依赖 `window`/`document`/DOM。 * 避免 Node 专属全局(`__dirname`、`process`),必要时做 guard。 * 模块使用 ESM,兼容 Worker 运行时。 ## 最小跨平台处理器 ```ts export type RuntimeEnv = Record export async function handleRequest( request: Request, env?: RuntimeEnv, ): Promise { const url = new URL(request.url) if (url.pathname === '/health') { return new Response('ok') } const apiBase = env?.API_BASE ?? 'https://example.com' const upstream = new URL(url.pathname, apiBase) const response = await fetch(upstream.toString(), { method: request.method, headers: request.headers, body: request.method === 'GET' ? undefined : request.body, }) return response } ``` ## Service Worker 适配层 ```ts import { handleRequest } from './shared' globalThis.addEventListener('fetch', (event) => { event.respondWith(handleRequest(event.request)) }) ``` ## Cloudflare Worker 适配层 ```ts import { handleRequest } from './shared' export default { fetch(request: Request, env: Record) { return handleRequest(request, env) }, } ``` ## Node.js 适配层(>=18) ```ts import { createServer } from 'node:http' import { handleRequest } from './shared' const server = createServer(async (req, res) => { const request = new Request(`http://localhost${req.url}`, { method: req.method, headers: req.headers as Record, }) const response = await handleRequest(request, process.env) res.writeHead(response.status, Object.fromEntries(response.headers)) res.end(await response.text()) }) server.listen(3000) ``` ## 常见坑 * 直接把 Node 的 `IncomingMessage` 当成 `Request` 使用。 * 在 Worker 里读取 `process.env`。 * 共享逻辑中使用 `Buffer`。 * 依赖 `window` 或 DOM API 处理 URL/存储。 --- --- url: 'https://mokup.icebreaker.top/zh/getting-started/overview.md' --- # 项目概览 Mokup 是一套围绕“文件即路由”的 HTTP 框架,覆盖开发与部署两个阶段: * **开发期**:通过 `mokup/vite` 插件,在 Vite dev 里直接拦截请求并运行路由 handler。 * **构建期**:通过 CLI 生成 `.mokup` 产物(manifest + handlers),供 Worker 或其他运行时使用。 * **运行时**:`mokup/runtime` 统一处理匹配、响应、延迟与 headers。 * **适配器**:`mokup/server/node` 提供 Node 适配器;`mokup/server/fetch` 运行时无关;`mokup/server/worker` 面向 Worker。 适合场景: * 需要文件式 HTTP 路由的内部工具或生产 API。 * 希望快速验证接口并保留 mock 能力。 * 希望统一一套规则,在多种运行时中复用。 接下来请查看安装与快速开始。