<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>recca0120 技術筆記</title><link>https://recca0120.github.io/</link><description>Recent content on recca0120 技術筆記</description><generator>Hugo -- gohugo.io</generator><language>zh-hant-tw</language><lastBuildDate>Tue, 09 Jun 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://recca0120.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>AI 幫你加了快取，但它悄悄污染了商業邏輯——用裝飾者模式根治這個問題</title><link>https://recca0120.github.io/2026/06/09/decorator-pattern-cache/</link><pubDate>Tue, 09 Jun 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/06/09/decorator-pattern-cache/</guid><description>分析 AI 寫程式時把快取邏輯嵌入 production code 的常見模式，以及為什麼「另開一個 class」不夠，用裝飾者模式才能真正把快取與商業邏輯切乾淨。</description><content:encoded>&lt;![CDATA[分析 AI 寫程式時把快取邏輯嵌入 production code 的常見模式，以及為什麼「另開一個 class」不夠，用裝飾者模式才能真正把快取與商業邏輯切乾淨。<br/><img src="https://recca0120.github.io/2026/06/09/decorator-pattern-cache/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/06/09/decorator-pattern-cache/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/06/09/decorator-pattern-cache/featured.png" medium="image"/><category>design-patterns</category><category>cache</category><category>TypeScript</category><category>clean-architecture</category><category>DevTools</category></item><item><title>Code Quest：用互動模式跑 Claude Code 的 Web UI，6/15 收費新制剛好避開</title><link>https://recca0120.github.io/2026/05/16/code-quest-claude-code-web-ui/</link><pubDate>Sat, 16 May 2026 14:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/16/code-quest-claude-code-web-ui/</guid><description>介紹 code-quest 這個開源專案——直接 spawn Claude Code CLI 並完整解析 NDJSON protocol，支援 session fork/resume/worktree、Split deployment、File explorer 與 Git 整合。分析為何互動模式在 6/15 新制後比 SDK / claude -p 更划算，以及架構上的核心差異。</description><content:encoded>&lt;![CDATA[介紹 code-quest 這個開源專案——直接 spawn Claude Code CLI 並完整解析 NDJSON protocol，支援 session fork/resume/worktree、Split deployment、File explorer 與 Git 整合。分析為何互動模式在 6/15 新制後比 SDK / claude -p 更划算，以及架構上的核心差異。<br/><img src="https://recca0120.github.io/2026/05/16/code-quest-claude-code-web-ui/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/16/code-quest-claude-code-web-ui/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/16/code-quest-claude-code-web-ui/featured.png" medium="image"/><category>claude-code</category><category>AI</category><category>open-source</category><category>web-ui</category><category>DevTools</category></item><item><title>用不同 SSH Key Clone GitHub 專案的三種方法</title><link>https://recca0120.github.io/2026/05/16/git-clone-different-ssh-key/</link><pubDate>Sat, 16 May 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/16/git-clone-different-ssh-key/</guid><description>當你有多個 GitHub 帳號或需要用特定 SSH key clone 某個 repo 時，介紹三種解法：GIT_SSH_COMMAND 一次性用法、git config core.sshCommand 設定單一 repo、以及 ~/.ssh/config Host 別名搭配 url.insteadOf 的多帳號管理。</description><content:encoded>&lt;![CDATA[當你有多個 GitHub 帳號或需要用特定 SSH key clone 某個 repo 時，介紹三種解法：GIT_SSH_COMMAND 一次性用法、git config core.sshCommand 設定單一 repo、以及 ~/.ssh/config Host 別名搭配 url.insteadOf 的多帳號管理。<br/><img src="https://recca0120.github.io/2026/05/16/git-clone-different-ssh-key/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/16/git-clone-different-ssh-key/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/16/git-clone-different-ssh-key/featured.png" medium="image"/><category>git</category><category>ssh</category><category>github</category><category>DevTools</category></item><item><title>Free Claude Code：把 Claude Code 的 API 請求轉到其他免費服務</title><link>https://recca0120.github.io/2026/05/15/free-claude-code-proxy/</link><pubDate>Fri, 15 May 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/15/free-claude-code-proxy/</guid><description>介紹 free-claude-code 這個開源 proxy，讓你可以繼續用 Claude Code 的 CLI、VS Code、JetBrains 介面，但後端換成 NVIDIA NIM、OpenRouter、DeepSeek、本機模型等免費或便宜的替代方案。</description><content:encoded>&lt;![CDATA[介紹 free-claude-code 這個開源 proxy，讓你可以繼續用 Claude Code 的 CLI、VS Code、JetBrains 介面，但後端換成 NVIDIA NIM、OpenRouter、DeepSeek、本機模型等免費或便宜的替代方案。<br/><img src="https://recca0120.github.io/2026/05/15/free-claude-code-proxy/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/15/free-claude-code-proxy/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/15/free-claude-code-proxy/featured.png" medium="image"/><category>claude-code</category><category>AI</category><category>proxy</category><category>open-source</category><category>DevTools</category></item><item><title>掃了 36 天 Claude Code 日誌：model 被靜默切換，效率差 11.5 倍</title><link>https://recca0120.github.io/2026/05/09/claude-code-model-cost-efficiency/</link><pubDate>Sat, 09 May 2026 16:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/09/claude-code-model-cost-efficiency/</guid><description>延續前兩篇 cache TTL audit，這次拆 model 維度。發現 server 端三次靜默切換 main agent model（Opus 4.6 → Sonnet 4.6 → Opus 4.7 → Sonnet 4.6），加上 sub-agent model 由 Claude Code 自主分配。跨 7 個時期比較「每百萬 main output 的總費用」，最有效率和最沒效率差 11.5 倍。</description><content:encoded>&lt;![CDATA[延續前兩篇 cache TTL audit，這次拆 model 維度。發現 server 端三次靜默切換 main agent model（Opus 4.6 → Sonnet 4.6 → Opus 4.7 → Sonnet 4.6），加上 sub-agent model 由 Claude Code 自主分配。跨 7 個時期比較「每百萬 main output 的總費用」，最有效率和最沒效率差 11.5 倍。<br/><img src="https://recca0120.github.io/2026/05/09/claude-code-model-cost-efficiency/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/09/claude-code-model-cost-efficiency/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/09/claude-code-model-cost-efficiency/featured.png" medium="image"/><category>claude-code</category><category>prompt-caching</category><category>ai-agent</category><category>python</category><category>AI</category></item><item><title>JetBrains Air：讓多個 AI Agent 同時幫你寫程式的 Agentic IDE</title><link>https://recca0120.github.io/2026/05/06/jetbrains-air-agentic-ide/</link><pubDate>Wed, 06 May 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/06/jetbrains-air-agentic-ide/</guid><description>介紹 JetBrains Air 這個 agentic 開發環境，說明它如何讓 Claude、Codex、Gemini、Junie 四個 agent 並行處理任務，以及 Local / Git Worktree / Docker 三種執行環境的差異。</description><content:encoded>&lt;![CDATA[介紹 JetBrains Air 這個 agentic 開發環境，說明它如何讓 Claude、Codex、Gemini、Junie 四個 agent 並行處理任務，以及 Local / Git Worktree / Docker 三種執行環境的差異。<br/><img src="https://recca0120.github.io/2026/05/06/jetbrains-air-agentic-ide/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/06/jetbrains-air-agentic-ide/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/06/jetbrains-air-agentic-ide/featured.png" medium="image"/><category>AI</category><category>IDE</category><category>JetBrains</category><category>DevTools</category></item><item><title>用 Knip 找出專案裡的死碼：ESLint 和 depcheck 都看不到的盲點</title><link>https://recca0120.github.io/2026/05/02/knip-dead-code-detector/</link><pubDate>Sat, 02 May 2026 16:06:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/02/knip-dead-code-detector/</guid><description>介紹 Knip 這個 JavaScript/TypeScript 死碼偵測工具，說明 ESLint 和 depcheck 各自的盲點，以及 Knip 怎麼用完整 module graph 一次找出未使用的檔案、export 和依賴。</description><content:encoded>&lt;![CDATA[介紹 Knip 這個 JavaScript/TypeScript 死碼偵測工具，說明 ESLint 和 depcheck 各自的盲點，以及 Knip 怎麼用完整 module graph 一次找出未使用的檔案、export 和依賴。<br/><img src="https://recca0120.github.io/2026/05/02/knip-dead-code-detector/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/02/knip-dead-code-detector/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/02/knip-dead-code-detector/featured.png" medium="image"/><category>Node.js</category><category>TypeScript</category><category>Testing</category></item><item><title>vitest-fail-on-console：讓 console.error 不再默默被忽略</title><link>https://recca0120.github.io/2026/05/02/vitest-fail-on-console/</link><pubDate>Sat, 02 May 2026 08:18:00 +0800</pubDate><guid>https://recca0120.github.io/2026/05/02/vitest-fail-on-console/</guid><description>介紹 vitest-fail-on-console 套件，說明為什麼 console.warn/error 出現在測試裡是壞味道，以及怎麼用這個套件強制讓測試失敗、保持輸出乾淨。</description><content:encoded>&lt;![CDATA[介紹 vitest-fail-on-console 套件，說明為什麼 console.warn/error 出現在測試裡是壞味道，以及怎麼用這個套件強制讓測試失敗、保持輸出乾淨。<br/><img src="https://recca0120.github.io/2026/05/02/vitest-fail-on-console/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/05/02/vitest-fail-on-console/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/05/02/vitest-fail-on-console/featured.png" medium="image"/><category>Testing</category><category>Vitest</category><category>Node.js</category><category>Testing</category></item><item><title>用 memfs + FakeWatchService 測試檔案系統：不碰磁碟的 Node.js 測試策略</title><link>https://recca0120.github.io/2026/04/28/memfs-fake-watch-testing/</link><pubDate>Tue, 28 Apr 2026 12:36:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/28/memfs-fake-watch-testing/</guid><description>實際拆解一個 FileService 的測試，說明如何用 memfs 替換 fs、手寫 Fake 替換 chokidar，讓檔案系統測試跑得快、結果穩、安全性有保障。</description><content:encoded>&lt;![CDATA[實際拆解一個 FileService 的測試，說明如何用 memfs 替換 fs、手寫 Fake 替換 chokidar，讓檔案系統測試跑得快、結果穩、安全性有保障。<br/><img src="https://recca0120.github.io/2026/04/28/memfs-fake-watch-testing/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/28/memfs-fake-watch-testing/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/28/memfs-fake-watch-testing/featured.png" medium="image"/><category>Testing</category><category>Vitest</category><category>Node.js</category><category>Testing</category></item><item><title>Claude Code /model 選單怎麼加舊版 model？3 種方法實測與踩坑紀錄</title><link>https://recca0120.github.io/2026/04/27/claude-code-model-picker-config/</link><pubDate>Mon, 27 Apr 2026 01:07:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/27/claude-code-model-picker-config/</guid><description>Opus 4.7 自動升級後想切回 4.6，但 /model 選單沒選項。實測 availableModels、modelOverrides、ANTHROPIC_CUSTOM_MODEL_OPTION 三種設定，整理 GitHub 社群討論與推薦配置。</description><content:encoded>&lt;![CDATA[Opus 4.7 自動升級後想切回 4.6，但 /model 選單沒選項。實測 availableModels、modelOverrides、ANTHROPIC_CUSTOM_MODEL_OPTION 三種設定，整理 GitHub 社群討論與推薦配置。<br/><img src="https://recca0120.github.io/2026/04/27/claude-code-model-picker-config/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/27/claude-code-model-picker-config/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/27/claude-code-model-picker-config/featured.png" medium="image"/><category>claude-code</category><category>cost-optimization</category><category>AI</category></item><item><title>掃完三個月 Claude Code 帳單，社群在傳的省錢建議大多沒效</title><link>https://recca0120.github.io/2026/04/26/claude-code-3-month-billing-postmortem/</link><pubDate>Sun, 26 Apr 2026 07:55:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/26/claude-code-3-month-billing-postmortem/</guid><description>$127K 等價成本、127K turns、四個模型、三個月。把自己當資料集驗證後，「長 session 是元兇」「skill 太多」這些常見直覺被數據打臉，真正能省的只有兩條。</description><content:encoded>&lt;![CDATA[$127K 等價成本、127K turns、四個模型、三個月。把自己當資料集驗證後，「長 session 是元兇」「skill 太多」這些常見直覺被數據打臉，真正能省的只有兩條。<br/><img src="https://recca0120.github.io/2026/04/26/claude-code-3-month-billing-postmortem/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/26/claude-code-3-month-billing-postmortem/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/26/claude-code-3-month-billing-postmortem/featured.png" medium="image"/><category>claude-code</category><category>prompt-caching</category><category>ai-agent</category><category>cost-optimization</category><category>AI</category></item><item><title>再掃 12 天，Claude Code sub-agent cache TTL 已連續 17 天 100% 5m——這不是 regression，是新預設值</title><link>https://recca0120.github.io/2026/04/26/claude-code-cache-ttl-17-days/</link><pubDate>Sun, 26 Apr 2026 05:55:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/26/claude-code-cache-ttl-17-days/</guid><description>4/14 我報告 5 連天 sub-agent 100% 5m，結論留在「持續觀察」。今天 4/26 再掃，連續變 17 天、15,727 個 API call、0 個 1h 寫入。Anthropic 把主 issue 關了沒解決，社群一片火。</description><content:encoded>&lt;![CDATA[4/14 我報告 5 連天 sub-agent 100% 5m，結論留在「持續觀察」。今天 4/26 再掃，連續變 17 天、15,727 個 API call、0 個 1h 寫入。Anthropic 把主 issue 關了沒解決，社群一片火。<br/><img src="https://recca0120.github.io/2026/04/26/claude-code-cache-ttl-17-days/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/26/claude-code-cache-ttl-17-days/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/26/claude-code-cache-ttl-17-days/featured.png" medium="image"/><category>claude-code</category><category>prompt-caching</category><category>ai-agent</category><category>python</category><category>AI</category></item><item><title>Node.js spawn 子行程 stdout 被截斷：六個方案比過，只有寫檔有用</title><link>https://recca0120.github.io/2026/04/26/nodejs-spawn-stdout-truncated/</link><pubDate>Sun, 26 Apr 2026 04:55:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/26/nodejs-spawn-stdout-truncated/</guid><description>Node.js spawn 大量輸出的 CLI，最後幾 KB 在 process.exit() 時消失。比較 6 種解法後，唯一可靠的純 stdlib 跨平台方案是把 stdout fd 直接接到檔案。</description><content:encoded>&lt;![CDATA[Node.js spawn 大量輸出的 CLI，最後幾 KB 在 process.exit() 時消失。比較 6 種解法後，唯一可靠的純 stdlib 跨平台方案是把 stdout fd 直接接到檔案。<br/><img src="https://recca0120.github.io/2026/04/26/nodejs-spawn-stdout-truncated/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/26/nodejs-spawn-stdout-truncated/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/26/nodejs-spawn-stdout-truncated/featured.png" medium="image"/><category>Node.js</category><category>stdio</category><category>spawn</category><category>DevOps</category></item><item><title>把 AI 生的網頁從「能用」升級到「有設計感」：web-design-skill 導讀</title><link>https://recca0120.github.io/2026/04/25/web-design-skill-claude-design/</link><pubDate>Sat, 25 Apr 2026 15:05:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/25/web-design-skill-claude-design/</guid><description>導讀 ConardLi/web-design-skill：用反陳腔濫調清單、oklch 色彩、預先宣告 design system、v0 草稿等規則，把 AI 生網頁從同質化美學救出來。</description><content:encoded>&lt;![CDATA[導讀 ConardLi/web-design-skill：用反陳腔濫調清單、oklch 色彩、預先宣告 design system、v0 草稿等規則，把 AI 生網頁從同質化美學救出來。<br/><img src="https://recca0120.github.io/2026/04/25/web-design-skill-claude-design/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/25/web-design-skill-claude-design/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/25/web-design-skill-claude-design/featured.png" medium="image"/><category>claude-code</category><category>ai-agent</category><category>Design</category><category>oklch</category><category>Frontend</category></item><item><title>MinerU 實測：把 PDF 論文變成 RAG 吃得下的 Markdown</title><link>https://recca0120.github.io/2026/04/24/mineru-pdf-to-markdown/</link><pubDate>Fri, 24 Apr 2026 21:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/24/mineru-pdf-to-markdown/</guid><description>餵 PDF 給 LLM 最痛的就是公式、表格、雙欄排版被拆爛。我用 MinerU 2.5 把一份多欄學術 PDF 轉成 Markdown，公式變 LaTeX、表格變 HTML、閱讀順序也對，CPU 模式就跑得動。</description><content:encoded>&lt;![CDATA[餵 PDF 給 LLM 最痛的就是公式、表格、雙欄排版被拆爛。我用 MinerU 2.5 把一份多欄學術 PDF 轉成 Markdown，公式變 LaTeX、表格變 HTML、閱讀順序也對，CPU 模式就跑得動。<br/><img src="https://recca0120.github.io/2026/04/24/mineru-pdf-to-markdown/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/24/mineru-pdf-to-markdown/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/24/mineru-pdf-to-markdown/featured.png" medium="image"/><category>MinerU</category><category>PDF</category><category>RAG</category><category>OCR</category><category>LLM</category><category>AI</category></item><item><title>React Compiler 不自動做的 3 件事：從 512ms 降到 6ms 的實戰</title><link>https://recca0120.github.io/2026/04/21/react-compiler-limits/</link><pubDate>Tue, 21 Apr 2026 22:30:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/21/react-compiler-limits/</guid><description>我以為開了 React Compiler 就不用手動 memo，結果切 tab 卡 512ms。用 Profiler 抓出 3 個 compiler 邊界：子 component 邊界、prop capture 的 identity 意圖、setInterval 動畫 state，附更多日常會遇到的範例。</description><content:encoded>&lt;![CDATA[我以為開了 React Compiler 就不用手動 memo，結果切 tab 卡 512ms。用 Profiler 抓出 3 個 compiler 邊界：子 component 邊界、prop capture 的 identity 意圖、setInterval 動畫 state，附更多日常會遇到的範例。<br/><img src="https://recca0120.github.io/2026/04/21/react-compiler-limits/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/21/react-compiler-limits/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/21/react-compiler-limits/featured.png" medium="image"/><category>React</category><category>React Compiler</category><category>Performance</category><category>Profiler</category><category>TypeScript</category><category>Frontend</category></item><item><title>AI Agent 時代為什麼更需要好測試：Fake + MSW 的 agent-friendly 特性</title><link>https://recca0120.github.io/2026/04/20/agent-friendly-testing/</link><pubDate>Mon, 20 Apr 2026 08:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/20/agent-friendly-testing/</guid><description>前三篇系列文講的 Fake + MSW + 共用測試 infra，對 Claude Code 這類 AI agent 幫助特別大。整理五個具體優勢：快速回饋、訊號可信、範本可學、refactor 安全、context 省用。</description><content:encoded>&lt;![CDATA[前三篇系列文講的 Fake + MSW + 共用測試 infra，對 Claude Code 這類 AI agent 幫助特別大。整理五個具體優勢：快速回饋、訊號可信、範本可學、refactor 安全、context 省用。<br/><img src="https://recca0120.github.io/2026/04/20/agent-friendly-testing/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/20/agent-friendly-testing/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/20/agent-friendly-testing/featured.png" medium="image"/><category>Testing</category><category>Claude Code</category><category>AI</category><category>Testing</category></item><item><title>Monorepo 跨層共用 HTTP Mock：外部 API 不適合用 Fake 時的方案</title><link>https://recca0120.github.io/2026/04/20/monorepo-shared-http-mock/</link><pubDate>Mon, 20 Apr 2026 07:45:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/20/monorepo-shared-http-mock/</guid><description>前兩篇講了用 Fake 處理內部 service，但外部 HTTP API 不適用這套。介紹怎麼在 monorepo 裡用 MSW / msw-fetch-mock 做同一份 HTTP mock 從前端測到後端，與 Fake 互補。</description><content:encoded>&lt;![CDATA[前兩篇講了用 Fake 處理內部 service，但外部 HTTP API 不適用這套。介紹怎麼在 monorepo 裡用 MSW / msw-fetch-mock 做同一份 HTTP mock 從前端測到後端，與 Fake 互補。<br/><img src="https://recca0120.github.io/2026/04/20/monorepo-shared-http-mock/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/20/monorepo-shared-http-mock/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/20/monorepo-shared-http-mock/featured.png" medium="image"/><category>Testing</category><category>MSW</category><category>Vitest</category><category>TypeScript</category><category>monorepo</category><category>Testing</category></item><item><title>Monorepo 跨層共用 Fake：一份測試替身從前端用到後端</title><link>https://recca0120.github.io/2026/04/20/monorepo-shared-fake-testing/</link><pubDate>Mon, 20 Apr 2026 07:30:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/20/monorepo-shared-fake-testing/</guid><description>在 monorepo 裡，前端、後端、業務邏輯各寫一套 mock 是重複勞動且容易行為不一致。這篇示範怎麼設計共用的 Fake，讓同一份測試替身從 React component 測到 API route 再到 service layer。</description><content:encoded>&lt;![CDATA[在 monorepo 裡，前端、後端、業務邏輯各寫一套 mock 是重複勞動且容易行為不一致。這篇示範怎麼設計共用的 Fake，讓同一份測試替身從 React component 測到 API route 再到 service layer。<br/><img src="https://recca0120.github.io/2026/04/20/monorepo-shared-fake-testing/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/20/monorepo-shared-fake-testing/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/20/monorepo-shared-fake-testing/featured.png" medium="image"/><category>Testing</category><category>Vitest</category><category>TypeScript</category><category>monorepo</category><category>Testing</category></item><item><title>DI + Fake + in-memory：寫出能長期維護的前端測試</title><link>https://recca0120.github.io/2026/04/20/di-fake-in-memory-testing/</link><pubDate>Mon, 20 Apr 2026 07:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/20/di-fake-in-memory-testing/</guid><description>介紹依賴注入、Fake 和 in-memory 實作三位一體的測試設計。用 FakeStorageService 的實例說明為什麼 Fake 比 Mock 好維護，以及怎麼寫一個可重用、可信賴的 in-memory Fake。</description><content:encoded>&lt;![CDATA[介紹依賴注入、Fake 和 in-memory 實作三位一體的測試設計。用 FakeStorageService 的實例說明為什麼 Fake 比 Mock 好維護，以及怎麼寫一個可重用、可信賴的 in-memory Fake。<br/><img src="https://recca0120.github.io/2026/04/20/di-fake-in-memory-testing/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/20/di-fake-in-memory-testing/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/20/di-fake-in-memory-testing/featured.png" medium="image"/><category>Testing</category><category>Vitest</category><category>TypeScript</category><category>Testing</category></item><item><title>GitButler：重新設計 Git 操作體驗的現代版本控制工具</title><link>https://recca0120.github.io/2026/04/17/gitbutler-modern-git-client/</link><pubDate>Fri, 17 Apr 2026 17:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/17/gitbutler-modern-git-client/</guid><description>GitButler 是一個基於 Git 的版本控制介面，支援平行分支、疊加分支、無限復原和 AI 整合。不用切換分支就能同時處理多個任務，拖拉就能完成 commit 管理，專為現代開發工作流程設計。</description><content:encoded>&lt;![CDATA[GitButler 是一個基於 Git 的版本控制介面，支援平行分支、疊加分支、無限復原和 AI 整合。不用切換分支就能同時處理多個任務，拖拉就能完成 commit 管理，專為現代開發工作流程設計。<br/><img src="https://recca0120.github.io/2026/04/17/gitbutler-modern-git-client/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/17/gitbutler-modern-git-client/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/17/gitbutler-modern-git-client/featured.png" medium="image"/><category>git</category><category>GitButler</category><category>DevOps</category></item><item><title>clipboard.js 包成 Promise：一個 function 搞定瀏覽器相容問題</title><link>https://recca0120.github.io/2026/04/16/clipboard-js-promise-fallback/</link><pubDate>Thu, 16 Apr 2026 23:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/16/clipboard-js-promise-fallback/</guid><description>navigator.clipboard 在本機開發（非 HTTPS）和 iOS Safari 上常常失效。用 clipboard.js 包裝成統一的 Promise 介面，讓 fallback 對呼叫端透明，接到任何框架都一樣用法。</description><content:encoded>&lt;![CDATA[navigator.clipboard 在本機開發（非 HTTPS）和 iOS Safari 上常常失效。用 clipboard.js 包裝成統一的 Promise 介面，讓 fallback 對呼叫端透明，接到任何框架都一樣用法。<br/><img src="https://recca0120.github.io/2026/04/16/clipboard-js-promise-fallback/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/16/clipboard-js-promise-fallback/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/16/clipboard-js-promise-fallback/featured.png" medium="image"/><category>clipboard.js</category><category>JavaScript</category><category>TypeScript</category><category>Frontend</category></item><item><title>用 Overmind 管理本機多服務：比 foreman 好用在哪</title><link>https://recca0120.github.io/2026/04/16/overmind-procfile-process-manager/</link><pubDate>Thu, 16 Apr 2026 18:45:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/16/overmind-procfile-process-manager/</guid><description>介紹 Overmind 這個 Procfile 流程管理器，整合 tmux 讓每個 process 可以獨立連線、單獨重啟，解決 foreman 的 log 截斷和顏色問題，適合 Rails、全端開發的本機開發環境。</description><content:encoded>&lt;![CDATA[介紹 Overmind 這個 Procfile 流程管理器，整合 tmux 讓每個 process 可以獨立連線、單獨重啟，解決 foreman 的 log 截斷和顏色問題，適合 Rails、全端開發的本機開發環境。<br/><img src="https://recca0120.github.io/2026/04/16/overmind-procfile-process-manager/featured.png" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/16/overmind-procfile-process-manager/featured.png" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/16/overmind-procfile-process-manager/featured.png" medium="image"/><category>overmind</category><category>tmux</category><category>Procfile</category><category>Rails</category><category>DevOps</category></item><item><title>打開 analytics.google.com 卻看到 Laravel Valet 憑證？根本原因與修復方法</title><link>https://recca0120.github.io/2026/04/15/valet-ca-cert-leak/</link><pubDate>Wed, 15 Apr 2026 12:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/15/valet-ca-cert-leak/</guid><description>在本機開發環境用 Laravel Valet，卻在瀏覽器看到外部網站（如 analytics.google.com）顯示 Valet 自簽憑證。整理這個問題的完整成因——dnsmasq 洩漏、nginx catch-all、瀏覽器憑證快取——以及逐步排查與修復的方法。</description><content:encoded>&lt;![CDATA[在本機開發環境用 Laravel Valet，卻在瀏覽器看到外部網站（如 analytics.google.com）顯示 Valet 自簽憑證。整理這個問題的完整成因——dnsmasq 洩漏、nginx catch-all、瀏覽器憑證快取——以及逐步排查與修復的方法。<br/><img src="https://recca0120.github.io/2026/04/15/valet-ca-cert-leak/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/15/valet-ca-cert-leak/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/15/valet-ca-cert-leak/featured.jpg" medium="image"/><category>laravel-valet</category><category>ssl</category><category>nginx</category><category>dnsmasq</category><category>macos</category><category>DevOps</category></item><item><title>React Compiler 1.0 + Vite 8：@vitejs/plugin-react v6 拆掉 Babel 之後，正確的安裝方式</title><link>https://recca0120.github.io/2026/04/14/react-compiler-vite-v6/</link><pubDate>Tue, 14 Apr 2026 23:30:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/14/react-compiler-vite-v6/</guid><description>React Compiler 2025 年 10 月 1.0 stable，但 @vitejs/plugin-react v6 同時把內建 Babel 換成 oxc，舊的 react({ babel: {...} }) 寫法在 Vite 8 已經不能用。整理 2026 年正確的安裝流程、ESLint 設定、漸進導入策略。</description><content:encoded>&lt;![CDATA[React Compiler 2025 年 10 月 1.0 stable，但 @vitejs/plugin-react v6 同時把內建 Babel 換成 oxc，舊的 react({ babel: {...} }) 寫法在 Vite 8 已經不能用。整理 2026 年正確的安裝流程、ESLint 設定、漸進導入策略。<br/><img src="https://recca0120.github.io/2026/04/14/react-compiler-vite-v6/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/14/react-compiler-vite-v6/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/14/react-compiler-vite-v6/featured.jpg" medium="image"/><category>react</category><category>react-compiler</category><category>vite</category><category>performance</category><category>Frontend</category></item><item><title>git worktree：一個 repo 同時開多份工作目錄，也是 AI agent 並行開發的關鍵</title><link>https://recca0120.github.io/2026/04/14/git-worktree-parallel-work/</link><pubDate>Tue, 14 Apr 2026 22:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/14/git-worktree-parallel-work/</guid><description>用 git worktree 避開 stash + branch switch 的切換成本，讓 hotfix、code review、長測試、多個 AI agent 並行跑在同一個 repo。整理常用指令、bare repo 模式，以及 2024–2026 新增的 relative paths 支援。</description><content:encoded>&lt;![CDATA[用 git worktree 避開 stash + branch switch 的切換成本，讓 hotfix、code review、長測試、多個 AI agent 並行跑在同一個 repo。整理常用指令、bare repo 模式，以及 2024–2026 新增的 relative paths 支援。<br/><img src="https://recca0120.github.io/2026/04/14/git-worktree-parallel-work/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/14/git-worktree-parallel-work/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/14/git-worktree-parallel-work/featured.jpg" medium="image"/><category>git</category><category>worktree</category><category>productivity</category><category>ai-agent</category><category>DevOps</category></item><item><title>2026 年用 Cloudflare Tunnel：不開 port、不買 IP，把本機服務推上公網</title><link>https://recca0120.github.io/2026/04/14/cloudflare-tunnel-2026/</link><pubDate>Tue, 14 Apr 2026 20:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/14/cloudflare-tunnel-2026/</guid><description>Cloudflare Tunnel 讓你從本機建立對外連線就能把 localhost 推上自己的網域，不需要開防火牆、不需要公網 IP。整理 2026 年最新的 remotely-managed tunnel 流程、Zero Trust Access 存取控制，以及和 ngrok、Tailscale Funnel 的取捨。</description><content:encoded>&lt;![CDATA[Cloudflare Tunnel 讓你從本機建立對外連線就能把 localhost 推上自己的網域，不需要開防火牆、不需要公網 IP。整理 2026 年最新的 remotely-managed tunnel 流程、Zero Trust Access 存取控制，以及和 ngrok、Tailscale Funnel 的取捨。<br/><img src="https://recca0120.github.io/2026/04/14/cloudflare-tunnel-2026/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/14/cloudflare-tunnel-2026/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/14/cloudflare-tunnel-2026/featured.jpg" medium="image"/><category>cloudflare</category><category>tunnel</category><category>zero-trust</category><category>networking</category><category>DevOps</category></item><item><title>掃了 95 天的 Claude Code 日誌，發現 Anthropic 還有第二波 cache TTL silent regression</title><link>https://recca0120.github.io/2026/04/14/claude-code-cache-ttl-audit/</link><pubDate>Tue, 14 Apr 2026 03:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/14/claude-code-cache-ttl-audit/</guid><description>社群在罵 Anthropic 3/6 偷改 cache TTL，但只看別人的 billing 不夠。我掃自己 95 天的 Claude Code 原生日誌，精準復現 3/6 那波 regression，並發現 4/9 起 sub-agent 100% 被降到 5m TTL—社群還沒人報告過的第二波。</description><content:encoded>&lt;![CDATA[社群在罵 Anthropic 3/6 偷改 cache TTL，但只看別人的 billing 不夠。我掃自己 95 天的 Claude Code 原生日誌，精準復現 3/6 那波 regression，並發現 4/9 起 sub-agent 100% 被降到 5m TTL—社群還沒人報告過的第二波。<br/><img src="https://recca0120.github.io/2026/04/14/claude-code-cache-ttl-audit/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/14/claude-code-cache-ttl-audit/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/14/claude-code-cache-ttl-audit/featured.jpg" medium="image"/><category>claude-code</category><category>prompt-caching</category><category>ai-agent</category><category>python</category><category>AI</category></item><item><title>Claude Code 的 session 開太久很浪費 token？一個多數人搞錯的成本模型</title><link>https://recca0120.github.io/2026/04/13/claude-code-session-cost-cache-misconception/</link><pubDate>Mon, 13 Apr 2026 18:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/13/claude-code-session-cost-cache-misconception/</guid><description>很多人以為 Claude Code session 開太久會浪費 token，於是頻繁 /clear。但因為 prompt caching 給 10% 的價格折扣，頻繁 clear 反而更貴。拆解真正影響成本的三個變數。</description><content:encoded>&lt;![CDATA[很多人以為 Claude Code session 開太久會浪費 token，於是頻繁 /clear。但因為 prompt caching 給 10% 的價格折扣，頻繁 clear 反而更貴。拆解真正影響成本的三個變數。<br/><img src="https://recca0120.github.io/2026/04/13/claude-code-session-cost-cache-misconception/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/13/claude-code-session-cost-cache-misconception/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/13/claude-code-session-cost-cache-misconception/featured.jpg" medium="image"/><category>claude-code</category><category>prompt-caching</category><category>ai-agent</category><category>cost-optimization</category><category>AI</category></item><item><title>chezmoi：用一份 dotfiles 打通 macOS、Linux、Windows 三個作業系統</title><link>https://recca0120.github.io/2026/04/13/chezmoi-dotfiles-management/</link><pubDate>Mon, 13 Apr 2026 15:30:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/13/chezmoi-dotfiles-management/</guid><description>chezmoi 是 Go 寫的 dotfiles 管理工具。用 Go template 處理機器差異、用 age 加密私密檔、用 run_onchange 自動裝套件。我跨三個作業系統同步 dotfiles 用的方法整理。</description><content:encoded>&lt;![CDATA[chezmoi 是 Go 寫的 dotfiles 管理工具。用 Go template 處理機器差異、用 age 加密私密檔、用 run_onchange 自動裝套件。我跨三個作業系統同步 dotfiles 用的方法整理。<br/><img src="https://recca0120.github.io/2026/04/13/chezmoi-dotfiles-management/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/13/chezmoi-dotfiles-management/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/13/chezmoi-dotfiles-management/featured.jpg" medium="image"/><category>chezmoi</category><category>dotfiles</category><category>age</category><category>Go</category><category>DevOps</category></item><item><title>zoxide：讓 cd 有記憶，打兩個字就跳到常用目錄</title><link>https://recca0120.github.io/2026/04/13/zoxide-smarter-cd/</link><pubDate>Mon, 13 Apr 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/13/zoxide-smarter-cd/</guid><description>zoxide 是一款 Rust 寫的 smart cd，用 frecency 演算法記住你常去的目錄。搭配 --cmd cd 直接取代原生 cd，再配 fzf 做互動式選擇，終端導航變成打兩個字就到。</description><content:encoded>&lt;![CDATA[zoxide 是一款 Rust 寫的 smart cd，用 frecency 演算法記住你常去的目錄。搭配 --cmd cd 直接取代原生 cd，再配 fzf 做互動式選擇，終端導航變成打兩個字就到。<br/><img src="https://recca0120.github.io/2026/04/13/zoxide-smarter-cd/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/13/zoxide-smarter-cd/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/13/zoxide-smarter-cd/featured.jpg" medium="image"/><category>zoxide</category><category>fish</category><category>terminal</category><category>productivity</category><category>DevOps</category></item><item><title>MemPalace：170 tokens 回憶所有對話，AI Agent 的長期記憶系統</title><link>https://recca0120.github.io/2026/04/08/mempalace-ai-memory-system/</link><pubDate>Wed, 08 Apr 2026 01:41:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/08/mempalace-ai-memory-system/</guid><description>MemPalace 是本地端的 AI 記憶系統，用記憶宮殿架構組織對話歷史，AAAK 壓縮格式達 30 倍壓縮率，啟動只需 170 tokens，LongMemEval 準確率 96.6%，完全離線。</description><content:encoded>&lt;![CDATA[MemPalace 是本地端的 AI 記憶系統，用記憶宮殿架構組織對話歷史，AAAK 壓縮格式達 30 倍壓縮率，啟動只需 170 tokens，LongMemEval 準確率 96.6%，完全離線。<br/><img src="https://recca0120.github.io/2026/04/08/mempalace-ai-memory-system/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/08/mempalace-ai-memory-system/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/08/mempalace-ai-memory-system/featured.jpg" medium="image"/><category>ai-agent</category><category>claude-code</category><category>MCP</category><category>Python</category><category>AI</category></item><item><title>NodeWarden：跑在 Cloudflare Workers 上的 Bitwarden，不用自架伺服器</title><link>https://recca0120.github.io/2026/04/07/nodewarden-bitwarden-cloudflare-workers/</link><pubDate>Tue, 07 Apr 2026 17:34:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/07/nodewarden-bitwarden-cloudflare-workers/</guid><description>NodeWarden 是第三方 Bitwarden 相容服務端，跑在 Cloudflare Workers 上，資料存 D1，附件放 R2 或 KV。不用維護 VPS，免費額度就能跑。</description><content:encoded>&lt;![CDATA[NodeWarden 是第三方 Bitwarden 相容服務端，跑在 Cloudflare Workers 上，資料存 D1，附件放 R2 或 KV。不用維護 VPS，免費額度就能跑。<br/><img src="https://recca0120.github.io/2026/04/07/nodewarden-bitwarden-cloudflare-workers/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/07/nodewarden-bitwarden-cloudflare-workers/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/07/nodewarden-bitwarden-cloudflare-workers/featured.jpg" medium="image"/><category>Cloudflare</category><category>Bitwarden</category><category>TypeScript</category><category>serverless</category><category>DevOps</category></item><item><title>claude-view：Claude Code 的 Mission Control，即時監控 session、成本、token 用量</title><link>https://recca0120.github.io/2026/04/07/claude-view-mission-control/</link><pubDate>Tue, 07 Apr 2026 17:30:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/07/claude-view-mission-control/</guid><description>claude-view 是 Rust 打造的 Claude Code 監控 dashboard，即時追蹤所有 session 的對話、成本、token 用量、sub-agent 樹狀圖，內建 85 個 MCP tools 和全文搜尋。</description><content:encoded>&lt;![CDATA[claude-view 是 Rust 打造的 Claude Code 監控 dashboard，即時追蹤所有 session 的對話、成本、token 用量、sub-agent 樹狀圖，內建 85 個 MCP tools 和全文搜尋。<br/><img src="https://recca0120.github.io/2026/04/07/claude-view-mission-control/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/07/claude-view-mission-control/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/07/claude-view-mission-control/featured.jpg" medium="image"/><category>claude-code</category><category>ai-agent</category><category>MCP</category><category>Rust</category><category>AI</category></item><item><title>bb-browser：不寫爬蟲、不用 API key，你的瀏覽器就是 API</title><link>https://recca0120.github.io/2026/04/07/bb-browser-your-browser-is-the-api/</link><pubDate>Tue, 07 Apr 2026 11:34:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/07/bb-browser-your-browser-is-the-api/</guid><description>bb-browser 直接在真實瀏覽器標籤裡跑程式碼，用你已登入的 cookie 發 fetch。36 個平台、103 條命令，還能當 MCP server 讓 Claude Code 存取整個網際網路。</description><content:encoded>&lt;![CDATA[bb-browser 直接在真實瀏覽器標籤裡跑程式碼，用你已登入的 cookie 發 fetch。36 個平台、103 條命令，還能當 MCP server 讓 Claude Code 存取整個網際網路。<br/><img src="https://recca0120.github.io/2026/04/07/bb-browser-your-browser-is-the-api/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/07/bb-browser-your-browser-is-the-api/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/07/bb-browser-your-browser-is-the-api/featured.jpg" medium="image"/><category>ai-agent</category><category>MCP</category><category>browser-automation</category><category>cli</category><category>AI</category></item><item><title>AionUi：一個介面管 12 種 AI Agent，免費開源的 Cowork 桌面應用</title><link>https://recca0120.github.io/2026/04/07/aionui-ai-cowork-app/</link><pubDate>Tue, 07 Apr 2026 11:03:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/07/aionui-ai-cowork-app/</guid><description>AionUi 是免費開源的 AI Cowork 桌面應用，用 Electron + React 打造，可在同一介面跑 Claude Code、Codex、Qwen Code 等 12+ 種 agent，支援 20+ 模型平台和排程任務。</description><content:encoded>&lt;![CDATA[AionUi 是免費開源的 AI Cowork 桌面應用，用 Electron + React 打造，可在同一介面跑 Claude Code、Codex、Qwen Code 等 12+ 種 agent，支援 20+ 模型平台和排程任務。<br/><img src="https://recca0120.github.io/2026/04/07/aionui-ai-cowork-app/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/07/aionui-ai-cowork-app/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/07/aionui-ai-cowork-app/featured.jpg" medium="image"/><category>ai-agent</category><category>electron</category><category>claude-code</category><category>MCP</category><category>AI</category></item><item><title>EmDash：用 Astro + Cloudflare 打造的全棧 TypeScript CMS，能取代 WordPress 嗎？</title><link>https://recca0120.github.io/2026/04/07/emdash-cms-astro-cloudflare/</link><pubDate>Tue, 07 Apr 2026 05:36:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/07/emdash-cms-astro-cloudflare/</guid><description>EmDash 是基於 Astro 和 Cloudflare 的全棧 TypeScript CMS，主打外掛沙箱隔離、Portable Text 結構化內容、多資料庫支援，定位為現代版 WordPress 替代方案。</description><content:encoded>&lt;![CDATA[EmDash 是基於 Astro 和 Cloudflare 的全棧 TypeScript CMS，主打外掛沙箱隔離、Portable Text 結構化內容、多資料庫支援，定位為現代版 WordPress 替代方案。<br/><img src="https://recca0120.github.io/2026/04/07/emdash-cms-astro-cloudflare/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/07/emdash-cms-astro-cloudflare/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/07/emdash-cms-astro-cloudflare/featured.jpg" medium="image"/><category>Astro</category><category>Cloudflare</category><category>TypeScript</category><category>CMS</category><category>Frontend</category></item><item><title>League Period：PHP 時間區間處理的瑞士刀</title><link>https://recca0120.github.io/2026/04/06/league-period/</link><pubDate>Mon, 06 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/06/league-period/</guid><description>用原生 DateTime 比較兩個時間區間是否重疊，要寫一堆 if 判斷。League Period 用不可變物件封裝時間區間，內建重疊、包含、間隙、交集等操作，一行搞定。</description><content:encoded>&lt;![CDATA[用原生 DateTime 比較兩個時間區間是否重疊，要寫一堆 if 判斷。League Period 用不可變物件封裝時間區間，內建重疊、包含、間隙、交集等操作，一行搞定。<br/><img src="https://recca0120.github.io/2026/04/06/league-period/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/06/league-period/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/06/league-period/featured.jpg" medium="image"/><category>php</category><category>period</category><category>datetime</category><category>league</category><category>scheduling</category><category>immutable</category><category>PHP</category></item><item><title>Strauss：Mozart 的繼承者，WordPress 外掛依賴隔離的更好選擇</title><link>https://recca0120.github.io/2026/04/05/strauss-composer-prefix/</link><pubDate>Sun, 05 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/05/strauss-composer-prefix/</guid><description>Strauss 是從 Mozart fork 出來的 Composer 依賴前綴工具，解決了 Mozart 幾個已知問題：支援 files autoloader、常數前綴、license 合規、零設定開箱即用。WordPress 外掛開發的現代選擇。</description><content:encoded>&lt;![CDATA[Strauss 是從 Mozart fork 出來的 Composer 依賴前綴工具，解決了 Mozart 幾個已知問題：支援 files autoloader、常數前綴、license 合規、零設定開箱即用。WordPress 外掛開發的現代選擇。<br/><img src="https://recca0120.github.io/2026/04/05/strauss-composer-prefix/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/05/strauss-composer-prefix/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/05/strauss-composer-prefix/cover.jpg" medium="image"/><category>strauss</category><category>composer</category><category>wordpress</category><category>php</category><category>namespace</category><category>PHP</category></item><item><title>Mozart：WordPress 外掛的 Composer 依賴隔離工具</title><link>https://recca0120.github.io/2026/04/04/mozart-composer-prefix/</link><pubDate>Sat, 04 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/04/mozart-composer-prefix/</guid><description>WordPress 外掛共用一個 PHP process，不同外掛用同一個 library 的不同版本就會炸。Mozart 把你的 vendor 依賴加上自訂 namespace 前綴，徹底隔離，不跟別人衝突。</description><content:encoded>&lt;![CDATA[WordPress 外掛共用一個 PHP process，不同外掛用同一個 library 的不同版本就會炸。Mozart 把你的 vendor 依賴加上自訂 namespace 前綴，徹底隔離，不跟別人衝突。<br/><img src="https://recca0120.github.io/2026/04/04/mozart-composer-prefix/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/04/mozart-composer-prefix/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/04/mozart-composer-prefix/cover.jpg" medium="image"/><category>mozart</category><category>composer</category><category>wordpress</category><category>php</category><category>namespace</category><category>PHP</category></item><item><title>pytest-mock：用 mocker fixture 讓 mock 更乾淨</title><link>https://recca0120.github.io/2026/04/03/pytest-mock/</link><pubDate>Fri, 03 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/03/pytest-mock/</guid><description>pytest-mock 提供 mocker fixture，把 unittest.mock.patch 整合進 pytest 的生命週期，不需要手動 start/stop，也不用 with 或 decorator。spy 保留原始行為同時追蹤呼叫，stub 測回調。</description><content:encoded>&lt;![CDATA[pytest-mock 提供 mocker fixture，把 unittest.mock.patch 整合進 pytest 的生命週期，不需要手動 start/stop，也不用 with 或 decorator。spy 保留原始行為同時追蹤呼叫，stub 測回調。<br/><img src="https://recca0120.github.io/2026/04/03/pytest-mock/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/03/pytest-mock/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/03/pytest-mock/cover.jpg" medium="image"/><category>pytest</category><category>mock</category><category>testing</category><category>python</category><category>spy</category><category>stub</category><category>unittest</category><category>Python</category><category>Testing</category></item><item><title>我用 Vibe Coding 做了一個全球開發者排行榜</title><link>https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/</link><pubDate>Fri, 03 Apr 2026 02:57:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/</guid><description>分享用 Astro + Svelte 5 + Three.js vibe coding 出 CodeAtlas 全球開發者排行榜的過程，從 GitHub API 資料收集到 3D 地球儀互動體驗。</description><content:encoded>&lt;![CDATA[分享用 Astro + Svelte 5 + Three.js vibe coding 出 CodeAtlas 全球開發者排行榜的過程，從 GitHub API 資料收集到 3D 地球儀互動體驗。<br/><img src="https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/03/codeatlas-vibe-coding-developer-ranking/featured.jpg" medium="image"/><category>Astro</category><category>Svelte</category><category>Three.js</category><category>GitHub API</category><category>Vibe Coding</category><category>Frontend</category></item><item><title>Typer：用 type hint 寫 CLI，不需要記 argparse API</title><link>https://recca0120.github.io/2026/04/02/typer-cli/</link><pubDate>Thu, 02 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/02/typer-cli/</guid><description>Typer 是 FastAPI 同作者寫的 CLI 框架，底層是 Click，但你不需要學 Click API。Type hint 就是 CLI 的規格，自動產生 help、shell 補全、子命令，把一堆 scripts 整理成一個工具。</description><content:encoded>&lt;![CDATA[Typer 是 FastAPI 同作者寫的 CLI 框架，底層是 Click，但你不需要學 Click API。Type hint 就是 CLI 的規格，自動產生 help、shell 補全、子命令，把一堆 scripts 整理成一個工具。<br/><img src="https://recca0120.github.io/2026/04/02/typer-cli/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/02/typer-cli/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/02/typer-cli/cover.jpg" medium="image"/><category>typer</category><category>cli</category><category>python</category><category>click</category><category>argparse</category><category>automation</category><category>Python</category></item><item><title>blinker：Python 的 signal 系統，讓模組之間不直接呼叫</title><link>https://recca0120.github.io/2026/04/01/blinker-signal/</link><pubDate>Wed, 01 Apr 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/04/01/blinker-signal/</guid><description>blinker 是 Python 輕量的 signal/event 系統，Flask 內部用它處理 request_started、request_finished 等事件。拿來做模組解耦、plugin 架構、避免 circular import 都很合適。</description><content:encoded>&lt;![CDATA[blinker 是 Python 輕量的 signal/event 系統，Flask 內部用它處理 request_started、request_finished 等事件。拿來做模組解耦、plugin 架構、避免 circular import 都很合適。<br/><img src="https://recca0120.github.io/2026/04/01/blinker-signal/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/04/01/blinker-signal/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/04/01/blinker-signal/cover.jpg" medium="image"/><category>blinker</category><category>signal</category><category>python</category><category>flask</category><category>event</category><category>Python</category></item><item><title>polyfactory：不要手刻測試假資料，讓 type hint 自動生成</title><link>https://recca0120.github.io/2026/03/31/polyfactory-test-data/</link><pubDate>Tue, 31 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/31/polyfactory-test-data/</guid><description>polyfactory 根據 Python type hint 自動產生測試資料，支援 dataclass、Pydantic v2、TypedDict、msgspec。搭配 pytest fixture 用，省掉每次寫測試都要 hardcode 一堆假資料的麻煩。</description><content:encoded>&lt;![CDATA[polyfactory 根據 Python type hint 自動產生測試資料，支援 dataclass、Pydantic v2、TypedDict、msgspec。搭配 pytest fixture 用，省掉每次寫測試都要 hardcode 一堆假資料的麻煩。<br/><img src="https://recca0120.github.io/2026/03/31/polyfactory-test-data/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/31/polyfactory-test-data/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/31/polyfactory-test-data/cover.jpg" medium="image"/><category>polyfactory</category><category>pytest</category><category>testing</category><category>python</category><category>pydantic</category><category>dataclass</category><category>factory</category><category>fixture</category><category>Python</category><category>Testing</category></item><item><title>pytest 入門：assert 就夠了，不需要記 self.assertEqual</title><link>https://recca0120.github.io/2026/03/30/pytest-getting-started/</link><pubDate>Mon, 30 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/30/pytest-getting-started/</guid><description>pytest 跟 unittest 最大的差距不是功能多寡，而是寫起來有多舒服。assert 直接用，失敗訊息自動展開，fixture 比 setUp/tearDown 靈活很多。</description><content:encoded>&lt;![CDATA[pytest 跟 unittest 最大的差距不是功能多寡，而是寫起來有多舒服。assert 直接用，失敗訊息自動展開，fixture 比 setUp/tearDown 靈活很多。<br/><img src="https://recca0120.github.io/2026/03/30/pytest-getting-started/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/30/pytest-getting-started/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/30/pytest-getting-started/cover.jpg" medium="image"/><category>pytest</category><category>testing</category><category>python</category><category>fixture</category><category>unit-testing</category><category>parametrize</category><category>Python</category><category>Testing</category></item><item><title>Docker Image 瘦身：先用 dive 找出問題，再對症下藥</title><link>https://recca0120.github.io/2026/03/29/docker-image-best-practices/</link><pubDate>Sun, 29 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/29/docker-image-best-practices/</guid><description>不要猜 Docker image 為什麼大，用 docker image history 和 dive 直接看。找到問題之後針對性修，比套用技巧清單更有效率。從 1.25GB 壓到 139MB 的實際過程。</description><content:encoded>&lt;![CDATA[不要猜 Docker image 為什麼大，用 docker image history 和 dive 直接看。找到問題之後針對性修，比套用技巧清單更有效率。從 1.25GB 壓到 139MB 的實際過程。<br/><img src="https://recca0120.github.io/2026/03/29/docker-image-best-practices/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/29/docker-image-best-practices/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/29/docker-image-best-practices/featured.jpg" medium="image"/><category>docker</category><category>dockerfile</category><category>devops</category><category>dive</category><category>optimization</category><category>container</category><category>multi-stage</category><category>DevOps</category></item><item><title>sslh：443 port 同時跑 HTTPS 和 SSH，一個 port 多個服務</title><link>https://recca0120.github.io/2026/03/28/sslh-port-multiplexer/</link><pubDate>Sat, 28 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/28/sslh-port-multiplexer/</guid><description>sslh 是 protocol multiplexer，讓 SSH、HTTPS、OpenVPN 共用同一個 port。最常見的用途是把 SSH 藏在 443，穿透只開放 HTTP/HTTPS 的嚴格防火牆。</description><content:encoded>&lt;![CDATA[sslh 是 protocol multiplexer，讓 SSH、HTTPS、OpenVPN 共用同一個 port。最常見的用途是把 SSH 藏在 443，穿透只開放 HTTP/HTTPS 的嚴格防火牆。<br/><img src="https://recca0120.github.io/2026/03/28/sslh-port-multiplexer/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/28/sslh-port-multiplexer/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/28/sslh-port-multiplexer/featured.jpg" medium="image"/><category>sslh</category><category>ssh</category><category>networking</category><category>security</category><category>linux</category><category>Tools</category></item><item><title>reverse_ssh：用標準 SSH 語法管理反向連線，穿透防火牆不需要 VPN</title><link>https://recca0120.github.io/2026/03/27/reverse-ssh/</link><pubDate>Fri, 27 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/27/reverse-ssh/</guid><description>reverse_ssh 把 SSH 反向：目標機主動連回 server，operator 再用原生 SSH 語法連進去。支援 HTTP/WebSocket/TLS 多種 transport 穿防火牆，SCP/SFTP/port forwarding 全部可用。</description><content:encoded>&lt;![CDATA[reverse_ssh 把 SSH 反向：目標機主動連回 server，operator 再用原生 SSH 語法連進去。支援 HTTP/WebSocket/TLS 多種 transport 穿防火牆，SCP/SFTP/port forwarding 全部可用。<br/><img src="https://recca0120.github.io/2026/03/27/reverse-ssh/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/27/reverse-ssh/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/27/reverse-ssh/featured.jpg" medium="image"/><category>ssh</category><category>reverse-shell</category><category>security</category><category>networking</category><category>golang</category><category>firewall</category><category>tunnel</category><category>Tools</category></item><item><title>yazi：Rust 寫的終端機檔案管理器，vim 鍵位、圖片預覽、Alacritty 解法</title><link>https://recca0120.github.io/2026/03/26/yazi-terminal-file-manager/</link><pubDate>Thu, 26 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/26/yazi-terminal-file-manager/</guid><description>yazi 是 Rust 寫的非同步終端機檔案管理器，vim 鍵位、圖片預覽、Lua 外掛、整合 fzf/zoxide。Alacritty 不支援圖片協議，macOS 用 Chafa，Linux 用 Überzug++ 搭配 X11/Wayland。</description><content:encoded>&lt;![CDATA[yazi 是 Rust 寫的非同步終端機檔案管理器，vim 鍵位、圖片預覽、Lua 外掛、整合 fzf/zoxide。Alacritty 不支援圖片協議，macOS 用 Chafa，Linux 用 Überzug++ 搭配 X11/Wayland。<br/><img src="https://recca0120.github.io/2026/03/26/yazi-terminal-file-manager/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/26/yazi-terminal-file-manager/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/26/yazi-terminal-file-manager/featured.jpg" medium="image"/><category>yazi</category><category>terminal</category><category>rust</category><category>file-manager</category><category>alacritty</category><category>Tools</category></item><item><title>reptyr：SSH 忘開 tmux 也救得回來，把 process 移進去</title><link>https://recca0120.github.io/2026/03/25/reptyr-reattach-terminal/</link><pubDate>Wed, 25 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/25/reptyr-reattach-terminal/</guid><description>reptyr 用 ptrace 把已在執行的 process 重新附加到新的 terminal。SSH 忘開 tmux 就跑了長時間任務，或者想把 background process 移進 screen，reptyr 一個指令搞定。</description><content:encoded>&lt;![CDATA[reptyr 用 ptrace 把已在執行的 process 重新附加到新的 terminal。SSH 忘開 tmux 就跑了長時間任務，或者想把 background process 移進 screen，reptyr 一個指令搞定。<br/><img src="https://recca0120.github.io/2026/03/25/reptyr-reattach-terminal/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/25/reptyr-reattach-terminal/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/25/reptyr-reattach-terminal/featured.jpg" medium="image"/><category>reptyr</category><category>linux</category><category>terminal</category><category>tmux</category><category>ssh</category><category>process</category><category>rescue</category><category>Tools</category></item><item><title>SQL NULL 的兩個陷阱：JOIN 撈不到資料、UNIQUE 限制失效</title><link>https://recca0120.github.io/2026/03/24/sql-null-join-unique/</link><pubDate>Tue, 24 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/24/sql-null-join-unique/</guid><description>各資料庫對 NULL 的解釋不同，衍生兩個常見問題：JOIN 欄位都是 NULL 時資料撈不到，以及 UNIQUE 欄位允許多筆 NULL 導致限制失效。MySQL、PostgreSQL、SQLite、SQL Server 行為各不同。</description><content:encoded>&lt;![CDATA[各資料庫對 NULL 的解釋不同，衍生兩個常見問題：JOIN 欄位都是 NULL 時資料撈不到，以及 UNIQUE 欄位允許多筆 NULL 導致限制失效。MySQL、PostgreSQL、SQLite、SQL Server 行為各不同。<br/><img src="https://recca0120.github.io/2026/03/24/sql-null-join-unique/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/24/sql-null-join-unique/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/24/sql-null-join-unique/featured.jpg" medium="image"/><category>sql</category><category/><category>mysql</category><category>postgresql</category><category>sqlite</category><category>database</category><category>Database</category></item><item><title>ky + p-limit：並發控制加上內建 retry，批次請求的完整解法</title><link>https://recca0120.github.io/2026/03/23/ky-concurrent-requests/</link><pubDate>Mon, 23 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/23/ky-concurrent-requests/</guid><description>ky 處理單一請求的 retry、timeout、auth hook，p-limit 控制同時執行的數量。兩個工具各司其職，批次打 100 個 API 不怕 429 也不怕網路抖動。</description><content:encoded>&lt;![CDATA[ky 處理單一請求的 retry、timeout、auth hook，p-limit 控制同時執行的數量。兩個工具各司其職，批次打 100 個 API 不怕 429 也不怕網路抖動。<br/><img src="https://recca0120.github.io/2026/03/23/ky-concurrent-requests/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/23/ky-concurrent-requests/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/23/ky-concurrent-requests/featured.jpg" medium="image"/><category>ky</category><category>typescript</category><category>javascript</category><category>concurrency</category><category>p-limit</category><category>api</category><category>rate-limiting</category><category>Frontend</category></item><item><title>fetch 並發控制：用 p-limit 限制同時請求數量，避免打爆 API</title><link>https://recca0120.github.io/2026/03/22/fetch-concurrent-requests/</link><pubDate>Sun, 22 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/22/fetch-concurrent-requests/</guid><description>Promise.all 同時發出 100 個請求，server 很容易回 429 或直接掛掉。用 p-limit 搭配 fetch 限制同時執行的請求數量，只需要一行設定，不需要自己寫 semaphore。</description><content:encoded>&lt;![CDATA[Promise.all 同時發出 100 個請求，server 很容易回 429 或直接掛掉。用 p-limit 搭配 fetch 限制同時執行的請求數量，只需要一行設定，不需要自己寫 semaphore。<br/><img src="https://recca0120.github.io/2026/03/22/fetch-concurrent-requests/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/22/fetch-concurrent-requests/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/22/fetch-concurrent-requests/featured.jpg" medium="image"/><category>fetch</category><category>javascript</category><category>typescript</category><category>concurrency</category><category>p-limit</category><category>api</category><category>rate-limiting</category><category>Frontend</category></item><item><title>auto-venv：Fish Shell 自動啟用 Python 虛擬環境，cd 進去就好</title><link>https://recca0120.github.io/2026/03/21/auto-venv-fish/</link><pubDate>Sat, 21 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/21/auto-venv-fish/</guid><description>auto-venv 是 Fish shell plugin，切換目錄時自動啟用和關閉 Python venv，支援 .venv、venv、.env、env 四種命名，用 git root 判斷範圍，相容 z、zoxide 等跳轉工具。</description><content:encoded>&lt;![CDATA[auto-venv 是 Fish shell plugin，切換目錄時自動啟用和關閉 Python venv，支援 .venv、venv、.env、env 四種命名，用 git root 判斷範圍，相容 z、zoxide 等跳轉工具。<br/><img src="https://recca0120.github.io/2026/03/21/auto-venv-fish/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/21/auto-venv-fish/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/21/auto-venv-fish/featured.jpg" medium="image"/><category>fish</category><category>python</category><category>venv</category><category>automation</category><category>shell</category><category>virtualenv</category><category>Python</category><category>Tools</category></item><item><title>Fish Shell + Laravel Valet：自動切換 PHP 版本和 Composer v1/v2</title><link>https://recca0120.github.io/2026/03/20/fish-valet-php-composer/</link><pubDate>Fri, 20 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/20/fish-valet-php-composer/</guid><description>用 fish shell alias 和 function 讓 php、composer、phpunit 自動跟著 Laravel Valet 的 PHP 版本走。解決舊專案需要 Composer v1 的問題，智慧判斷用 composer.phar 還是系統 composer。</description><content:encoded>&lt;![CDATA[用 fish shell alias 和 function 讓 php、composer、phpunit 自動跟著 Laravel Valet 的 PHP 版本走。解決舊專案需要 Composer v1 的問題，智慧判斷用 composer.phar 還是系統 composer。<br/><img src="https://recca0120.github.io/2026/03/20/fish-valet-php-composer/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/20/fish-valet-php-composer/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/20/fish-valet-php-composer/featured.jpg" medium="image"/><category>fish</category><category>laravel</category><category>valet</category><category>php</category><category>composer</category><category>shell</category><category>version-manager</category><category>PHP</category><category>Tools</category></item><item><title>Python Mock 踩坑：patch 要指向被測模組，不是原始模組</title><link>https://recca0120.github.io/2026/03/19/python-mock-imported-function/</link><pubDate>Thu, 19 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/19/python-mock-imported-function/</guid><description>Python unittest.mock.patch 的最常見誤區：mock utils.sum 但測試還是跑真的邏輯。原因是 from utils import sum 在被測模組建立了自己的 binding，patch 要指向被測模組的命名空間，不是函式的原始定義位置。</description><content:encoded>&lt;![CDATA[Python unittest.mock.patch 的最常見誤區：mock utils.sum 但測試還是跑真的邏輯。原因是 from utils import sum 在被測模組建立了自己的 binding，patch 要指向被測模組的命名空間，不是函式的原始定義位置。<br/><img src="https://recca0120.github.io/2026/03/19/python-mock-imported-function/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/19/python-mock-imported-function/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/19/python-mock-imported-function/featured.jpg" medium="image"/><category>python</category><category>testing</category><category>mock</category><category>unittest</category><category>patch</category><category>pytest</category><category>Python</category><category>Testing</category></item><item><title>PHP goto 不是邪魔：用來寫 retry 比 while 少一層縮排</title><link>https://recca0120.github.io/2026/03/18/php-goto-retry/</link><pubDate>Wed, 18 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/18/php-goto-retry/</guid><description>PHP goto 語法在 retry 邏輯上比 while loop 更直覺：少一層縮排、流程更平坦、語意更精確。只有明確要重試才跳回去，成功直接 return，失敗次數用完才 throw。</description><content:encoded>&lt;![CDATA[PHP goto 語法在 retry 邏輯上比 while loop 更直覺：少一層縮排、流程更平坦、語意更精確。只有明確要重試才跳回去，成功直接 return，失敗次數用完才 throw。<br/><img src="https://recca0120.github.io/2026/03/18/php-goto-retry/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/18/php-goto-retry/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/18/php-goto-retry/featured.jpg" medium="image"/><category>php</category><category>goto</category><category>retry</category><category>pattern</category><category>PHP</category></item><item><title>Laravel Subdomain Route 裡產出 Root Domain 網址：clone UrlGenerator</title><link>https://recca0120.github.io/2026/03/17/laravel-urlgenerator-subdomain/</link><pubDate>Tue, 17 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/17/laravel-urlgenerator-subdomain/</guid><description>Laravel 在 subdomain route 裡呼叫 url() 或 route()，產出的網址會帶 subdomain。用 clone app(UrlGenerator::class) 再 useOrigin() 就能在不影響全域的情況下產出 root domain 網址。</description><content:encoded>&lt;![CDATA[Laravel 在 subdomain route 裡呼叫 url() 或 route()，產出的網址會帶 subdomain。用 clone app(UrlGenerator::class) 再 useOrigin() 就能在不影響全域的情況下產出 root domain 網址。<br/><img src="https://recca0120.github.io/2026/03/17/laravel-urlgenerator-subdomain/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/17/laravel-urlgenerator-subdomain/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/17/laravel-urlgenerator-subdomain/featured.jpg" medium="image"/><category>laravel</category><category>php</category><category>testing</category><category>routing</category><category>Laravel</category><category>PHP</category></item><item><title>CLI-Anything：讓 AI Agent 操作任何軟體的通用橋樑</title><link>https://recca0120.github.io/2026/03/15/cli-anything-agent-native-cli/</link><pubDate>Sun, 15 Mar 2026 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/15/cli-anything-agent-native-cli/</guid><description>不用截圖、不用點擊——用一行指令讓 Claude Code 為任何軟體生成完整 CLI，AI Agent 從此直接呼叫 GIMP、Blender、LibreOffice</description><content:encoded>&lt;![CDATA[不用截圖、不用點擊——用一行指令讓 Claude Code 為任何軟體生成完整 CLI，AI Agent 從此直接呼叫 GIMP、Blender、LibreOffice<br/><img src="https://recca0120.github.io/2026/03/15/cli-anything-agent-native-cli/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/15/cli-anything-agent-native-cli/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/15/cli-anything-agent-native-cli/cover.jpg" medium="image"/><category>claude-code</category><category>ai-agent</category><category>cli</category><category>automation</category><category>AI</category></item><item><title>terraform test：內建的 Terraform 模組測試框架，不需要 Go</title><link>https://recca0120.github.io/2026/03/15/terraform-test/</link><pubDate>Sun, 15 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/15/terraform-test/</guid><description>Terraform 1.6 內建測試框架，用 .tftest.hcl 寫測試，支援 plan-only 單元測試、mock_provider 無需雲端憑證、expect_failures 驗證錯誤、apply 整合測試。不需要學 Go，直接用 HCL 寫。</description><content:encoded>&lt;![CDATA[Terraform 1.6 內建測試框架，用 .tftest.hcl 寫測試，支援 plan-only 單元測試、mock_provider 無需雲端憑證、expect_failures 驗證錯誤、apply 整合測試。不需要學 Go，直接用 HCL 寫。<br/><img src="https://recca0120.github.io/2026/03/15/terraform-test/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/15/terraform-test/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/15/terraform-test/featured.jpg" medium="image"/><category>terraform</category><category>testing</category><category>infrastructure-as-code</category><category>devops</category><category>DevOps</category></item><item><title>Terraform 入門：用程式碼管理雲端基礎設施，告別手動點擊</title><link>https://recca0120.github.io/2026/03/14/terraform-infrastructure-as-code/</link><pubDate>Sat, 14 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/14/terraform-infrastructure-as-code/</guid><description>Terraform 是 Infrastructure as Code 工具，用 HCL 宣告式語言管理 AWS、GCP、Cloudflare 等 3000+ provider。本文涵蓋安裝、HCL 語法、state 管理、module 架構、多環境策略與實際部署靜態網站範例。</description><content:encoded>&lt;![CDATA[Terraform 是 Infrastructure as Code 工具，用 HCL 宣告式語言管理 AWS、GCP、Cloudflare 等 3000+ provider。本文涵蓋安裝、HCL 語法、state 管理、module 架構、多環境策略與實際部署靜態網站範例。<br/><img src="https://recca0120.github.io/2026/03/14/terraform-infrastructure-as-code/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/14/terraform-infrastructure-as-code/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/14/terraform-infrastructure-as-code/featured.jpg" medium="image"/><category>terraform</category><category>infrastructure-as-code</category><category>aws</category><category>devops</category><category>cloudflare</category><category>DevOps</category></item><item><title>Terratest：Terraform 基礎設施的自動化整合測試</title><link>https://recca0120.github.io/2026/03/14/terratest-infrastructure-testing/</link><pubDate>Sat, 14 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/14/terratest-infrastructure-testing/</guid><description>Terratest 是一個用 Go 撰寫真實整合測試的函式庫，針對 Terraform 模組部署真實基礎設施、驗證其行為，然後銷毀。涵蓋 terraform、http_helper、aws、retry、test_structure 套件，附完整 Go 程式碼範例。</description><content:encoded>&lt;![CDATA[Terratest 是一個用 Go 撰寫真實整合測試的函式庫，針對 Terraform 模組部署真實基礎設施、驗證其行為，然後銷毀。涵蓋 terraform、http_helper、aws、retry、test_structure 套件，附完整 Go 程式碼範例。<br/><img src="https://recca0120.github.io/2026/03/14/terratest-infrastructure-testing/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/14/terratest-infrastructure-testing/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/14/terratest-infrastructure-testing/featured.jpg" medium="image"/><category>terratest</category><category>terraform</category><category>go</category><category>testing</category><category>aws</category><category>infrastructure</category><category>DevOps</category></item><item><title>Zustand：React 狀態管理，不需要 Provider，不需要 Reducer</title><link>https://recca0120.github.io/2026/03/13/zustand-react-state-management/</link><pubDate>Fri, 13 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/13/zustand-react-state-management/</guid><description>Zustand 是 1KB 的 React 狀態管理庫，一個 create() 搞定 state 和 action，不需要 Provider 包裝，selector 自動避免多餘 re-render，內建 persist、devtools、immer middleware。</description><content:encoded>&lt;![CDATA[Zustand 是 1KB 的 React 狀態管理庫，一個 create() 搞定 state 和 action，不需要 Provider 包裝，selector 自動避免多餘 re-render，內建 persist、devtools、immer middleware。<br/><img src="https://recca0120.github.io/2026/03/13/zustand-react-state-management/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/13/zustand-react-state-management/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/13/zustand-react-state-management/featured.jpg" medium="image"/><category>zustand</category><category>react</category><category>state-management</category><category>typescript</category><category>javascript</category><category>Frontend</category></item><item><title>ky：用 fetch 的人，不用再寫那些重複的樣板</title><link>https://recca0120.github.io/2026/03/12/ky-http-client/</link><pubDate>Thu, 12 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/12/ky-http-client/</guid><description>ky 是基於 fetch 的輕量 HTTP 客戶端，4KB gzip 零依賴，內建 retry 指數退避、timeout、hooks 攔截器、JSON 簡寫，比 axios 小 3 倍，比裸 fetch 少寫一半程式碼。</description><content:encoded>&lt;![CDATA[ky 是基於 fetch 的輕量 HTTP 客戶端，4KB gzip 零依賴，內建 retry 指數退避、timeout、hooks 攔截器、JSON 簡寫，比 axios 小 3 倍，比裸 fetch 少寫一半程式碼。<br/><img src="https://recca0120.github.io/2026/03/12/ky-http-client/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/12/ky-http-client/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/12/ky-http-client/featured.jpg" medium="image"/><category>ky</category><category>fetch</category><category>http</category><category>typescript</category><category>javascript</category><category>Frontend</category></item><item><title>Zod：TypeScript 最省力的資料驗證，Schema 即型別</title><link>https://recca0120.github.io/2026/03/11/zod-typescript-validation/</link><pubDate>Wed, 11 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/11/zod-typescript-validation/</guid><description>Zod 是 TypeScript-first 的 schema 驗證庫，定義一次 schema 自動推斷型別，不用重複寫 interface。支援 parse/safeParse、transform、refine、discriminated union，2kb gzip，零依賴。</description><content:encoded>&lt;![CDATA[Zod 是 TypeScript-first 的 schema 驗證庫，定義一次 schema 自動推斷型別，不用重複寫 interface。支援 parse/safeParse、transform、refine、discriminated union，2kb gzip，零依賴。<br/><img src="https://recca0120.github.io/2026/03/11/zod-typescript-validation/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/11/zod-typescript-validation/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/11/zod-typescript-validation/featured.jpg" medium="image"/><category>zod</category><category>typescript</category><category>validation</category><category>javascript</category><category>Frontend</category></item><item><title>用 Biome 取代 ESLint + Prettier：快 35 倍，一個工具搞定</title><link>https://recca0120.github.io/2026/03/10/biome-eslint-prettier-replacement/</link><pubDate>Tue, 10 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/10/biome-eslint-prettier-replacement/</guid><description>Biome 是用 Rust 寫的前端工具鏈，formatter 比 Prettier 快 35 倍，455 條 lint 規則涵蓋 ESLint + TypeScript ESLint，一個 biome.json 取代五個設定檔，支援自動遷移。</description><content:encoded>&lt;![CDATA[Biome 是用 Rust 寫的前端工具鏈，formatter 比 Prettier 快 35 倍，455 條 lint 規則涵蓋 ESLint + TypeScript ESLint，一個 biome.json 取代五個設定檔，支援自動遷移。<br/><img src="https://recca0120.github.io/2026/03/10/biome-eslint-prettier-replacement/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/10/biome-eslint-prettier-replacement/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/10/biome-eslint-prettier-replacement/featured.jpg" medium="image"/><category>biome</category><category>eslint</category><category>prettier</category><category>typescript</category><category>developer-tools</category><category>Tools</category><category>Frontend</category></item><item><title>Mole：一個指令清掉 macOS 95GB 的垃圾</title><link>https://recca0120.github.io/2026/03/09/mole-macos-cleaner/</link><pubDate>Mon, 09 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/09/mole-macos-cleaner/</guid><description>Mole 是 38K stars 的 macOS 清理工具，mo clean 深度清除快取、mo uninstall 連殘留一起刪、mo purge 清 node_modules，乾淨又安全，支援 dry-run 先預覽。</description><content:encoded>&lt;![CDATA[Mole 是 38K stars 的 macOS 清理工具，mo clean 深度清除快取、mo uninstall 連殘留一起刪、mo purge 清 node_modules，乾淨又安全，支援 dry-run 先預覽。<br/><img src="https://recca0120.github.io/2026/03/09/mole-macos-cleaner/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/09/mole-macos-cleaner/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/09/mole-macos-cleaner/cover.jpg" medium="image"/><category>macos</category><category>mole</category><category>developer-tools</category><category>productivity</category><category>Tools</category></item><item><title>OpenSpec：讓 AI Coding Assistant 照規格做事，不要亂寫</title><link>https://recca0120.github.io/2026/03/08/openspec-sdd/</link><pubDate>Sun, 08 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/08/openspec-sdd/</guid><description>OpenSpec 是 spec-driven development 框架，在 AI 寫程式之前先對齊需求。propose 產出規格文件，apply 按規格實作，archive 歸檔完成的變更。支援 Claude Code、Cursor、Copilot 等 30+ 工具。</description><content:encoded>&lt;![CDATA[OpenSpec 是 spec-driven development 框架，在 AI 寫程式之前先對齊需求。propose 產出規格文件，apply 按規格實作，archive 歸檔完成的變更。支援 Claude Code、Cursor、Copilot 等 30+ 工具。<br/><img src="https://recca0120.github.io/2026/03/08/openspec-sdd/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/08/openspec-sdd/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/08/openspec-sdd/cover.jpg" medium="image"/><category>openspec</category><category>ai</category><category>sdd</category><category>claude-code</category><category>cursor</category><category>developer-tools</category><category>workflow</category><category>Tools</category><category>DevOps</category></item><item><title>拋棄 Husky，用 Lefthook 讓 Git Hooks 快一倍</title><link>https://recca0120.github.io/2026/03/08/lefthook-git-hooks/</link><pubDate>Sun, 08 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/08/lefthook-git-hooks/</guid><description>用 Go 寫的 Lefthook 一個 YAML 搞定所有 Git hooks，平行執行省掉一半 commit 等待時間，支援 monorepo、跨語言、本地覆寫，比 Husky + lint-staged 更快更簡單。</description><content:encoded>&lt;![CDATA[用 Go 寫的 Lefthook 一個 YAML 搞定所有 Git hooks，平行執行省掉一半 commit 等待時間，支援 monorepo、跨語言、本地覆寫，比 Husky + lint-staged 更快更簡單。<br/><img src="https://recca0120.github.io/2026/03/08/lefthook-git-hooks/cover.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/08/lefthook-git-hooks/cover.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/08/lefthook-git-hooks/cover.jpg" medium="image"/><category>git</category><category>lefthook</category><category>husky</category><category>developer-tools</category><category>devops</category><category>Tools</category><category>DevOps</category></item><item><title>用 sql.js-httpvfs 在 GitHub Pages 上查詢 SQLite 資料庫</title><link>https://recca0120.github.io/2026/03/07/sql-js-httpvfs-static-hosting/</link><pubDate>Sat, 07 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/07/sql-js-httpvfs-static-hosting/</guid><description>介紹 sql.js-httpvfs，透過 HTTP Range Request 在靜態網站上直接查詢 SQLite，不需要下載整個資料庫檔案。</description><content:encoded>&lt;![CDATA[介紹 sql.js-httpvfs，透過 HTTP Range Request 在靜態網站上直接查詢 SQLite，不需要下載整個資料庫檔案。<br/><img src="https://recca0120.github.io/2026/03/07/sql-js-httpvfs-static-hosting/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/07/sql-js-httpvfs-static-hosting/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/07/sql-js-httpvfs-static-hosting/featured.jpg" medium="image"/><category>sql.js</category><category>SQLite</category><category>JavaScript</category><category>WebAssembly</category><category>Frontend</category><category>Database</category></item><item><title>瀏覽器端儲存方案比較：sql.js vs IndexedDB vs localStorage</title><link>https://recca0120.github.io/2026/03/06/browser-storage-comparison/</link><pubDate>Fri, 06 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/06/browser-storage-comparison/</guid><description>比較 sql.js、IndexedDB、localStorage 等瀏覽器端儲存方案的容量、查詢能力、效能與適用場景，附完整比較表格。</description><content:encoded>&lt;![CDATA[比較 sql.js、IndexedDB、localStorage 等瀏覽器端儲存方案的容量、查詢能力、效能與適用場景，附完整比較表格。<br/><img src="https://recca0120.github.io/2026/03/06/browser-storage-comparison/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/06/browser-storage-comparison/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/06/browser-storage-comparison/featured.jpg" medium="image"/><category>sql.js</category><category>SQLite</category><category>IndexedDB</category><category>JavaScript</category><category>Frontend</category><category>Database</category></item><item><title>sql.js + IndexedDB：打造離線優先的 Web App</title><link>https://recca0120.github.io/2026/03/05/sql-js-offline-web-app/</link><pubDate>Thu, 05 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/05/sql-js-offline-web-app/</guid><description>用 sql.js 搭配 IndexedDB 實現瀏覽器端 SQLite 資料持久化，打造不需後端也能離線運作的 Web 應用。</description><content:encoded>&lt;![CDATA[用 sql.js 搭配 IndexedDB 實現瀏覽器端 SQLite 資料持久化，打造不需後端也能離線運作的 Web 應用。<br/><img src="https://recca0120.github.io/2026/03/05/sql-js-offline-web-app/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/05/sql-js-offline-web-app/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/05/sql-js-offline-web-app/featured.jpg" medium="image"/><category>sql.js</category><category>SQLite</category><category>IndexedDB</category><category>JavaScript</category><category>Frontend</category><category>Database</category></item><item><title>用 sql.js 在瀏覽器跑 SQLite：安裝到 CRUD 完整教學</title><link>https://recca0120.github.io/2026/03/04/sql-js-browser-sqlite/</link><pubDate>Wed, 04 Mar 2026 09:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/04/sql-js-browser-sqlite/</guid><description>介紹 sql.js 套件，從安裝、初始化、CRUD 操作到匯入匯出 .db 檔案和 Web Worker 用法，在瀏覽器端直接使用 SQLite。</description><content:encoded>&lt;![CDATA[介紹 sql.js 套件，從安裝、初始化、CRUD 操作到匯入匯出 .db 檔案和 Web Worker 用法，在瀏覽器端直接使用 SQLite。<br/><img src="https://recca0120.github.io/2026/03/04/sql-js-browser-sqlite/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/04/sql-js-browser-sqlite/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/04/sql-js-browser-sqlite/featured.jpg" medium="image"/><category>sql.js</category><category>SQLite</category><category>JavaScript</category><category>WebAssembly</category><category>Frontend</category><category>Database</category></item><item><title>LiVue：用 Vue 3 取代 Alpine.js 的 Server-Driven 開發方式</title><link>https://recca0120.github.io/2026/03/03/livue-laravel-vue-server-driven/</link><pubDate>Tue, 03 Mar 2026 06:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/03/livue-laravel-vue-server-driven/</guid><description>介紹 LiVue 套件，在 Blade 模板裡直接用 Vue 3 directive，不用寫 API 也不用拆 .vue 檔，兼具 Livewire 和 Vue 生態系的優勢。</description><content:encoded>&lt;![CDATA[介紹 LiVue 套件，在 Blade 模板裡直接用 Vue 3 directive，不用寫 API 也不用拆 .vue 檔，兼具 Livewire 和 Vue 生態系的優勢。<br/><img src="https://recca0120.github.io/2026/03/03/livue-laravel-vue-server-driven/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/03/livue-laravel-vue-server-driven/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/03/livue-laravel-vue-server-driven/featured.jpg" medium="image"/><category>Laravel</category><category>Vue.js</category><category>Blade</category><category>Alpine.js</category><category>LiVue</category><category>Laravel</category><category>Frontend</category></item><item><title>msw-fetch-mock：用 undici 風格的 API 寫 Fetch Mock，6 套方案完整比較</title><link>https://recca0120.github.io/2026/03/03/msw-fetch-mock/</link><pubDate>Tue, 03 Mar 2026 06:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/03/msw-fetch-mock/</guid><description>比較 msw-fetch-mock、MSW、nock、fetch-mock 等 6 套 HTTP mock 方案的差異，說明 msw-fetch-mock 的架構優勢與適用場景。</description><content:encoded>&lt;![CDATA[比較 msw-fetch-mock、MSW、nock、fetch-mock 等 6 套 HTTP mock 方案的差異，說明 msw-fetch-mock 的架構優勢與適用場景。<br/><img src="https://recca0120.github.io/2026/03/03/msw-fetch-mock/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/03/msw-fetch-mock/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/03/msw-fetch-mock/featured.jpg" medium="image"/><category>Testing</category><category>MSW</category><category>JavaScript</category><category>Testing</category><category>Frontend</category></item><item><title>VS Code PHPUnit &amp; Pest Test Explorer 完整設定指南</title><link>https://recca0120.github.io/2026/03/03/vscode-phpunit/</link><pubDate>Tue, 03 Mar 2026 05:00:00 +0800</pubDate><guid>https://recca0120.github.io/2026/03/03/vscode-phpunit/</guid><description>PHPUnit &amp; Pest Test Explorer 完整設定：Docker Compose、Sail、SSH、Xdebug 除錯，支援 PHPUnit 7-12 與 Pest 1-4。</description><content:encoded>&lt;![CDATA[PHPUnit &amp; Pest Test Explorer 完整設定：Docker Compose、Sail、SSH、Xdebug 除錯，支援 PHPUnit 7-12 與 Pest 1-4。<br/><img src="https://recca0120.github.io/2026/03/03/vscode-phpunit/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2026/03/03/vscode-phpunit/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2026/03/03/vscode-phpunit/featured.jpg" medium="image"/><category>PHPUnit</category><category>Pest</category><category>VS Code</category><category>Docker</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>PHP 解析 CSV 指數格式：用 sprintf 動態精度轉回小數</title><link>https://recca0120.github.io/2024/07/16/php-csv/</link><pubDate>Tue, 16 Jul 2024 06:05:35 +0800</pubDate><guid>https://recca0120.github.io/2024/07/16/php-csv/</guid><description>Excel 匯出 CSV 時會把小數轉成 8.90E-05 這類指數格式，直接用 bcmul 會報錯。用 sprintf 搭配動態小數位數計算，可正確還原任意精度的浮點數。</description><content:encoded>&lt;![CDATA[Excel 匯出 CSV 時會把小數轉成 8.90E-05 這類指數格式，直接用 bcmul 會報錯。用 sprintf 搭配動態小數位數計算，可正確還原任意精度的浮點數。<br/><img src="https://recca0120.github.io/2024/07/16/php-csv/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2024/07/16/php-csv/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2024/07/16/php-csv/featured.jpg" medium="image"/><category>PHP</category><category>PHP</category></item><item><title>Blade 重複 HTML 區塊的 3 種消除方法</title><link>https://recca0120.github.io/2024/05/07/laravel-blade-template/</link><pubDate>Tue, 07 May 2024 07:47:27 +0800</pubDate><guid>https://recca0120.github.io/2024/05/07/laravel-blade-template/</guid><description>Blade template 出現重複的 HTML 區塊時，可選 @include、Component 或 ob_start 三種做法，各有適用場景，其中 ob_start 不需要額外建立檔案。</description><content:encoded>&lt;![CDATA[Blade template 出現重複的 HTML 區塊時，可選 @include、Component 或 ob_start 三種做法，各有適用場景，其中 ob_start 不需要額外建立檔案。<br/><img src="https://recca0120.github.io/2024/05/07/laravel-blade-template/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2024/05/07/laravel-blade-template/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2024/05/07/laravel-blade-template/featured.jpg" medium="image"/><category>Laravel</category><category>Blade</category><category>Laravel</category><category>PHP</category></item><item><title>用 Alpine.js 處理 Laravel AJAX 驗證錯誤</title><link>https://recca0120.github.io/2024/04/19/alpine-validate-errors-for-laravel/</link><pubDate>Fri, 19 Apr 2024 09:30:26 +0800</pubDate><guid>https://recca0120.github.io/2024/04/19/alpine-validate-errors-for-laravel/</guid><description>為 Alpine.js 寫 errors plugin 仿照 Laravel MessageBag API，讓 AJAX 表單 422 驗證錯誤像 @error directive 一樣顯示。</description><content:encoded>&lt;![CDATA[為 Alpine.js 寫 errors plugin 仿照 Laravel MessageBag API，讓 AJAX 表單 422 驗證錯誤像 @error directive 一樣顯示。<br/><img src="https://recca0120.github.io/2024/04/19/alpine-validate-errors-for-laravel/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2024/04/19/alpine-validate-errors-for-laravel/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2024/04/19/alpine-validate-errors-for-laravel/featured.jpg" medium="image"/><category>Alpine.js</category><category>Laravel</category><category>JavaScript</category><category>Validation</category><category>Frontend</category><category>Laravel</category></item><item><title>Laravel Vite 自訂 domain 載入失敗？設定 server.host 解決</title><link>https://recca0120.github.io/2023/03/20/laravel-vite-host/</link><pubDate>Mon, 20 Mar 2023 07:08:10 +0800</pubDate><guid>https://recca0120.github.io/2023/03/20/laravel-vite-host/</guid><description>Laravel Vite 預設指向 localhost，自訂 domain 時 asset 載入失敗。設定 server.host 即可解決。</description><content:encoded>&lt;![CDATA[Laravel Vite 預設指向 localhost，自訂 domain 時 asset 載入失敗。設定 server.host 即可解決。<br/><img src="https://recca0120.github.io/2023/03/20/laravel-vite-host/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/03/20/laravel-vite-host/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/03/20/laravel-vite-host/featured.jpg" medium="image"/><category>Laravel</category><category>Vite</category><category>Laravel</category><category>Frontend</category></item><item><title>用 Composer InstalledVersions 在程式中取得套件版本號</title><link>https://recca0120.github.io/2023/02/25/php-third-package-version/</link><pubDate>Sat, 25 Feb 2023 10:04:03 +0800</pubDate><guid>https://recca0120.github.io/2023/02/25/php-third-package-version/</guid><description>用 Composer 2 的 InstalledVersions::getVersion() 在執行期取得套件版本，無需解析 composer.lock。</description><content:encoded>&lt;![CDATA[用 Composer 2 的 InstalledVersions::getVersion() 在執行期取得套件版本，無需解析 composer.lock。<br/><img src="https://recca0120.github.io/2023/02/25/php-third-package-version/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/02/25/php-third-package-version/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/02/25/php-third-package-version/featured.jpg" medium="image"/><category>PHP</category><category>Composer</category><category>PHP</category></item><item><title>Laravel 10 Database Expression 解決跨資料庫 SQL 差異</title><link>https://recca0120.github.io/2023/02/20/laravel-10-database-expression/</link><pubDate>Mon, 20 Feb 2023 05:41:20 +0800</pubDate><guid>https://recca0120.github.io/2023/02/20/laravel-10-database-expression/</guid><description>MySQL 的 IF() 在 SQLite 會報錯。Laravel 10 Expression interface 依 Grammar 產生對應 SQL，將跨資料庫差異封裝進 class。</description><content:encoded>&lt;![CDATA[MySQL 的 IF() 在 SQLite 會報錯。Laravel 10 Expression interface 依 Grammar 產生對應 SQL，將跨資料庫差異封裝進 class。<br/><img src="https://recca0120.github.io/2023/02/20/laravel-10-database-expression/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/02/20/laravel-10-database-expression/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/02/20/laravel-10-database-expression/featured.jpg" medium="image"/><category>Laravel</category><category>MySQL</category><category>SQLite</category><category>Testing</category><category>Laravel</category><category>Database</category></item><item><title>用 sqliteCreateFunction 讓 SQLite 支援 MySQL 專屬函式</title><link>https://recca0120.github.io/2023/02/17/laravel-sqlite-function/</link><pubDate>Fri, 17 Feb 2023 03:27:51 +0800</pubDate><guid>https://recca0120.github.io/2023/02/17/laravel-sqlite-function/</guid><description>SQLite 測試遇到 MySQL 專屬 function 如 FIELD 會噴 no such function 錯誤，用 sqliteCreateFunction 自行補上即可。</description><content:encoded>&lt;![CDATA[SQLite 測試遇到 MySQL 專屬 function 如 FIELD 會噴 no such function 錯誤，用 sqliteCreateFunction 自行補上即可。<br/><img src="https://recca0120.github.io/2023/02/17/laravel-sqlite-function/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/02/17/laravel-sqlite-function/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/02/17/laravel-sqlite-function/featured.jpg" medium="image"/><category>Laravel</category><category>SQLite</category><category>Testing</category><category>Laravel</category><category>Database</category></item><item><title>Eloquent Macro：saveQuietly 後手動觸發特定 Event</title><link>https://recca0120.github.io/2023/01/20/eloquent-macro-fire-event/</link><pubDate>Fri, 20 Jan 2023 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2023/01/20/eloquent-macro-fire-event/</guid><description>用 saveQuietly 存資料不觸發任何事件，再透過 Builder macro 定義 fire 方法，選擇性地手動觸發 created 或其他 Eloquent event。</description><content:encoded>&lt;![CDATA[用 saveQuietly 存資料不觸發任何事件，再透過 Builder macro 定義 fire 方法，選擇性地手動觸發 created 或其他 Eloquent event。<br/><img src="https://recca0120.github.io/2023/01/20/eloquent-macro-fire-event/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/20/eloquent-macro-fire-event/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/20/eloquent-macro-fire-event/featured.jpg" medium="image"/><category>Laravel</category><category>Eloquent</category><category>Laravel</category><category>PHP</category></item><item><title>S3 Storage::exists 拋出 403 UnableToCheckFileExistence 的解法</title><link>https://recca0120.github.io/2023/01/19/leagueflysystemunabletocheckfileexistence/</link><pubDate>Thu, 19 Jan 2023 03:12:27 +0800</pubDate><guid>https://recca0120.github.io/2023/01/19/leagueflysystemunabletocheckfileexistence/</guid><description>IAM 缺少 s3:ListBucket 時，S3 對不存在的檔案回傳 403 而非 404，[Flysystem](https://flysystem.thephpleague.com) 因此拋出例外。補上權限後 exists() 即可正常回傳 false。</description><content:encoded>&lt;![CDATA[IAM 缺少 s3:ListBucket 時，S3 對不存在的檔案回傳 403 而非 404，[Flysystem](https://flysystem.thephpleague.com) 因此拋出例外。補上權限後 exists() 即可正常回傳 false。<br/><img src="https://recca0120.github.io/2023/01/19/leagueflysystemunabletocheckfileexistence/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/19/leagueflysystemunabletocheckfileexistence/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/19/leagueflysystemunabletocheckfileexistence/featured.jpg" medium="image"/><category>Laravel</category><category>Flysystem</category><category>AWS</category><category>Laravel</category><category>DevOps</category></item><item><title>修復 PHPUnit 出現 gRPC thread pool idle 警告的方法</title><link>https://recca0120.github.io/2023/01/18/waiting-for-thread-pool-to-idle-before-forking/</link><pubDate>Wed, 18 Jan 2023 15:37:51 +0800</pubDate><guid>https://recca0120.github.io/2023/01/18/waiting-for-thread-pool-to-idle-before-forking/</guid><description>PHPUnit 測試時不斷出現 gRPC "Waiting for thread pool to idle" 警告，原因是 gRPC 版本過高，降版到 1.49.0 即可消除。</description><content:encoded>&lt;![CDATA[PHPUnit 測試時不斷出現 gRPC "Waiting for thread pool to idle" 警告，原因是 gRPC 版本過高，降版到 1.49.0 即可消除。<br/><img src="https://recca0120.github.io/2023/01/18/waiting-for-thread-pool-to-idle-before-forking/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/18/waiting-for-thread-pool-to-idle-before-forking/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/18/waiting-for-thread-pool-to-idle-before-forking/featured.jpg" medium="image"/><category>PHP</category><category>PHPUnit</category><category>PHP</category><category>Testing</category></item><item><title>在 WSL2 安裝 Docker CE 取代 Docker Desktop</title><link>https://recca0120.github.io/2023/01/15/wsl2-install-docker-ce/</link><pubDate>Sun, 15 Jan 2023 10:00:00 +0800</pubDate><guid>https://recca0120.github.io/2023/01/15/wsl2-install-docker-ce/</guid><description>不用 Docker Desktop，直接在 WSL2 安裝 Docker CE 的完整步驟，包含加入 docker 群組免 sudo、啟用 systemd 讓 Docker 開機自動啟動。</description><content:encoded>&lt;![CDATA[不用 Docker Desktop，直接在 WSL2 安裝 Docker CE 的完整步驟，包含加入 docker 群組免 sudo、啟用 systemd 讓 Docker 開機自動啟動。<br/><img src="https://recca0120.github.io/2023/01/15/wsl2-install-docker-ce/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/15/wsl2-install-docker-ce/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/15/wsl2-install-docker-ce/featured.jpg" medium="image"/><category>Docker</category><category>WSL2</category><category>Linux</category><category>DevOps</category><category>Windows</category></item><item><title>Laravel Facade 底層如何透過 Container 取 instance</title><link>https://recca0120.github.io/2023/01/12/laravel-container-facade/</link><pubDate>Thu, 12 Jan 2023 05:34:12 +0800</pubDate><guid>https://recca0120.github.io/2023/01/12/laravel-container-facade/</guid><description>Facade 透過 getFacadeAccessor 從 Container 解析 instance 的機制，以及 bind 與 singleton 的差異。</description><content:encoded>&lt;![CDATA[Facade 透過 getFacadeAccessor 從 Container 解析 instance 的機制，以及 bind 與 singleton 的差異。<br/><img src="https://recca0120.github.io/2023/01/12/laravel-container-facade/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/12/laravel-container-facade/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/12/laravel-container-facade/featured.jpg" medium="image"/><category>Laravel</category><category>PHP</category><category>Laravel</category><category>PHP</category></item><item><title>PHPUnit 測試含 sleep() 的程式：2 種 mock 解法比較</title><link>https://recca0120.github.io/2023/01/06/phpunit-sleep/</link><pubDate>Fri, 06 Jan 2023 08:55:58 +0800</pubDate><guid>https://recca0120.github.io/2023/01/06/phpunit-sleep/</guid><description>程式裡有 sleep() 讓每次測試都要多等幾秒，可抽出 Clock class 用 Mockery spy 替換，或用 php-mock 直接 mock 內建函式，無需改動原始碼。</description><content:encoded>&lt;![CDATA[程式裡有 sleep() 讓每次測試都要多等幾秒，可抽出 Clock class 用 Mockery spy 替換，或用 php-mock 直接 mock 內建函式，無需改動原始碼。<br/><img src="https://recca0120.github.io/2023/01/06/phpunit-sleep/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/06/phpunit-sleep/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/06/phpunit-sleep/featured.jpg" medium="image"/><category>PHP</category><category>PHPUnit</category><category>Mockery</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>用 Laravel Container bind 解決第三方套件無法自動注入</title><link>https://recca0120.github.io/2023/01/06/how-to-use-laravel-container-to-register-third-party-package/</link><pubDate>Fri, 06 Jan 2023 01:46:18 +0800</pubDate><guid>https://recca0120.github.io/2023/01/06/how-to-use-laravel-container-to-register-third-party-package/</guid><description>第三方 class constructor 需要參數時 Container 無法自動 resolve。在 ServiceProvider 用 bind 註冊，即可在任何地方 type-hint 注入。</description><content:encoded>&lt;![CDATA[第三方 class constructor 需要參數時 Container 無法自動 resolve。在 ServiceProvider 用 bind 註冊，即可在任何地方 type-hint 注入。<br/><img src="https://recca0120.github.io/2023/01/06/how-to-use-laravel-container-to-register-third-party-package/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2023/01/06/how-to-use-laravel-container-to-register-third-party-package/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2023/01/06/how-to-use-laravel-container-to-register-third-party-package/featured.jpg" medium="image"/><category>Laravel</category><category>PHP</category><category>Composer</category><category>Laravel</category><category>PHP</category></item><item><title>macOS PHP 執行 HTTP 請求噴出 NSCFConstantString 錯誤的解法</title><link>https://recca0120.github.io/2022/12/26/php-objc86150-nscfconstantstring-initialize-may-have-been-in-progress-in-another-thread/</link><pubDate>Mon, 26 Dec 2022 08:41:00 +0800</pubDate><guid>https://recca0120.github.io/2022/12/26/php-objc86150-nscfconstantstring-initialize-may-have-been-in-progress-in-another-thread/</guid><description>macOS artisan tinker 用 Guzzle 噴 NSCFConstantString 錯誤，設環境變數關閉 fork safety 即可解決。</description><content:encoded>&lt;![CDATA[macOS artisan tinker 用 Guzzle 噴 NSCFConstantString 錯誤，設環境變數關閉 fork safety 即可解決。<br/><img src="https://recca0120.github.io/2022/12/26/php-objc86150-nscfconstantstring-initialize-may-have-been-in-progress-in-another-thread/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/26/php-objc86150-nscfconstantstring-initialize-may-have-been-in-progress-in-another-thread/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/26/php-objc86150-nscfconstantstring-initialize-may-have-been-in-progress-in-another-thread/featured.jpg" medium="image"/><category>PHP</category><category>Guzzle</category><category>macOS</category><category>PHP</category></item><item><title>用 PSR-7 message 格式讓 Python requests 結果無縫傳給 PHP</title><link>https://recca0120.github.io/2022/12/26/psr7-message-python-requests-php/</link><pubDate>Mon, 26 Dec 2022 06:34:26 +0800</pubDate><guid>https://recca0120.github.io/2022/12/26/psr7-message-python-requests-php/</guid><description>Python 爬蟲、PHP 後續處理的跨語言架構：讓 Python 輸出 PSR-7 message 字串，PHP 用 parseResponse 解析，免手動拆 header 和 body。</description><content:encoded>&lt;![CDATA[Python 爬蟲、PHP 後續處理的跨語言架構：讓 Python 輸出 PSR-7 message 字串，PHP 用 parseResponse 解析，免手動拆 header 和 body。<br/><img src="https://recca0120.github.io/2022/12/26/psr7-message-python-requests-php/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/26/psr7-message-python-requests-php/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/26/psr7-message-python-requests-php/featured.jpg" medium="image"/><category>PHP</category><category>PSR-7</category><category>Guzzle</category><category>Symfony</category><category>PHP</category></item><item><title>用 PDO::exec 載入 Schema 讓 Laravel 測試快 7 倍</title><link>https://recca0120.github.io/2022/12/19/laravel-load-schema-to-sqlite-in-memory-database/</link><pubDate>Mon, 19 Dec 2022 15:26:54 +0800</pubDate><guid>https://recca0120.github.io/2022/12/19/laravel-load-schema-to-sqlite-in-memory-database/</guid><description>透過 PDO::exec 把 schema dump 直接載入 SQLite in-memory database，跳過逐一跑 migration 的瓶頸，實測從 2 分 21 秒降到 18 秒。</description><content:encoded>&lt;![CDATA[透過 PDO::exec 把 schema dump 直接載入 SQLite in-memory database，跳過逐一跑 migration 的瓶頸，實測從 2 分 21 秒降到 18 秒。<br/><img src="https://recca0120.github.io/2022/12/19/laravel-load-schema-to-sqlite-in-memory-database/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/19/laravel-load-schema-to-sqlite-in-memory-database/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/19/laravel-load-schema-to-sqlite-in-memory-database/featured.jpg" medium="image"/><category>Laravel</category><category>SQLite</category><category>Testing</category><category>Migration</category><category>Laravel</category><category>Testing</category></item><item><title>修復 Laravel Migration 的 Unknown database type enum 錯誤</title><link>https://recca0120.github.io/2022/12/13/laravel-how-to-fix-unknown-database-type-enum-requested/</link><pubDate>Tue, 13 Dec 2022 06:16:19 +0800</pubDate><guid>https://recca0120.github.io/2022/12/13/laravel-how-to-fix-unknown-database-type-enum-requested/</guid><description>Doctrine DBAL 不認識 MySQL enum 型別導致 migration 失敗。介紹 Type::addType 與 registerDoctrineTypeMapping 兩種解法。</description><content:encoded>&lt;![CDATA[Doctrine DBAL 不認識 MySQL enum 型別導致 migration 失敗。介紹 Type::addType 與 registerDoctrineTypeMapping 兩種解法。<br/><img src="https://recca0120.github.io/2022/12/13/laravel-how-to-fix-unknown-database-type-enum-requested/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/13/laravel-how-to-fix-unknown-database-type-enum-requested/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/13/laravel-how-to-fix-unknown-database-type-enum-requested/featured.jpg" medium="image"/><category>Laravel</category><category>Migration</category><category>MySQL</category><category>Laravel</category><category>Database</category></item><item><title>PHPUnit for VSCode 3.0：整合 Testing API 與 Docker 支援</title><link>https://recca0120.github.io/2022/12/06/rebuilt-phpunit-for-vscode/</link><pubDate>Tue, 06 Dec 2022 05:57:57 +0800</pubDate><guid>https://recca0120.github.io/2022/12/06/rebuilt-phpunit-for-vscode/</guid><description>從頭重寫 PHPUnit for VSCode，整合 VS Code Testing API 讓測試結果即時顯示在側邊欄，新增 Docker 和 SSH 遠端執行環境支援。</description><content:encoded>&lt;![CDATA[從頭重寫 PHPUnit for VSCode，整合 VS Code Testing API 讓測試結果即時顯示在側邊欄，新增 Docker 和 SSH 遠端執行環境支援。<br/><img src="https://recca0120.github.io/2022/12/06/rebuilt-phpunit-for-vscode/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/06/rebuilt-phpunit-for-vscode/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/06/rebuilt-phpunit-for-vscode/featured.jpg" medium="image"/><category>PHPUnit</category><category>PHP</category><category>VS Code</category><category>Docker</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>Laravel Queue Job 在 Transaction 內讀到舊資料的解法</title><link>https://recca0120.github.io/2022/12/02/laravel-model-reads-old-data-when-using-transaction-and-queue/</link><pubDate>Fri, 02 Dec 2022 10:30:00 +0800</pubDate><guid>https://recca0120.github.io/2022/12/02/laravel-model-reads-old-data-when-using-transaction-and-queue/</guid><description>Transaction 內 dispatch 的 Job 因 SerializesModels 讀到舊資料，用 afterCommit() 讓 Job 等 commit 後再入列。</description><content:encoded>&lt;![CDATA[Transaction 內 dispatch 的 Job 因 SerializesModels 讀到舊資料，用 afterCommit() 讓 Job 等 commit 後再入列。<br/><img src="https://recca0120.github.io/2022/12/02/laravel-model-reads-old-data-when-using-transaction-and-queue/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/12/02/laravel-model-reads-old-data-when-using-transaction-and-queue/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/12/02/laravel-model-reads-old-data-when-using-transaction-and-queue/featured.jpg" medium="image"/><category>Laravel</category><category>Queue</category><category>Redis</category><category>Laravel</category><category>PHP</category></item><item><title>Laravel 測試只驗 View Data，不碰 HTML</title><link>https://recca0120.github.io/2022/11/29/laravel-testing-view-data/</link><pubDate>Tue, 29 Nov 2022 15:58:53 +0800</pubDate><guid>https://recca0120.github.io/2022/11/29/laravel-testing-view-data/</guid><description>複雜頁面測 HTML 很脆弱，改用 viewData() 直接驗 View 變數更穩定；比較 Model 時用 id 或 toArray() 而非直接 assertEquals 物件。</description><content:encoded>&lt;![CDATA[複雜頁面測 HTML 很脆弱，改用 viewData() 直接驗 View 變數更穩定；比較 Model 時用 id 或 toArray() 而非直接 assertEquals 物件。<br/><img src="https://recca0120.github.io/2022/11/29/laravel-testing-view-data/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/29/laravel-testing-view-data/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/29/laravel-testing-view-data/featured.jpg" medium="image"/><category>Laravel</category><category>Testing</category><category>Blade</category><category>Laravel</category><category>Testing</category></item><item><title>PHPUnit 測試 Closure：用 Mockery::spy 驗證 Callback</title><link>https://recca0120.github.io/2022/11/26/how-to-test-closure-in-phpunit/</link><pubDate>Sat, 26 Nov 2022 09:31:24 +0800</pubDate><guid>https://recca0120.github.io/2022/11/26/how-to-test-closure-in-phpunit/</guid><description>用 Mockery::spy 包裝 closure，解決在 closure 內寫 assertion 無法偵測「callback 根本沒被呼叫」的盲點，同時驗證參數和呼叫次數。</description><content:encoded>&lt;![CDATA[用 Mockery::spy 包裝 closure，解決在 closure 內寫 assertion 無法偵測「callback 根本沒被呼叫」的盲點，同時驗證參數和呼叫次數。<br/><img src="https://recca0120.github.io/2022/11/26/how-to-test-closure-in-phpunit/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/26/how-to-test-closure-in-phpunit/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/26/how-to-test-closure-in-phpunit/featured.jpg" medium="image"/><category>PHPUnit</category><category>Mockery</category><category>PHP</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>用 Mockery mock IteratorAggregate 讓 foreach 正常運作</title><link>https://recca0120.github.io/2022/11/25/how-to-use-mockery-to-mock-iteratoraggregate/</link><pubDate>Fri, 25 Nov 2022 03:41:39 +0800</pubDate><guid>https://recca0120.github.io/2022/11/25/how-to-use-mockery-to-mock-iteratoraggregate/</guid><description>直接 mock IteratorAggregate 物件會導致 foreach 無法迭代。讓 getIterator 回傳 ArrayObject，測試中即可正常迭代 mock 資料。</description><content:encoded>&lt;![CDATA[直接 mock IteratorAggregate 物件會導致 foreach 無法迭代。讓 getIterator 回傳 ArrayObject，測試中即可正常迭代 mock 資料。<br/><img src="https://recca0120.github.io/2022/11/25/how-to-use-mockery-to-mock-iteratoraggregate/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/25/how-to-use-mockery-to-mock-iteratoraggregate/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/25/how-to-use-mockery-to-mock-iteratoraggregate/featured.jpg" medium="image"/><category>Mockery</category><category>PHPUnit</category><category>PHP</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>Laravel min/max 驗證數字為何失效？加上 numeric 就解決了</title><link>https://recca0120.github.io/2022/11/23/laravel-validation-numeric/</link><pubDate>Wed, 23 Nov 2022 08:30:00 +0800</pubDate><guid>https://recca0120.github.io/2022/11/23/laravel-validation-numeric/</guid><description>Laravel 的 min/max 規則預設比較字串長度而非數值，導致 0 通過驗證。加上 numeric 規則後才會做數值比較，避免驗證邏輯出錯。</description><content:encoded>&lt;![CDATA[Laravel 的 min/max 規則預設比較字串長度而非數值，導致 0 通過驗證。加上 numeric 規則後才會做數值比較，避免驗證邏輯出錯。<br/><img src="https://recca0120.github.io/2022/11/23/laravel-validation-numeric/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/23/laravel-validation-numeric/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/23/laravel-validation-numeric/featured.jpg" medium="image"/><category>Laravel</category><category>Validation</category><category>PHP</category><category>Laravel</category><category>PHP</category></item><item><title>用 Mockery::capture 測試含亂數的方法，寫出確定的 assertion</title><link>https://recca0120.github.io/2022/11/22/mockery-capturing-arguments/</link><pubDate>Tue, 22 Nov 2022 14:21:07 +0800</pubDate><guid>https://recca0120.github.io/2022/11/22/mockery-capturing-arguments/</guid><description>方法內部有 random_int 導致回傳值不固定時，用 Mockery::capture 搭配 passthru 抓出中間值，就能算出確定的 expected 值來完成 assertion。</description><content:encoded>&lt;![CDATA[方法內部有 random_int 導致回傳值不固定時，用 Mockery::capture 搭配 passthru 抓出中間值，就能算出確定的 expected 值來完成 assertion。<br/><img src="https://recca0120.github.io/2022/11/22/mockery-capturing-arguments/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/22/mockery-capturing-arguments/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/22/mockery-capturing-arguments/featured.jpg" medium="image"/><category>PHP</category><category>Mockery</category><category>PHPUnit</category><category>Testing</category><category>Testing</category><category>PHP</category></item><item><title>免費取得 VMware Fusion Player 個人授權的申請步驟</title><link>https://recca0120.github.io/2022/11/21/vmware-fusion/</link><pubDate>Mon, 21 Nov 2022 13:34:53 +0800</pubDate><guid>https://recca0120.github.io/2022/11/21/vmware-fusion/</guid><description>VMware Fusion Player 提供個人免費授權，到官網建立帳號即可取得 License Key 和下載連結，附上表單驗證失敗時可用的備用填表資訊。</description><content:encoded>&lt;![CDATA[VMware Fusion Player 提供個人免費授權，到官網建立帳號即可取得 License Key 和下載連結，附上表單驗證失敗時可用的備用填表資訊。<br/><img src="https://recca0120.github.io/2022/11/21/vmware-fusion/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/21/vmware-fusion/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/21/vmware-fusion/featured.jpg" medium="image"/><category>VMware</category><category>macOS</category></item><item><title>修復 DBngin 找不到 mysql 指令的問題</title><link>https://recca0120.github.io/2022/11/08/dbngin-mysql-command/</link><pubDate>Tue, 08 Nov 2022 03:16:32 +0800</pubDate><guid>https://recca0120.github.io/2022/11/08/dbngin-mysql-command/</guid><description>DBngin 支援多版本 MySQL 但不會自動加 PATH，一行指令把最新版路徑寫進 .bashrc，終端機就能直接使用 mysql 指令。</description><content:encoded>&lt;![CDATA[DBngin 支援多版本 MySQL 但不會自動加 PATH，一行指令把最新版路徑寫進 .bashrc，終端機就能直接使用 mysql 指令。<br/><img src="https://recca0120.github.io/2022/11/08/dbngin-mysql-command/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2022/11/08/dbngin-mysql-command/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2022/11/08/dbngin-mysql-command/featured.jpg" medium="image"/><category>MySQL</category><category>macOS</category><category>Database</category><category>macOS</category></item><item><title>Laravel LazyCollection 搭 Generator 為何延遲載入失效</title><link>https://recca0120.github.io/2020/08/17/lazy-collection-with-generator-notice/</link><pubDate>Mon, 17 Aug 2020 12:46:29 +0800</pubDate><guid>https://recca0120.github.io/2020/08/17/lazy-collection-with-generator-notice/</guid><description>直接把 Generator 傳給 LazyCollection 會被 iterator_to_array 一次全部展開，失去延遲載入效果，必須用 Closure 包一層才能正確運作。</description><content:encoded>&lt;![CDATA[直接把 Generator 傳給 LazyCollection 會被 iterator_to_array 一次全部展開，失去延遲載入效果，必須用 Closure 包一層才能正確運作。<br/><img src="https://recca0120.github.io/2020/08/17/lazy-collection-with-generator-notice/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/08/17/lazy-collection-with-generator-notice/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/08/17/lazy-collection-with-generator-notice/featured.jpg" medium="image"/><category>Laravel</category><category>PHP</category><category>Laravel</category><category>PHP</category></item><item><title>GuzzleHttp getContents() 第二次回傳空字串的原因</title><link>https://recca0120.github.io/2020/08/05/guzzlehttp-psr7-response-get-contents-notice/</link><pubDate>Wed, 05 Aug 2020 13:12:49 +0800</pubDate><guid>https://recca0120.github.io/2020/08/05/guzzlehttp-psr7-response-get-contents-notice/</guid><description>GuzzleHttp Response 用 (string) 轉換每次都自動 seek(0)，但 getContents() 不會重置指標，第二次呼叫只拿到空字串，需手動 rewind。</description><content:encoded>&lt;![CDATA[GuzzleHttp Response 用 (string) 轉換每次都自動 seek(0)，但 getContents() 不會重置指標，第二次呼叫只拿到空字串，需手動 rewind。<br/><img src="https://recca0120.github.io/2020/08/05/guzzlehttp-psr7-response-get-contents-notice/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/08/05/guzzlehttp-psr7-response-get-contents-notice/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/08/05/guzzlehttp-psr7-response-get-contents-notice/featured.jpg" medium="image"/><category>Guzzle</category><category>PSR-7</category><category>PHP</category><category>PHP</category></item><item><title>用 bluetoothctl 讓樹莓派連上藍芽裝置</title><link>https://recca0120.github.io/2020/07/21/raspberry-pi-connect-bluetooth-using-command/</link><pubDate>Tue, 21 Jul 2020 14:48:18 +0800</pubDate><guid>https://recca0120.github.io/2020/07/21/raspberry-pi-connect-bluetooth-using-command/</guid><description>樹莓派沒有桌面環境時，用 bluetoothctl 的 scan、pair、trust、connect 指令完成藍芽配對，加上 trust 指令讓下次開機自動重新連線。</description><content:encoded>&lt;![CDATA[樹莓派沒有桌面環境時，用 bluetoothctl 的 scan、pair、trust、connect 指令完成藍芽配對，加上 trust 指令讓下次開機自動重新連線。<br/><img src="https://recca0120.github.io/2020/07/21/raspberry-pi-connect-bluetooth-using-command/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/07/21/raspberry-pi-connect-bluetooth-using-command/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/07/21/raspberry-pi-connect-bluetooth-using-command/featured.jpg" medium="image"/><category>Raspberry Pi</category><category>Bluetooth</category><category>Linux</category><category>DevOps</category></item><item><title>解決 Redis 6.0 升級後的 Connection Refused 錯誤</title><link>https://recca0120.github.io/2020/07/21/redis-connection-refused/</link><pubDate>Tue, 21 Jul 2020 14:40:39 +0800</pubDate><guid>https://recca0120.github.io/2020/07/21/redis-connection-refused/</guid><description>升級 Redis 到 6.0 後出現 Connection refused，原因是 bind 和 protected-mode 預設值變更，修改 redis.conf 這兩個設定即可恢復連線。</description><content:encoded>&lt;![CDATA[升級 Redis 到 6.0 後出現 Connection refused，原因是 bind 和 protected-mode 預設值變更，修改 redis.conf 這兩個設定即可恢復連線。<br/><img src="https://recca0120.github.io/2020/07/21/redis-connection-refused/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/07/21/redis-connection-refused/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/07/21/redis-connection-refused/featured.jpg" medium="image"/><category>Redis</category><category>Database</category><category>DevOps</category></item><item><title>用 Blade 引擎 Render Markdown 並嵌入動態語法</title><link>https://recca0120.github.io/2020/07/14/render-markdown-by-blade-template-engine/</link><pubDate>Tue, 14 Jul 2020 09:49:36 +0800</pubDate><guid>https://recca0120.github.io/2020/07/14/render-markdown-by-blade-template-engine/</guid><description>透過 Blade addExtension 讓 .md 檔支援 @include 和變數，再交給 CommonMark 轉成 HTML，適合需要動態內容的文件系統。</description><content:encoded>&lt;![CDATA[透過 Blade addExtension 讓 .md 檔支援 @include 和變數，再交給 CommonMark 轉成 HTML，適合需要動態內容的文件系統。<br/><img src="https://recca0120.github.io/2020/07/14/render-markdown-by-blade-template-engine/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/07/14/render-markdown-by-blade-template-engine/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/07/14/render-markdown-by-blade-template-engine/featured.jpg" medium="image"/><category>Laravel</category><category>Blade</category><category>Composer</category><category>CommonMark</category><category>Laravel</category><category>PHP</category></item><item><title>Windows 10 在 1GB 記憶體機器上的 5 項優化設定</title><link>https://recca0120.github.io/2020/07/06/windows-10-running-in-1g-ram/</link><pubDate>Mon, 06 Jul 2020 10:36:37 +0800</pubDate><guid>https://recca0120.github.io/2020/07/06/windows-10-running-in-1g-ram/</guid><description>1GB 記憶體跑 Windows 10，關閉 Memory Compression、Superfetch、Defender、Ndu 等服務，用 PowerShell 指令大幅降低記憶體佔用。</description><content:encoded>&lt;![CDATA[1GB 記憶體跑 Windows 10，關閉 Memory Compression、Superfetch、Defender、Ndu 等服務，用 PowerShell 指令大幅降低記憶體佔用。<br/><img src="https://recca0120.github.io/2020/07/06/windows-10-running-in-1g-ram/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/07/06/windows-10-running-in-1g-ram/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/07/06/windows-10-running-in-1g-ram/featured.jpg" medium="image"/><category>Windows</category><category>Windows</category></item><item><title>如何把 Windows 10 安裝到 AWS EC2 上</title><link>https://recca0120.github.io/2020/07/04/running-windows10-on-aws-ec2/</link><pubDate>Sat, 04 Jul 2020 11:34:00 +0800</pubDate><guid>https://recca0120.github.io/2020/07/04/running-windows10-on-aws-ec2/</guid><description>用 VirtualBox 建立 Windows 10 VHD 映像，透過 AWS VM Import 匯入成 AMI，再啟動 EC2 執行個體，完整涵蓋 IAM 角色設定和 RDP 連線步驟。</description><content:encoded>&lt;![CDATA[用 VirtualBox 建立 Windows 10 VHD 映像，透過 AWS VM Import 匯入成 AMI，再啟動 EC2 執行個體，完整涵蓋 IAM 角色設定和 RDP 連線步驟。<br/><img src="https://recca0120.github.io/2020/07/04/running-windows10-on-aws-ec2/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/07/04/running-windows10-on-aws-ec2/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/07/04/running-windows10-on-aws-ec2/featured.jpg" medium="image"/><category>Windows</category><category>AWS</category><category>Windows</category><category>DevOps</category></item><item><title>Laravel 測試跟隨 Redirect：用 followingRedirects() 驗證最終頁面</title><link>https://recca0120.github.io/2020/06/25/laravel-testing-what-happens-after-a-redirect/</link><pubDate>Thu, 25 Jun 2020 12:04:22 +0800</pubDate><guid>https://recca0120.github.io/2020/06/25/laravel-testing-what-happens-after-a-redirect/</guid><description>POST 後只拿到 302 看不到最終頁面，用 followingRedirects() 自動跟隨轉址，直接對目標頁面做 assertion，並說明與舊版的差異。</description><content:encoded>&lt;![CDATA[POST 後只拿到 302 看不到最終頁面，用 followingRedirects() 自動跟隨轉址，直接對目標頁面做 assertion，並說明與舊版的差異。<br/><img src="https://recca0120.github.io/2020/06/25/laravel-testing-what-happens-after-a-redirect/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/25/laravel-testing-what-happens-after-a-redirect/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/25/laravel-testing-what-happens-after-a-redirect/featured.jpg" medium="image"/><category>Laravel</category><category>PHPUnit</category><category>Testing</category><category>Laravel</category><category>Testing</category></item><item><title>Laravel Feature 測試中偽造 $_SERVER 變數的 2 種方法</title><link>https://recca0120.github.io/2020/06/23/laravel-testing-mock-server-variables/</link><pubDate>Tue, 23 Jun 2020 12:18:59 +0800</pubDate><guid>https://recca0120.github.io/2020/06/23/laravel-testing-mock-server-variables/</guid><description>測試 IP 限制等依賴 $_SERVER 的邏輯時，可透過 get/post 方法的 server 參數單次傳入，或用 withServerVariables() 在整個測試方法內全域生效。</description><content:encoded>&lt;![CDATA[測試 IP 限制等依賴 $_SERVER 的邏輯時，可透過 get/post 方法的 server 參數單次傳入，或用 withServerVariables() 在整個測試方法內全域生效。<br/><img src="https://recca0120.github.io/2020/06/23/laravel-testing-mock-server-variables/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/23/laravel-testing-mock-server-variables/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/23/laravel-testing-mock-server-variables/featured.jpg" medium="image"/><category>Laravel</category><category>Testing</category><category>PHPUnit</category><category>Laravel</category><category>Testing</category></item><item><title>解決 Laravel Eloquent 大量資料處理時的 Memory Leak</title><link>https://recca0120.github.io/2020/06/18/laravel-eloquent-memory-leak/</link><pubDate>Thu, 18 Jun 2020 14:00:42 +0800</pubDate><guid>https://recca0120.github.io/2020/06/18/laravel-eloquent-memory-leak/</guid><description>Eloquent 每個 Model 實例都會快取載入的 relation，大量迴圈時記憶體持續攀升，呼叫 setRelations([]) 可手動清除快取釋放記憶體。</description><content:encoded>&lt;![CDATA[Eloquent 每個 Model 實例都會快取載入的 relation，大量迴圈時記憶體持續攀升，呼叫 setRelations([]) 可手動清除快取釋放記憶體。<br/><img src="https://recca0120.github.io/2020/06/18/laravel-eloquent-memory-leak/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/18/laravel-eloquent-memory-leak/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/18/laravel-eloquent-memory-leak/featured.jpg" medium="image"/><category>Laravel</category><category>Eloquent</category><category>PHP</category><category>Laravel</category><category>PHP</category></item><item><title>修復 Redis TCP backlog 超過 somaxconn 的啟動警告</title><link>https://recca0120.github.io/2020/06/12/redis-the-tcp-backlog-setting-of-511-cannot-be-enforced-because-/proc/sys/net/core/somaxconn-is-set-to-the-lower-value-of-128/</link><pubDate>Fri, 12 Jun 2020 14:01:07 +0800</pubDate><guid>https://recca0120.github.io/2020/06/12/redis-the-tcp-backlog-setting-of-511-cannot-be-enforced-because-/proc/sys/net/core/somaxconn-is-set-to-the-lower-value-of-128/</guid><description>Redis 警告 TCP backlog 511 超過 somaxconn 128，連線量大時會掉線。調高 somaxconn 並寫入 sysctl.conf 讓設定永久生效。</description><content:encoded>&lt;![CDATA[Redis 警告 TCP backlog 511 超過 somaxconn 128，連線量大時會掉線。調高 somaxconn 並寫入 sysctl.conf 讓設定永久生效。<br/><img src="https://recca0120.github.io/2020/06/12/redis-the-tcp-backlog-setting-of-511-cannot-be-enforced-because-/proc/sys/net/core/somaxconn-is-set-to-the-lower-value-of-128/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/12/redis-the-tcp-backlog-setting-of-511-cannot-be-enforced-because-/proc/sys/net/core/somaxconn-is-set-to-the-lower-value-of-128/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/12/redis-the-tcp-backlog-setting-of-511-cannot-be-enforced-because-/proc/sys/net/core/somaxconn-is-set-to-the-lower-value-of-128/featured.jpg" medium="image"/><category>Redis</category><category>Linux</category><category>Database</category><category>DevOps</category></item><item><title>Laravel Migration 3 個常見踩坑與 SQLite 自動驗證</title><link>https://recca0120.github.io/2020/06/08/laravel-create-migration-precautions/</link><pubDate>Mon, 08 Jun 2020 22:18:27 +0800</pubDate><guid>https://recca0120.github.io/2020/06/08/laravel-create-migration-precautions/</guid><description>整理 migration rollback 常出錯的 3 種寫法：closure 混用、索引與欄位同時刪、多次 dropColumn，並用 SQLite in-memory 測試提前攔截。</description><content:encoded>&lt;![CDATA[整理 migration rollback 常出錯的 3 種寫法：closure 混用、索引與欄位同時刪、多次 dropColumn，並用 SQLite in-memory 測試提前攔截。<br/><img src="https://recca0120.github.io/2020/06/08/laravel-create-migration-precautions/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/08/laravel-create-migration-precautions/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/08/laravel-create-migration-precautions/featured.jpg" medium="image"/><category>Laravel</category><category>Migration</category><category>SQLite</category><category>Laravel</category><category>Database</category></item><item><title>PHP Curl Comodo SSL 憑證過期錯誤：用 GuzzleHttp verify 解決</title><link>https://recca0120.github.io/2020/06/01/how-to-fix-comodo-rsa-certification-authority-ssl-error-in-php/</link><pubDate>Mon, 01 Jun 2020 18:29:17 +0800</pubDate><guid>https://recca0120.github.io/2020/06/01/how-to-fix-comodo-rsa-certification-authority-ssl-error-in-php/</guid><description>PHP Curl 在 Linux 拋出 Comodo SSL 過期錯誤，原因是系統缺少 Root CA。用 GuzzleHttp verify 指定憑證路徑，可全域或針對單一 Service 設定。</description><content:encoded>&lt;![CDATA[PHP Curl 在 Linux 拋出 Comodo SSL 過期錯誤，原因是系統缺少 Root CA。用 GuzzleHttp verify 指定憑證路徑，可全域或針對單一 Service 設定。<br/><img src="https://recca0120.github.io/2020/06/01/how-to-fix-comodo-rsa-certification-authority-ssl-error-in-php/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/06/01/how-to-fix-comodo-rsa-certification-authority-ssl-error-in-php/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/06/01/how-to-fix-comodo-rsa-certification-authority-ssl-error-in-php/featured.jpg" medium="image"/><category>PHP</category><category>Guzzle</category><category>Laravel</category><category>PHP</category><category>Laravel</category></item><item><title>修復 adb server/client 版本不一致的問題</title><link>https://recca0120.github.io/2020/05/29/fix-adb-server-version-does-not-match-this-client-problem/</link><pubDate>Fri, 29 May 2020 19:01:07 +0800</pubDate><guid>https://recca0120.github.io/2020/05/29/fix-adb-server-version-does-not-match-this-client-problem/</guid><description>Mac 上 Homebrew 和 Android SDK 各裝一份 adb 導致版本衝突。移除 Homebrew 版並將 PATH 指向 SDK 即可解決。</description><content:encoded>&lt;![CDATA[Mac 上 Homebrew 和 Android SDK 各裝一份 adb 導致版本衝突。移除 Homebrew 版並將 PATH 指向 SDK 即可解決。<br/><img src="https://recca0120.github.io/2020/05/29/fix-adb-server-version-does-not-match-this-client-problem/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/05/29/fix-adb-server-version-does-not-match-this-client-problem/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/05/29/fix-adb-server-version-does-not-match-this-client-problem/featured.jpg" medium="image"/><category>Android</category><category>macOS</category><category>macOS</category></item><item><title>用 ValidationException::withMessages 手動丟出 Laravel 驗證錯誤</title><link>https://recca0120.github.io/2020/05/24/how-to-throw-a-validation-exception-manually-in-laravel/</link><pubDate>Sun, 24 May 2020 12:51:54 +0800</pubDate><guid>https://recca0120.github.io/2020/05/24/how-to-throw-a-validation-exception-manually-in-laravel/</guid><description>呼叫外部 API 出錯時，用 ValidationException::withMessages 把錯誤包裝成 Laravel 標準驗證格式，讓前端用同一套邏輯顯示錯誤訊息。</description><content:encoded>&lt;![CDATA[呼叫外部 API 出錯時，用 ValidationException::withMessages 把錯誤包裝成 Laravel 標準驗證格式，讓前端用同一套邏輯顯示錯誤訊息。<br/><img src="https://recca0120.github.io/2020/05/24/how-to-throw-a-validation-exception-manually-in-laravel/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2020/05/24/how-to-throw-a-validation-exception-manually-in-laravel/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2020/05/24/how-to-throw-a-validation-exception-manually-in-laravel/featured.jpg" medium="image"/><category>Laravel</category><category>Validation</category><category>PHP</category><category>Laravel</category><category>PHP</category></item><item><title>tester-phpunit：在 Atom 編輯器內直接跑 PHPUnit 測試</title><link>https://recca0120.github.io/2017/07/23/tester-phpunit/</link><pubDate>Sun, 23 Jul 2017 12:36:38 +0800</pubDate><guid>https://recca0120.github.io/2017/07/23/tester-phpunit/</guid><description>為 Atom 編輯器開發的 PHPUnit 套件 tester-phpunit，讓你不用離開編輯器就能執行 PHP 單元測試，告別手動切換終端機的麻煩。</description><content:encoded>&lt;![CDATA[為 Atom 編輯器開發的 PHPUnit 套件 tester-phpunit，讓你不用離開編輯器就能執行 PHP 單元測試，告別手動切換終端機的麻煩。<br/><img src="https://recca0120.github.io/2017/07/23/tester-phpunit/featured.jpg" alt="Featured image"/>]]></content:encoded><enclosure url="https://recca0120.github.io/2017/07/23/tester-phpunit/featured.jpg" type="image/png" length="0"/><media:content url="https://recca0120.github.io/2017/07/23/tester-phpunit/featured.jpg" medium="image"/><category>PHPUnit</category><category>PHP</category><category>Testing</category><category>Atom</category><category>Testing</category><category>PHP</category></item></channel></rss>