{"generatedAt":"2026-11-15","count":38,"posts":[{"slug":"ai-news-missing-morning-digests","title":"朝のダイジェストが 20 日のうち 8 日欠けていた — 503 の再試行と 13:00 の作り直し","date":"2026-11-02","summary":"AI ニュースの毎朝のダイジェストが直近 20 日で 8 日欠けていた。Gemini の 503 を 3 秒で諦めていたのを間隔を空けた再試行に変え、失敗した日を 13:00 に作り直し、応答が止まる場合の 2 分の上限とタグキャッシュも足した。","tags":["ai-news-feed-app","cloudflare-workers","cron","gemini","reliability"],"apps":["ai-news-feed-app"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/ai-news-missing-morning-digests"},{"slug":"chronoscroll-stable-ids-across-refreshes","title":"本文のハッシュを id にしたら、月次更新で特集が減った — chronoscroll の id の継続","date":"2026-11-02","summary":"chronoscroll の月次更新が、特集が 1,927 件から 1,922 件に減って失敗した。原因は本文のハッシュを含むイベント id と、7 日で消える Actions cache。消えた id を同じ日の最も近い行に結び付け、対応を記録して直した。","tags":["chronoscroll","data-pipeline","data-quality","github-actions"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-stable-ids-across-refreshes"},{"slug":"digest-2026-11","title":"更新ダイジェスト 2026 年 10 月下旬〜11 月 2 日","date":"2026-11-02","summary":"2026-10-15〜11-02 に全リポジトリへ入った変更のまとめ。Next 16.4 の障害、古い RSC を読みに行く問題、全リポジトリのセキュリティレビュー、静的サイトの空の 404、chronoscroll の id の継続、sumai-log の CSP 本適用。","tags":["digest","changelog"],"apps":["chronoscroll","ai-news-feed-app","service-anatomy","resume","sumai-log","agent-cockpit","lumen-bloom","skydial","hyper-tetris","tricking-3d","snippet-sprint","css-atelier","glsl-atelier","somewhere-now","lifeplan-simulator","elparaiso","ai-primer","my-apps-portal"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/digest-2026-11"},{"slug":"s-maxage-reached-the-browser","title":"CDN 向けの s-maxage がブラウザに届いていた — デプロイ後に消えたチャンクを読みに行く","date":"2026-11-02","summary":"Vercel が書き換えていた Cache-Control が Workers ではそのままブラウザに届き、デプロイ後に古い RSC が消えたチャンクを読みに行って画面が崩れた。Worker のラッパーで書き換え、Next.js の 8 本に同じ修正を入れた。","tags":["cloudflare-workers","nextjs","opennext","cache"],"apps":["resume","ai-primer","utility-tracker","elparaiso","ai-news-feed-app","lifeplan-simulator","service-anatomy","housing-performance-simulator"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/s-maxage-reached-the-browser"},{"slug":"service-worker-offline-shell-307","title":"/index.html の 307 でオフラインのシェルが空振りしていた — Service Worker のキャッシュキー","date":"2026-11-02","summary":"Workers の静的アセットは /index.html を 307 で / に転送する。これをそのまま precache すると転送済みの応答が保存され、ナビゲーションには使えない。PWA 4 本のシェルのキーを / に揃え、sw.js に初めてテストを付けた。","tags":["service-worker","pwa","cloudflare-workers","testing"],"apps":["skydial","css-atelier","snippet-sprint","lumen-bloom"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/service-worker-offline-shell-307"},{"slug":"sumai-log-script-csp-in-two-steps","title":"スクリプトの CSP を 2 段階で本適用する — TanStack Start と zod と Google マップ","date":"2026-11-02","summary":"script-src の無かった sumai-log に、毎リクエストの nonce と Report-Only で CSP を入れ、5 日間違反 0 件を確かめて本適用した。zod の eval の試し、Google マップの nonce、ワイルドカードを入れなかった理由をまとめる。","tags":["sumai-log","security","csp","tanstack-start","cloudflare-workers"],"apps":["sumai-log"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/sumai-log-script-csp-in-two-steps"},{"slug":"timeouts-on-every-external-call","title":"止まった接続は、例外より長く待たされる — 外部呼び出しのタイムアウトを全部見直す","date":"2026-11-02","summary":"2026-10-26 の全リポジトリのレビューで共通して多かった指摘は、外部呼び出しにタイムアウトが無いことだった。SDK ごとの渡し方の違い、上限の値の決め方、再試行に乗せる形、CI の audit-gate が失敗を合格にしていた件を整理する。","tags":["reliability","security-review","cloudflare-workers","ci"],"apps":["ai-news-feed-app","chronoscroll","somewhere-now","acro-finder","resume","lumen-bloom","my-apps-portal"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/timeouts-on-every-external-call"},{"slug":"chronoscroll-collection-audit","title":"古い特集 32 本を主題の記事と照らし直す — chronoscroll の特集の監査","date":"2026-10-13","summary":"chronoscroll の初期に作った特集 32 本を、日付だけでなく文言も主題の Wikipedia 記事と照らして見直した。968 件を確認し、128 件を直して 26 件を外した。どんな誤りがあり、どう確かめたか。","tags":["chronoscroll","fact-checking","data-quality"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-collection-audit"},{"slug":"chronoscroll-residue-and-suppress","title":"年表の要約に残ったパーサの残骸を数えて直す — chronoscroll の検出器と suppress.yaml","date":"2026-10-13","summary":"chronoscroll の要約に残っていた日付の断片や {{Cite の切れ端を検出器で数え、469 件を年ページの元の行と突き合わせて直した。種類ごとの件数に上限を置いて戻りを防ぎ、イベントでない行は新しい suppress.yaml で取り除いた。","tags":["chronoscroll","data-quality","fact-checking"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-residue-and-suppress"},{"slug":"css-atelier-hit-test-and-no-webgl","title":"矩形に表れない重なりを採点する — CSS Atelier の当たり判定と WebGL のない端末","date":"2026-10-13","summary":"CSS Atelier に、プレビューの指定した点でいちばん手前の要素を調べる当たり判定の採点を足し、z-index や pointer-events のレッスンを作った。WebGL が使えない端末で 3D のあるレッスンが進めなかった問題の直し方も。","tags":["css-atelier","learning-apps","testing"],"apps":["css-atelier"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/css-atelier-hit-test-and-no-webgl"},{"slug":"digest-2026-10-mid","title":"更新ダイジェスト 2026 年 10 月中旬","date":"2026-10-13","updated":"2026-11-02","summary":"2026-10-11〜14 に全リポジトリへ入った変更のまとめ。Next.js 7 本の nonce CSP とページのキャッシュ、chronoscroll の特集 64 本と監査と表示の高速化、CSS Atelier の当たり判定、Service Anatomy の期限つきの記述、学習アプリと技の追加。","tags":["digest","changelog"],"apps":["chronoscroll","service-anatomy","css-atelier","glsl-atelier","ai-primer","tricking-3d","acro-finder","lumen-bloom","ai-news-feed-app","my-apps-portal"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/digest-2026-10-mid"},{"slug":"nonce-csp-seven-nextjs-apps","title":"Next.js 7 本の CSP を Worker の nonce にして、Observatory を A+ にした","date":"2026-10-13","summary":"Next.js 7 本の CSP を、Worker が HTML ごとに nonce を付ける方式に揃えて Observatory を A+ 125 にした。'strict-dynamic' を付けない理由と、nonce の信頼境界、塞いだ SVG の script href の穴。","tags":["csp","security","observatory","cloudflare-workers","nextjs"],"apps":["ai-primer","elparaiso","lifeplan-simulator","housing-performance-simulator","utility-tracker","resume","ai-news-feed-app","service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/nonce-csp-seven-nextjs-apps"},{"slug":"opennext-incremental-cache-1102","title":"事前生成したページを毎回描き直していた — OpenNext の incrementalCache と 1102","date":"2026-10-13","summary":"OpenNext で載せた Next.js 6 本が incrementalCache を設定しておらず、事前生成したページをリクエストごとに描画し直していた。ai-primer ではプリフェッチの一斉リクエストで CPU 上限（1102）に当たった。直し方と、キャッシュで出た 404 の言語の罠。","tags":["cloudflare-workers","opennext","nextjs","performance"],"apps":["ai-primer","elparaiso","resume","utility-tracker","lifeplan-simulator","housing-performance-simulator"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/opennext-incremental-cache-1102"},{"slug":"service-anatomy-expiring-claims","title":"いつか古くなる記述に期限を付ける — Service Anatomy の expires","date":"2026-10-13","summary":"キャンペーンの終了日や月ごとの料率のように、いつか正しくなくなる記述を記事の frontmatter に expires として登録し、期限を過ぎたものをテストと freshness の一覧に出すようにした。最初に見つかった 2 件の書き直しまで。","tags":["service-anatomy","content-modeling","fact-checking"],"apps":["service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/service-anatomy-expiring-claims"},{"slug":"tricking-3d-plausibility-and-camera","title":"技の動きをテストで疑う — tricking-3d の妥当性テストとカメラ追従","date":"2026-10-13","summary":"トリッキングの技を 3D で見る tricking-3d を 27 技から 59 技にした。股関節の可動域・着地足の滑り・手をつく順番を数値のテストにし、移動する技はカメラが腰の軌跡を平滑化して追う。スマホでは注釈を番号の丸印にまとめた。","tags":["tricking-3d","threejs","testing","animation"],"apps":["tricking-3d"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/tricking-3d-plausibility-and-camera"},{"slug":"acro-finder-official-sources-only","title":"施設ディレクトリを公式情報だけで保つ","date":"2026-10-07","summary":"ACRO/FINDER の施設データを見直した。公式のページに書かれた事実だけを入れて薄い施設を 54 件から 5 件にし、200 を返しても中身が違うリンクを直し、閉鎖は公式の告知で判断した。座標と距離はテストで守る。","tags":["acro-finder","fact-checking","testing"],"apps":["acro-finder"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/acro-finder-official-sources-only"},{"slug":"ai-primer-keeping-lessons-fresh","title":"AI リテラシー教材を古くしないために","date":"2026-10-07","summary":"AI Primer は各レッスンに最終確認日を持たせ、90 日を過ぎた項目は月次のワークフローが知らせる。2026 年 10 月の見直しで変わっていた事実、一次情報で確かめられない出典の扱い、内部リンクと日付のテストで見つけた穴。","tags":["ai-primer","learning-apps","fact-checking","testing"],"apps":["ai-primer"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/ai-primer-keeping-lessons-fresh"},{"slug":"chronoscroll-collections-from-existing-events","title":"27,000 件の年表から特集を編む","date":"2026-10-07","summary":"chronoscroll の特集を 7 本から 17 本にした。新しく書かずに既存のイベントを参照するだけで組み、Wikipedia の記事と日付や表現が食い違うイベントは外した。10 月のデータを巻き戻さない JSON の作り方、OGP、R2 への同期も。","tags":["chronoscroll","content-modeling","fact-checking"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-collections-from-existing-events"},{"slug":"chronoscroll-sveltekit-3","title":"chronoscroll を SvelteKit 3 へ上げた記録","date":"2026-10-07","summary":"Dependabot の更新 PR では通らなかった 2 つの理由（tsconfig の $app/tsconfig と古い cookie の override）、移行ツールの差分を全部読んだ理由、R2 に置く 27,453 ページのファイル名が変わらないことの確かめ方。","tags":["sveltekit","chronoscroll","migration","dependabot"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-sveltekit-3"},{"slug":"csp-without-unsafe-inline","title":"CSP を 'unsafe-inline' なしにする 3 つの方法","date":"2026-10-07","summary":"静的 HTML はビルド時の sha256 を _headers に、ルール上限に収まらないページは meta タグに、Worker を通る HTML は HTMLRewriter で毎回の nonce を。3 本の Observatory を A+ 125 にした方法の選び方と、CSP が 2 本重なった失敗。","tags":["csp","security","observatory","cloudflare-workers","nextjs"],"apps":["my-apps-portal","acro-finder","service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/csp-without-unsafe-inline"},{"slug":"css-atelier-auto-grading","title":"CSS の課題を自動で採点する","date":"2026-10-07","summary":"CSS Atelier を 37 レッスンから 80 レッスンに増やしたときの採点の作り方。計算値と描画位置の使い分け、実ブラウザで記録した値を Node で再生するテスト、column-reverse で作るスクロール済みの状態、判定を見送ったもの。","tags":["css-atelier","learning-apps","testing"],"apps":["css-atelier"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/css-atelier-auto-grading"},{"slug":"deps-and-workflows-in-one-day","title":"公開 24 リポジトリの依存とワークフローを一日で揃えた話","date":"2026-10-07","summary":"2026-10-06 に source-map-js の勧告が全 lockfile に同時に当たった日の対応と、GitHub Actions のコミット SHA 固定、Dependabot の Actions 更新 PR 49 本の処理とグループ化、自動マージを minor/patch に限っている理由。","tags":["dependabot","github-actions","ci","security"],"apps":["my-apps-portal","service-anatomy","ai-primer","acro-finder","chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/deps-and-workflows-in-one-day"},{"slug":"glsl-atelier-readpixels-grading","title":"シェーダーの課題を readPixels で採点し、解けることをテストで確かめる","date":"2026-10-07","summary":"GLSL Atelier を 31 レッスンから 60 レッスンに増やした。描いた画素を 24×24 の点で採点し、シェーダーを JS に移して解答が通ることを Node で確かめ、ブラウザでは 4 通りの画面で全レッスンを回した。そこで見つかった初回実行のバグも書く。","tags":["glsl-atelier","learning-apps","testing","threejs"],"apps":["glsl-atelier"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/glsl-atelier-readpixels-grading"},{"slug":"mobile-lighthouse-90-three-apps","title":"モバイル Lighthouse を 90 に届かせた 3 本の記録","date":"2026-10-07","summary":"acro-finder・chronoscroll・service-anatomy のモバイル Performance を 2026-10-06 に 90 以上にした記録。和文フォントと HTML の削減、フェードインで遅れた LCP、/ のリダイレクト、そして版とばらつきの扱い。","tags":["lighthouse","performance","nextjs","sveltekit"],"apps":["acro-finder","chronoscroll","service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/mobile-lighthouse-90-three-apps"},{"slug":"snippet-sprint-snippets-on-real-toolchains","title":"300 を超えるコード片を公式の処理系で動かした話","date":"2026-10-07","summary":"タイピングゲーム Snippet Sprint の問題を 118 問から 375 問に増やした。問題の文字列をそのまま処理系に通し、配布物はチェックサムで照合し、画面に収まるかも測った。確かめられなかったものも書き残す。","tags":["snippet-sprint","testing","learning-apps"],"apps":["snippet-sprint"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/snippet-sprint-snippets-on-real-toolchains"},{"slug":"workers-cpu-limit-1102","title":"無料プランの Worker が CPU 上限（1102）で 503 を返した話","date":"2026-10-07","summary":"Next.js を OpenNext で載せたこのポートフォリオと acro-finder が、Workers 無料プランの CPU 上限で 503 を返した。原因の調べ方と、ページを静的アセットにして Worker を起動させない直し方、staticAssetsIncrementalCache の役目。","tags":["cloudflare-workers","opennext","nextjs","static-site"],"apps":["my-apps-portal","acro-finder"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/workers-cpu-limit-1102"},{"slug":"always-on-3d-without-losing-mobile-lighthouse","title":"常時 3D 描画でモバイルの Lighthouse を落とさない工夫","date":"2026-10-06","summary":"Lumen Bloom のモバイル Performance 45 → 99 までにやったこと。適応フレームレート、影パスの動的停止、段階的な初期化と compileAsync、タブ非表示で全停止。Skydial・Snippet Sprint の遅延読込とあわせて。","tags":["threejs","performance","lighthouse","lumen-bloom","skydial"],"apps":["lumen-bloom","skydial","snippet-sprint","hyper-tetris","tricking-3d"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/always-on-3d-without-losing-mobile-lighthouse"},{"slug":"chronoscroll-r2-worker-hybrid","title":"chronoscroll — 27,137 件の年表を R2 + Worker のハイブリッドで配る理由","date":"2026-10-06","summary":"静的アセットの上限 20,000 ファイルに収まらない 27,231 ファイルを、イベント個別ページだけ R2 に置くことで無料枠のまま配信した設計。差分同期・プレビュービルドのガード・大量削除の安全弁について。","tags":["chronoscroll","cloudflare-workers","cloudflare-r2","sveltekit","static-site"],"apps":["chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/chronoscroll-r2-worker-hybrid"},{"slug":"digest-2026-10","title":"更新ダイジェスト 2026 年 9 月下旬〜10 月上旬","date":"2026-10-06","updated":"2026-10-13","summary":"2026-09-22〜10-06 に全リポジトリへ入った変更のまとめ。依存更新と ESLint 10、undici の脆弱性対応、axe 違反ゼロ化、UI 層のテスト、SHIG 2 巡目、Effect 4、chronoscroll の Workers 移行、Service Anatomy の新記事 8 本。","tags":["digest","changelog"],"apps":["chronoscroll","service-anatomy","somewhere-now","ai-news-feed-app","my-apps-portal","acro-finder","utility-tracker","hyper-tetris"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/digest-2026-10"},{"slug":"effect-4-in-three-pipelines","title":"Effect 4 を 3 本のパイプラインに入れてみた","date":"2026-10-06","summary":"2026-09-30 GA の Effect 4.0 を、chronoscroll のビルドパイプライン・Somewhere Now の Cron・AI ニュースダイジェストの日次ジョブに同じ日に導入した記録。型付きの失敗、TestClock でのリトライ検証、Workers の CPU 上限との付き合い方。","tags":["effect","typescript","cloudflare-workers","cron","error-handling"],"apps":["chronoscroll","somewhere-now","ai-news-feed-app"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/effect-4-in-three-pipelines"},{"slug":"how-this-portfolio-works","title":"自分のアプリを自分で計測するポートフォリオの作り方","date":"2026-10-06","summary":"このポートフォリオ自体の設計。npm registry・OSV・GitHub API から各アプリの状態を集め、ビルド時に静的化して Workers の CPU 上限を避けるまでの経緯と、セキュリティ列とバッジの意味の違い。","tags":["my-apps-portal","cloudflare-workers","nextjs","security"],"apps":["my-apps-portal"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/how-this-portfolio-works"},{"slug":"lighthouse-and-observatory-playbook","title":"Lighthouse 100×4 と Observatory A+ を複数アプリで取るための定石","date":"2026-10-06","summary":"複数のアプリで繰り返し効いた手順。Three.js の動的 import、インライン polyfill の無効化、Next のフォント方針、CSP から unsafe-inline を外すこと、ビーコンに SRI を付けない理由、中央値判定の CI ガード。","tags":["lighthouse","observatory","performance","csp","security"],"apps":["hyper-tetris","tricking-3d","snippet-sprint","skydial","css-atelier","service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/lighthouse-and-observatory-playbook"},{"slug":"live-code-under-strict-csp","title":"学習アプリ3本の共通設計 — 厳格な CSP のまま学習者のコードを実行する","date":"2026-10-06","summary":"CSS Atelier は constructable stylesheet、GLSL Atelier は readPixels と不透明オリジンの sandbox iframe、AI Primer はビルド時の Markdown 変換。「解説→記述→自動採点」を unsafe-inline なしで成立させた方法。","tags":["css-atelier","glsl-atelier","ai-primer","csp","security","learning-apps"],"apps":["css-atelier","glsl-atelier","ai-primer"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/live-code-under-strict-csp"},{"slug":"service-anatomy-articles-as-records","title":"Service Anatomy — 記事をデータベースのレコードとして扱う編集設計","date":"2026-10-06","summary":"解剖スコア・確度3段階の技術構成・出典を frontmatter に持たせ、記事の整合性を CI で検証する仕組み。nonce CSP を捨てて全ルートを SSG 化し、Cloudflare Workers へ移すまでの判断も含めて。","tags":["service-anatomy","nextjs","content-modeling","cloudflare-workers","csp"],"apps":["service-anatomy"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/service-anatomy-articles-as-records"},{"slug":"shig-as-a-ui-yardstick","title":"ソシオメディア HIG 100 項目を UI 見直しの物差しにしている話","date":"2026-10-06","summary":"確認ダイアログを取り消し通知に置き換える（57・54）、戻るで閉じる（59・60・82）、色だけに頼らない（96）、7mm のタップ領域（78）、寛容な入力（50）。コミットに番号を添えて 2026 年 9〜10 月に全リポジトリを 2 巡した記録。","tags":["shig","ux","accessibility","design"],"apps":["utility-tracker","agent-cockpit","skydial","acro-finder","resume","snippet-sprint"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/shig-as-a-ui-yardstick"},{"slug":"somewhere-now-worker-kv-cron","title":"Somewhere Now — 5,000 地点超のライブカメラを Worker + KV + Cron で生かし続ける","date":"2026-10-06","summary":"ライブ配信は終わり videoId は変わる。カメラ定義と生存状態を分け、Cron が YouTube Data API で追いかける設計と、サブリクエスト上限・同時発火・waitUntil で失敗を飲み込む罠。","tags":["somewhere-now","cloudflare-workers","cloudflare-kv","cron","youtube-api"],"apps":["somewhere-now"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/somewhere-now-worker-kv-cron"},{"slug":"testing-policy-lib-100-ui-jsdom","title":"テスト方針 — 純ロジック層は 100% ゲート、UI 層は jsdom の振る舞いテスト","date":"2026-10-06","summary":"engine/lib を純関数に寄せて 100% の閾値を CI で強制し、2026-09-28〜10-01 に全リポジトリで UI 層にも jsdom + Testing Library の振る舞いテストと「実測値の 2 ポイント下」のゲートを足した経緯。書いて見つかったバグも。","tags":["testing","vitest","coverage","ci"],"apps":["css-atelier","lumen-bloom","agent-cockpit","acro-finder","snippet-sprint","roba-hud"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/testing-policy-lib-100-ui-jsdom"},{"slug":"vercel-to-cloudflare-workers","title":"Vercel から Cloudflare Workers へ公開アプリをまとめて移した記録","date":"2026-10-06","summary":"2026-08-11 の Vercel 停止から始まった移行の定型手順と、_headers のカンマ連結・html_handling の 307・OpenNext の Cron ラッパー・nonce CSP・esbuild 未宣言・SITE_URL の localhost 化といった罠。","tags":["cloudflare-workers","vercel","migration","opennext","vite"],"apps":["lumen-bloom","skydial","snippet-sprint","resume","acro-finder","ai-news-feed-app","chronoscroll"],"url":"https://my-apps-portal.saitotakuya0719.workers.dev/blog/vercel-to-cloudflare-workers"}]}