ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
Loading the full README. The saved excerpt is shown below.
技術書を読んでいて見つけた誤りや「こう書いた方が読みやすい」という気づきを、読者が投稿して共有できる公開サイトです。 投稿には誤りの箇所(ページ・行や版・刷)を添えられ、出版社からの回答とステータスは管理者が記録して公開します。 ℹ️ 掲載されている投稿は投稿者からの報告であり、出版社の回答がつくまでは未確認の情報です。各投稿に表示される出版社の回答・ステータスとあわせてご確認ください。 ※ 数値は 2026-08 時点。 技術書を読んでいると、公式の正誤表にも載っていない誤りに出くわすことがあります。そうした「誤りを見つけ、指摘した」という記録を、技術者自身のポートフォリオとして残せる場が欲しいというのが動機の一つです。 また、投稿しやすいフォームを用意することで、他の技術者の方も気軽に指摘でき、正誤情報がより多くの人に届く——そんな場になればと考えて作りました。 技術的にも、不特定多数が投稿する公開 UGC(ユーザー生成コンテンツ)を題材に、認証・認可・データ整合性・セキュリティといった「実運用で効いてくる設計」を自分の手で一通り実装してみたかった、という狙いもありました。Read the full README ↗
技術書を読んでいて見つけた誤りや「こう書いた方が読みやすい」という気づきを、読者が投稿して共有できる公開サイトです。
投稿には誤りの箇所(ページ・行や版・刷)を添えられ、出版社からの回答とステータスは管理者が記録して公開します。
ℹ️ 掲載されている投稿は投稿者からの報告であり、出版社の回答がつくまでは未確認の情報です。各投稿に表示される出版社の回答・ステータスとあわせてご確認ください。
| 開発体制 | 個人開発(要件・設計・実装・運用・法務文書まで) |
| 期間 | 2026-06 〜 継続中(本番公開・検索インデックス済み) |
| 規模 | コミット 597 / マージ済み PR 221 |
| テスト | 単体 376 件(Vitest)/ e2e 212 件(Playwright) |
| 運用 | Vercel 本番稼働 / GitHub Actions で CI / Dependabot / CodeQL |
※ 数値は 2026-08 時点。
技術書を読んでいると、公式の正誤表にも載っていない誤りに出くわすことがあります。そうした「誤りを見つけ、指摘した」という記録を、技術者自身のポートフォリオとして残せる場が欲しいというのが動機の一つです。 また、投稿しやすいフォームを用意することで、他の技術者の方も気軽に指摘でき、正誤情報がより多くの人に届く——そんな場になればと考えて作りました。
技術的にも、不特定多数が投稿する公開 UGC(ユーザー生成コンテンツ)を題材に、認証・認可・データ整合性・セキュリティといった「実運用で効いてくる設計」を自分の手で一通り実装してみたかった、という狙いもありました。
| 領域 | 採用技術 |
|---|---|
| フレームワーク | Next.js 16(App Router)/ React 19 / TypeScript |
| スタイリング | Tailwind CSS v4 / 自作 UI コンポーネント(lucide-react アイコン) |
| 認証 | Supabase Auth(メール確認・PKCE code フロー) |
| データベース | PostgreSQL(Supabase ホスティング) |
| ORM | Prisma v7(@prisma/adapter-pg) |
| バリデーション | Zod |
| テーブル UI | TanStack Table |
| 外部 API | OpenBD(日本語書誌・ISBN)/ Google Books(タイトル検索・書影) |
| テスト | Vitest(単体)/ Testing Library / Playwright(e2e) |
| CI | GitHub Actions(lint + typecheck + test + build)/ CodeQL / Dependabot |
| デプロイ | Vercel |
「不特定多数が投稿する公開 UGC」を題材にしたので、判断の多くはきれいな正解が無く、何かを差し出すものでした。代表的なものを、捨てた側も含めて挙げます。
strict-dynamic にし、代償として全ページ動的レンダリングを受け入れたscript-src に 'unsafe-inline' を置かないので、注入されたインラインスクリプトは実行されません。
捨てたもの: 静的生成と CDN キャッシュ。 静的に生成した HTML にはリクエストごとの nonce を差し込めず、strict-dynamic 下ではスクリプトが全部ブロックされるため、全ルートが動的になります。この変更で静的でなくなったのは 13 ルート(/terms・/how-to-use など文章だけの低トラフィックページ)で、主要ページは DB を読むので元から動的でした。'unsafe-inline' に緩めれば静的に戻せる=後戻りできる形の代償だと確認したうえで XSS 耐性を取っています。
style-src だけは 'unsafe-inline' を許しています(nonce は <style> 要素にしか効かず、React の style 属性には効かないため)。script-src の厳格さを守る方を優先した非対称です。
サーバーレスでは関数インスタンスが使い捨て+並走するのでプロセス内メモリでは数えられず、共有ストアが要ります。Upstash Redis がこの用途の定番ですが採りませんでした。
原子性は INSERT … ON CONFLICT DO UPDATE … RETURNING の1文で取り、ローカル実 DB で並列 20 発 → 許可 5・カウント 20(数え落とし 0)を実測して確認しました。スライディングウィンドウにせず固定ウィンドウにしたのは、目的が「外部コストの青天井を防ぐこと」であって瞬間流量の平滑化ではないからです。
内部 UI からの更新に自前 API Route を挟むのは、同一プロセス内で HTTP 往復と JSON 二重シリアライズを増やすだけで、分離の実も速度も得られません。残した Route Handler は 3 本だけです。
そのぶん穴が開きます。 画像アップロードを Route Handler にしたのは Server Actions のボディ上限が既定 1MB だからですが(bodySizeLimit を緩めると全アクション共通に効いて DDoS 耐性を削る)、Route Handler には Server Actions の自動 CSRF 対策が効きません。同じ検査(Origin と Host の一致)を utils/same-origin.ts に自前で実装し、別オリジン・Origin 欠落を 403 にすることを e2e で担保しています。
管理操作の Server Action は実行時に必ず services/auth.ts の requireAdminServerAction を通し、そこで毎回 DB の最新ロールを確認します。proxy.ts(認証チェックのみ)とレイアウトガードは表示制御と早期リダイレクトのための補助で、セキュリティを保証していません。
Server Action は直接 POST できる公開エンドポイントであり、レイアウトのガードはキャッシュで古くなりうるため、「どこか1つでも通れば守られる」ではなく「ここを通らないと何もできない」場所を決める方が重要でした。
auth.users を削除してログインを不能にし、Profile は残して PII だけスクラブ、Report は保全して「退会済みユーザー」と表示します。公開 UGC では投稿はコミュニティの資産で、消すと他の読者の文脈まで壊れるためです(匿名化すれば GDPR 消去権の対象からも外れます)。
管理者向けの「ユーザー削除」機能は作りませんでした。 スパムや規約違反の掃除も同じ退会処理を代行する形にしています。Report.userId が Restrict で消せないうえ、目的(ログイン不可・PII 消去)はスクラブだけで達成でき、分岐を作らない方が安全だからです。
束ねないと「削除は成立したのに監査ログの書き込みが失敗して『削除に失敗しました』と返る」が起きます。半端な状態と嘘の文言という、利用者からは直しようのない組み合わせが構造的に消えます。
監査ログを書く操作は、すべて束ねています。 一般に「ログ」は観測のための best-effort でトランザクションに入れるものではなく、罠は名前の方でした(createAuditLog は名前がログでも中身は説明義務のある業務記録)。線引きを「取り返しがつくか」にしなかったのは、嘘の文言が、画面で結果が見えて戻せる操作(ステータス更新など)でも同じように起きるためです。
例外は退会の2か所です。Supabase の admin API(外部)をまたぐのでトランザクションに入らず、退会は成立させ、監査ログの失敗はサーバーのログ(console.error)に残すだけにしています。取り消せない退会が済んだ後で「失敗しました」と返すのは嘘になるためです。
外部サービス(Supabase Storage)は束ねられないので、どちらに倒すかを決める問題になります。画像削除は DB を先に消して孤児ファイルを残す側に倒しました(逆に倒すと画像が壊れて表示され、利用者に見える分だけ実害が大きい)。
当初は Report の列に持っていましたが、列のままだと上書き事故が構造的に起きます。ステータス更新のフォームは保存のたびに列の値を丸ごと送るので、管理者がステータスだけ変えるつもりで押すと、その間に出版社が書いた回答が消えます。行を足すだけの形には上書きが存在しません。
スキーマから旧列を消す変更は expand-contract で「移す」と「消す」を別のリリースに分け、間に切り戻せる期間を作りました。
到達しない分岐を潰そうと @typescript-eslint/no-unnecessary-condition を一時的に有効化して件数を測ったところ 14 件中、素直に従ってよいのは 1 件だけでした。残りは外部境界・Server Action の戻り型・キャストに由来する偽陽性で、この構成である限り比率は改善しません。--max-warnings 0 運用なので、常設すると抑制コメントが 13 本並んで本物が埋もれます。
代わりに「棚卸しのときに手で回す検査」に位置づけました。収穫の中身は期待と違っていて、デッドコード検出としては 1/14 でも「キャストが型に嘘をついている箇所」の検出としては 2/2 で当たっており、実際そのうち 1 件はエラーページ自身が 500 になるバグでした。測り方は docs/learnings.md に残してあります。
This is the beginning of the README. Read the rest at the original. Read the full README ↗