---
title: "@ox-content/code-play"
description: オンデマンドのドキュメントサンプル実行向け、オプトイン API と UI です。
---

# @ox-content/code-play

Code Play は、ドキュメントのサンプルをオンデマンドで実行します。別プラグインです。
`@ox-content/vite-plugin` は有効にせず、このパッケージを入れただけでは何も起きません。
言語を列挙するまで動きません。

このサイトの [ドキュメント例](/examples/code-play.md) とスタンドアロンの
[`examples/code-play`](https://github.com/ubugeeei-prod/ox-content/tree/main/examples/code-play)
アプリは、JavaScript、TypeScript、Rust、Go を有効にしています。Rust、Go、
リモート言語はオプトインするまでオフです。

## インストール

<pm>npm install @ox-content/code-play@beta</pm>

```ts
import { oxContent } from "@ox-content/vite-plugin";
import { codePlay } from "@ox-content/code-play";

export default {
  plugins: [
    oxContent({ highlight: true }),
    codePlay({
      languages: {
        typescript: { execute: true, typecheck: true },
        javascript: true,
        rust: true,
        go: true,
      },
      ui: "default",
      viewers: { config: true, stdio: true, stderr: true, provenance: true, timing: true },
      srcDir: "content",
    }),
  ],
};
```

このプラグインは、パッケージインストールの上に乗る二段目のオプトインです。
`play` のないフェンス、または一覧にない言語は、普通のハイライト付きブロックのままです。
Code Play ブロックがないページには hydrate スクリプトは入りません。
ビルド全体で Code Play を使わない場合は `ox-code-play.js` も出力されません。

## プラグインオプション

| オプション  | 型                                              | 既定       | 役割                                           |
| ----------- | ----------------------------------------------- | ---------- | ---------------------------------------------- |
| `languages` | `Record<string, true \| LanguageEnableOptions>` | `{}`       | execute / typecheck / `endpoint` を有効化      |
| `ui`        | `"default" \| "compact" \| "headless"`          | `default`  | サンプル周りのクロム                           |
| `viewers`   | `Partial<ViewerFlags>`                          | すべてオン | stdio / stderr / config / … の表示             |
| `timeoutMs` | `number`                                        | `10000`    | 実行ごとのタイムアウト                         |
| `endpoints` | `{ rust?, go?, typecheck? }`                    | official   | プレイグラウンド / typecheck の URL            |
| `proxy`     | `boolean`                                       | `true`     | Vite **dev** の `/__ox-code-play/*` をマウント |
| `srcDir`    | `string`                                        | `"docs"`   | play フェンス照合に使う Markdown ルート        |
| `outDir`    | `string`                                        | Vite out   | SSG 後に拡張する書き出し HTML                  |
| `base`      | `string`                                        | `"/"`      | `ox-code-play.js` の公開パス                   |

`LanguageEnableOptions` は、その言語のスキーマ向けに `execute`、`typecheck`、`endpoint`、
`config` の上書きを受け付けます（TypeScript の `strict`、Rust の
`crateType`、Go の `withVet`、…）。

## 執筆

フェンスに `play` を付けます。言語が対応しているときは `typecheck` も足せます。

````md
```ts play typecheck play-title="Strict TypeScript" play-strict=false play-target=ESNext
const n: number = 1;
console.log(n);
```

```rust play typecheck play-title="Release-mode Rust" play-mode=release
fn main() {
    println!("ok");
}
```

```go play typecheck play-title="Go vet on"
package main

import "fmt"

func main() {
    fmt.Println("ok")
}
```
````

`play-title` はウィジェットのラベルです。`play-compact` / `play-headless` は
そのサンプルだけ UI プリセットを変え、`play-timeout=2500` はタイムアウトを変えます。
`play-viewers=stdio,stderr,-timing` でビューアーを切り替えられます。
`play-<config-key>=...` は言語ごとの config 値なので、TypeScript は
`play-strict=false`、Rust は `play-edition=2021`、Go は `play-withVet=false`
のように書けます。

HTML / MDX 形式:

```html
<CodePlay lang="ts" title="Loose TS" typecheck ui="compact" config-strict="false">
  const n = 1;
</CodePlay>
```

プロジェクト単位の例は、サンプルごとに `play-project` または `project` で明示します。
現在のフェンスは主たる実行スニペットのままにし、project metadata としてファイル名、
provider、外部 fallback link を追加します。

````md
```ts play play-project=stackblitz play-file=src/main.ts play-entry=src/main.ts play-files=package.json,src/App.tsx play-project-url=https://stackblitz.com/edit/example
console.log("project");
```
````

`play-file` は現在のフェンスをプロジェクト内のどのファイルとして扱うかを指定します。
`play-files` は追加ファイルのカンマ区切りリストで、Markdown source file からの相対パスとして
解決され、`srcDir` の内側に制限されます。provider metadata adapter は
`stackblitz`、`codesandbox`、`webcontainer`、`external` に対応しています。
Code Play は provider script を読み込みません。安全な `http(s)` URL があるとき、
生成された widget は project metadata と **Open** fallback link を描画します。

## Headless API

```ts
import { createCodePlay } from "@ox-content/code-play";

const play = createCodePlay({ languages: { typescript: true } });
const session = play.createSession({
  language: "ts",
  code: "const n: number = 1;\nconsole.log(n);",
});

const check = await session.typecheck();
const run = await session.run();

run.stdio; // timestamped stdin / stdout / stderr events
run.stdout; // concatenated stdout text
run.stderr; // concatenated stderr text
run.provenance; // where it compiled, where it ran
run.timing; // phase durations and totalMs
session.config; // editable language config
```

有効にしていない言語を求めると `createCodePlay()` は throw します。
`session.setConfig({ strict: false })` は、config ビューアーが編集するのと同じオブジェクトを更新します。
`session.cancel()` は進行中の run または typecheck を中止し、
`status: "cancelled"` を返します。既定のツールバーは、実行中に **Cancel** を出します。
テストでは `transport`（たとえば `createMemoryTransport`）を注入し、
CI がライブのプレイグラウンドに触れないようにします。

| フィールド        | 意味                                                                 |
| ----------------- | -------------------------------------------------------------------- |
| `run.status`      | `ok` / `error` / `offline` / `timeout` / `cancelled` / `unsupported` |
| `run.stdio`       | タイムスタンプ付きの `stdin` / `stdout` / `stderr` イベント          |
| `run.stdout`      | 連結した stdout テキスト                                             |
| `run.stderr`      | 連結した stderr テキスト                                             |
| `run.diagnostics` | 任意の行 / 列付きのコンパイラ / ランタイムメッセージ                 |
| `run.provenance`  | どこでコンパイルし、どこで実行したか                                 |
| `run.timing`      | フェーズ時間と `totalMs`                                             |
| `run.preview`     | バックエンドが UI のときのフレームワーク iframe `srcdoc`             |
| `session.stdout`  | `lastResult.stdout` と同じ                                           |
| `session.stderr`  | `lastResult.stderr` と同じ                                           |

独自 UI では、export されている `RunActionState` ヘルパー
`idleRunActionState()`、`runningRunActionState(action)`、
`resultRunActionState(action, result)` を使えます。transport や CORS の失敗は
`status: "offline"` になり、コンパイル / ランタイムエラーとは別に扱えます。

## UI

| プリセット | 振る舞い                                                        |
| ---------- | --------------------------------------------------------------- |
| `default`  | ツールバーと stdio / stderr / config / provenance / timing タブ |
| `compact`  | Run / type-check と stdio、stderr                               |
| `headless` | DOM クロムなし。セッション API を使う                           |

ビューアーは `viewers` で個別に切り替えられます。hydrate 後のウィジェットは
polite なステータス領域、`aria-busy`、tab panel、矢印キーによるタブ移動を提供します。

## 言語

| 言語                      | 実行 | 型チェック | バックエンド                                    |
| ------------------------- | ---- | ---------- | ----------------------------------------------- |
| TypeScript                | yes  | yes        | ローカル strip-types + `tsgo` + `node:vm`       |
| Rust                      | yes  | yes        | `play.rust-lang.org`（または `endpoints.rust`） |
| Go                        | yes  | yes        | `play.golang.org`（または `endpoints.go`）      |
| JavaScript                | yes  | no         | `node:vm` / サンドボックス iframe               |
| Vue、React、Svelte、Solid | yes  | no         | iframe `srcdoc` + esm.sh import map             |
| Python、PHP、Ruby、sh、…  | yes  | no         | Piston 互換の `languages.<id>.endpoint`         |

完全なカタログは [ロードマップ](/code-play-roadmap.md) と同じ一覧です。
`ts`、`c++`、`bash`、`coq` のようなエイリアスは正規 id に解決されます。

## プレイグラウンドプロキシ

Vite の **dev サーバー** のみです。`codePlay({ proxy: true })`（既定）は次をマウントします。

| パス                             | 転送先                                                        |
| -------------------------------- | ------------------------------------------------------------- |
| `POST /__ox-code-play/rust`      | `endpoints.rust`（既定 `https://play.rust-lang.org/execute`） |
| `POST /__ox-code-play/go`        | `endpoints.go`（既定 `https://play.golang.org/compile`）      |
| `POST /__ox-code-play/typecheck` | ローカル `tsgo`（リモートコンパイラなし）                     |

これらのルートは **POST** のみを受け付け、本文を 256 KiB で上限し、
`http(s)` 以外の宛先や埋め込み認証情報付き URL を拒否します。上流の
失敗は汎用 JSON `{ "error": "..." }` を返し、fetch の詳細は漏らしません。

プロキシは本番の SSG 出力には入りません。公開ページでは `endpoints` を公式
プレイグラウンド（または自分の HTTPS 実行器）に向けるか、
dev ミドルウェアが不要なら `proxy: false` にしてください。

静的ホストは `POST /__ox-code-play/typecheck` を提供しません。TypeScript の
**Run** はブラウザ内で動きます（型を剥がしてからサンドボックス iframe）。
到達可能な `endpoints.typecheck` を設定しない限り、公開ウィジェットから
**Typecheck** ボタンは省かれます。Vite プロキシ経路は `vite dev` のあいだだけ使います。

公開ページ上の Rust と Go は、ブラウザから直接 `endpoints.rust` / `endpoints.go`
を呼びます。公式プレイグラウンドが既定です。より厳密な分離、監査、または上流の
ブラウザポリシー変更への fallback が必要な場合は、`endpoints` を自分で制御する
実行器へ向けてください。

## セキュリティ

`play` フェンスは、出荷する他のスクリプトと同じ **信頼できるサイトコンテンツ** です。
訪問者が書いたものや未レビューの断片に `play` を付けないでください。

- サンプルは Markdown transform や SSG のあいだには実行されません。
- **JavaScript / TypeScript の実行** は Node では `node:vm`、ブラウザでは
  `<iframe sandbox="allow-scripts">`（`allow-same-origin` なし）です。
  ページ起源の `Function` では決して動きません。サンプルはホストページの DOM や
  ストレージを読めません。
- **Vue / React / Svelte / Solid** プレビューは同じ iframe フラグと
  `srcdoc` を使います。プレビューランタイムは `esm.sh` から読みます。
- `sh` は docs ホスト上でローカルシェルを起動しません。
- **Rust / Go** はソースを `play.rust-lang.org` / `play.golang.org`
  （または `endpoints` の上書き）へ POST します。それらのホストはサンプルを見ます。
  プライバシーポリシーが適用されます。
- Piston 互換の `languages.<id>.endpoint` はその言語のソースを受け取ります。
  信頼できる HTTPS エンドポイントだけを、埋め込み認証情報なしで設定してください。
- Project sandbox payload は、現在のフェンスと `play-files` の信頼済み source を埋め込みます。
  追加ファイルは Markdown source root 配下の相対パスだけを受け付けます。symlink の実パスも
  埋め込み前に検査され、存在しないファイルや大きすぎるファイルは widget warning になります。
  provider URL は認証情報なしの `http(s)` に制限されます。

## 初回公開

`@ox-content/code-play` は npm では新しいです。Trusted publishing はパッケージを
作れないので、メンテナーがノート PC から **一度** 公開し、そのあと
`npm trust` で trusted publisher を登録します。コマンドは
[リリース作業](/release.md#first-time-npm-publishing) にあります。

後続 PR は [Code Play ロードマップ](/code-play-roadmap.md) を見てください。
