PageSourceSearch

https://hsb.horse/blog/

html hsb.horse collected 2026-10-01 16:22:48 UTC 68,583 bytes, 247 lines download raw bytes

1<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"><meta name="theme-color" content="#7B8CFF"><link rel="manifest" href="/brand/manifest.ja.webmanifest"><link rel="icon" href="/favicon.ico" sizes="32x32"><link rel="apple-touch-icon" href="/brand/apple-icon.png"><title>ブログ | hsb.horse</title><meta name="description" content="技術メモや開発の知見をまとめた記事一覧です。"><meta name="robots" content="index,follow,max-image-preview:large"><meta name="googlebot" content="index,follow,max-image-preview:large"><link rel="canonical" href="https://hsb.horse/blog/"><link rel="alternate" type="application/rss+xml" title="hsb.horse RSS" href="https://hsb.horse/rss.xml"><link rel="alternate" hreflang="ja" href="https://hsb.horse/blog/"><link rel="alternate" hreflang="en" href="https://hsb.horse/en/blog/"><link rel="alternate" hreflang="ko" href="https://hsb.horse/ko/blog/"><link rel="alternate" hreflang="x-default" href="https://hsb.horse/blog/"><meta property="og:type" content="website"><meta property="og:title" content="ブログ | hsb.horse"><meta property="og:description" content="技術メモや開発の知見をまとめた記事一覧です。"><meta property="og:url" content="https://hsb.horse/blog/"><meta property="og:site_name" content="hsb.horse"><meta property="og:locale" content="ja_JP"><meta property="og:locale:alternate" content="en_US"><meta property="og:locale:alternate" content="ko_KR"><meta property="og:image" content="https://hsb.horse/brand/og.jpg"><meta property="og:image:secure_url" content="https://hsb.horse/brand/og.jpg"><meta property="og:image:type" content="image/jpeg"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta property="og:image:alt" content="ブログ | hsb.horse"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="ブログ | hsb.horse"><meta name="twitter:description" content="技術メモや開発の知見をまとめた記事一覧です。"><meta name="twitter:image" content="https://hsb.horse/brand/og.jpg"><meta name="twitter:image:alt" content="ブログ | hsb.horse">
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"hsb.horse","url":"https://hsb.horse","potentialAction":{"@type":"SearchAction","target":"https://hsb.horse/search?q={search_term_string}","query-input":"required name=search_term_string"}}</script>
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"Person","name":"馬じぇべ","alternateName":"mktbsh","description":"Software Engineer in Tokyo, Japan.","url":"https://hsb.horse","sameAs":["https://github.com/mktbsh","https://x.com/hsb_horse","https://instagram.com/hsbhorse","https://bsky.app/profile/hsb.horse","https://note.com/hsb_horse","https://www.youtube.com/@hsb_horse"]}</script>
1<script>
2if (window.location.hostname === 'hsb.horse') {
3  window.dataLayer = window.dataLayer || [];
4}
5</script>
vendor: 85 bytes, line 5
5<script type="text/partytown" async src="https://www.googletagmanager.com/gtag/js?id=
5G-WZ3RT34EZZ
vendor: 11 bytes, line 5
5"></script>
5<script type="text/partytown">
6if (window.location.hostname === 'hsb.horse') {
7  
vendor: 133 bytes, lines 7-10
7window.dataLayer = window.dataLayer || [];
8  function gtag(){dataLayer.push(arguments);}
9  gtag('js', new Date());
10  gtag('config', '
10G-WZ3RT34EZZ
vendor: 4 bytes, line 10
10');
11}
12</script>
12<script type="application/ld+json">{"@context":"https://schema.org","@type":"CollectionPage","name":"ブログ","description":"技術メモや開発の知見をまとめた記事一覧です。","url":"https://hsb.horse/blog/","inLanguage":"ja","isPartOf":{"@type":"WebSite","name":"hsb.horse","url":"https://hsb.horse"}}</script>
12<link rel="stylesheet" href="/_astro/Layout.C_Dl0_B8.css">
12<script type="module" src="/_astro/page.CmFj6_gu.js"></script>
12<script>!(function(w,p,f,c){if(!window.crossOriginIsolated && !navigator.serviceWorker) return;c=w[p]=Object.assign(w[p]||{},{"lib":"/~partytown/","debug":false});c[f]=(c[f]||[]).concat(["dataLayer.push"])})(window,'partytown','forward');/* Partytown 0.13.2 - MIT QwikDev */
13const t={preserveBehavior:!1},e=e=>{if("string"==typeof e)return[e,t];const[n,r=t]=e;return[n,{...t,...r}]},n=Object.freeze((t=>{const e=new Set;let n=[];do{Object.getOwnPropertyNames(n).forEach((t=>{"function"==typeof n[t]&&e.add(t)}))}while((n=Object.getPrototypeOf(n))!==Object.prototype);return Array.from(e)})());!function(t,r,o,i,a,s,c,l,d,p,u=t,f){function h(){f||(f=1,"/"==(c=(s.lib||"/~partytown/")+(s.debug?"debug/":""))[0]&&(d=r.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(l=setTimeout(v,(null==s?void 0:s.fallbackTimeout)||1e4),r.addEventListener("pt0",w),a?y(1):o.serviceWorker?o.serviceWorker.register(c+(s.swPath||"partytown-sw.js"),{scope:c}).then((function(t){t.active?y():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&y()}))}),console.error):v())))}function y(e){p=r.createElement(e?"script":"iframe"),t._pttab=Date.now(),e||(p.style.display="block",p.style.width="0",p.style.height="0",p.style.border="0",p.style.visibility="hidden",p.setAttribute("aria-hidden",!0)),p.src=c+"partytown-"+(e?"atomics.js?v=0.13.2":"sandbox-sw.html?"+t._pttab),r.querySelector(s.sandboxParent||"body").appendChild(p)}function v(n,o){for(w(),i==t&&(s.forward||[]).map((function(n){const[r]=e(n);delete t[r.split(".")[0]]})),n=0;n<d.length;n++)(o=r.createElement("script")).innerHTML=d[n].innerHTML,o.nonce=s.nonce,r.head.appendChild(o);p&&p.parentNode.removeChild(p)}function w(){clearTimeout(l)}s=t.partytown||{},i==t&&(s.forward||[]).map((function(r){const[o,{preserveBehavior:i}]=e(r);u=t,o.split(".").map((function(e,r,o){var a;u=u[o[r]]=r+1<o.length?u[o[r]]||(a=o[r+1],n.includes(a)?[]:{}):(()=>{let e=null;if(i){const{methodOrProperty:n,thisObject:r}=((t,e)=>{let n=t;for(let t=0;t<e.length-1;t+=1)n=n[e[t]];return{thisObject:n,methodOrProperty:e.length>0?n[e[e.length-1]]:void 0}})(t,o);"function"==typeof n&&(e=(...t)=>n.apply(r,...t))}return function(){let n;return e&&(n=e(arguments)),(t._ptf=t._ptf||[]).push(o,arguments),n}})()}))})),"complete"==r.readyState?h():(t.addEventListener("DOMContentLoaded",h),t.addEventListener("load",h))}(window,document,navigator,top,window.crossOriginIsolated);;(e=>{e.addEventListener("astro:before-swap",e=>{let r=document.body.querySelector("iframe[src*='/~partytown/']");if(r)e.newDocument.body.append(r)})})(document);</script>
13</head><body class="ds-body"><div class="sticky top-0 w-full z-50"><header class="ds-header-bg h-25 flex w-full flex-col border-b border-white/10 text-white"><!-- Upper Header: 52px --><div class="h-13 flex items-center justify-between px-4"><!-- Left: Site Name --><a href="/" class="flex items-center gap-2 text-xl font-bold tracking-tight"><img src="/_astro/logo.BfjYuCVe_2m1qjO.webp" alt="logo" fetchpriority="high" loading="eager" decoding="async" width="128" height="128" class="size-10 object-cover">hsb.horse</a><!-- Right: Search Icon & User Avatar --><div class="flex items-center gap-4"><a href="/search/" aria-label="検索" class="ds-icon-button"><svg width="20" height="20" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg></a><!-- Language Switcher Dropdown --><div class="relative"><button type="button" id="lang-menu-btn" aria-label="言語を選択" class="ds-nav-button flex items-center gap-1"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Language</title><circle cx="12" cy="12" r="10"></circle><line x1="2" x2="22" y1="12" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg><span class="uppercase">ja</span><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mt-0.5"><title>Dropdown</title><path d="m6 9 6 6 6-6"></path></svg></button><!-- Dropdown Menu --><div id="lang-menu" class="ds-menu-surface absolute right-0 top-full z-50 mt-1 hidden w-32 origin-top-right transform transition-all duration-200"><a href="/blog/" class="ds-menu-item text-blue-600 font-medium bg-blue-50/50">日本語</a><a href="/en/blog/" class="ds-menu-item text-gray-700">英語</a><a href="/ko/blog/" class="ds-menu-item text-gray-700">韓国語</a></div></div><div class="relative"><button type="button" id="user-menu-btn" aria-haspopup="menu" aria-expanded="false" aria-controls="user-menu-menu" class="rounded-full overflow-hidden size-8 bg-gray-200 border border-gray-300 cursor-pointer" aria-label="ユーザーメニュー"><img src="/_astro/avatar.3IxvIyEM_Z2r67Eu.webp" alt="avatar" decoding="async" loading="lazy" width="150" height="150" class="size-full object-cover"></button><div id="user-menu-menu" role="menu" class="ds-menu-surface fixed min-w-40 hidden z-50 origin-top-left transform transition-all duration-200 w-44"><a href="/about/me/" role="menuitem" class="ds-menu-item">About</a><div class="my-1 border-t border-gray-100"></div><a href="https://github.com/mktbsh" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">GitHub</a><a href="https://x.com/hsb_horse" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">X</a><a href="https://instagram.com/hsbhorse" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">Instagram</a><a href="https://bsky.app/profile/hsb.horse" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">Bluesky</a><a href="https://note.com/hsb_horse" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">Note</a><a href="https://www.youtube.com/@hsb_horse" target="_blank" rel="noopener noreferrer" role="menuitem" class="ds-menu-item">YouTube</a></div></div>
13<script>(function(){const buttonId = "user-menu-btn";
14const menuId = "user-menu-menu";
15const align = "right";
16const offsetY = 8;
17
18const menuBtn = document.getElementById(buttonId);
19const menu = document.getElementById(menuId);
20
21const closeMenu = () => {
22  if (!menuBtn || !menu) return;
23  menu.classList.add('hidden');
24  menuBtn.setAttribute('aria-expanded', 'false');
25};
26
27const positionMenu = () => {
28  if (!menuBtn || !menu) return;
29  const wasHidden = menu.classList.contains('hidden');
30  if (wasHidden) {
31    menu.classList.remove('hidden');
32    menu.style.visibility = 'hidden';
33  }
34
35  const rect = menuBtn.getBoundingClientRect();
36  const menuRect = menu.getBoundingClientRect();
37  let left = rect.left;
38
39  if (align === 'right') {
40    left = rect.right - menuRect.width;
41  }
42
43  left = Math.max(8, Math.min(left, window.innerWidth - menuRect.width - 8));
44  let top = rect.bottom + offsetY;
45
46  if (top + menuRect.height > window.innerHeight - 8) {
47    top = Math.max(8, rect.top - menuRect.height - offsetY);
48  }
49
50  menu.style.left = `${Math.round(left)}px`;
51  menu.style.top = `${Math.round(top)}px`;
52
53  if (wasHidden) {
54    menu.classList.add('hidden');
55    menu.style.visibility = '';
56  }
57};
58
59if (menuBtn && menu) {
60  menuBtn.addEventListener('click', (event) => {
61    event.stopPropagation();
62    const isHidden = menu.classList.contains('hidden');
63    if (isHidden) {
64      menu.classList.remove('hidden');
65      positionMenu();
66      menuBtn.setAttribute('aria-expanded', 'true');
67      return;
68    }
69    closeMenu();
70  });
71
72  menu.addEventListener('click', (event) => {
73    const target = event.target;
74    if (target instanceof HTMLElement && target.closest('a')) {
75      closeMenu();
76    }
77  });
78
79  document.addEventListener('click', (event) => {
80    const target = event.target;
81    if (!(target instanceof Node)) return;
82    if (!menuBtn.contains(target) && !menu.contains(target)) {
83      closeMenu();
84    }
85  });
86
87  document.addEventListener('keydown', (event) => {
88    if (event.key === 'Escape') {
89      closeMenu();
90    }
91  });
92
93  window.addEventListener('resize', () => {
94    if (!menu.classList.contains('hidden')) {
95      positionMenu();
96    }
97  });
98  window.addEventListener('scroll', closeMenu, true);
99}
100})();</script>
100</div></div><!-- Lower Header: 48px --><div class="h-12 flex items-center px-4 overflow-hidden gap-4"><!-- Mobile Sidebar Toggle (Visible relative to mobile/desktop, logic needed) --><button type="button" id="nav-toggle-btn" aria-label="ナビゲーションを切り替え" class="ds-nav-button hidden"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Menu</title><line x1="4" x2="20" y1="12" y2="12"></line><line x1="4" x2="20" y1="6" y2="6"></line><line x1="4" x2="20" y1="18" y2="18"></line></svg></button><!-- Navigation --><nav class="flex-1 overflow-x-auto overflow-y-visible no-scrollbar"><ul class="flex items-center gap-6 whitespace-nowrap text-sm font-medium text-slate-300 [&amp;_li]:text-white"><li><a href="/">ホーム</a></li><li><a href="/about/me/">About</a></li><li><a href="/blog/">ブログ</a></li><li><a href="/snippets/">スニペット</a></li><li><a href="/glossary/">用語集</a></li><li><a href="/tools/">ツール</a></li><li><a href="/oss/">OSS</a></li><li><a href="/changelog/">更新履歴</a></li><li><div class="relative"><button type="button" id="tags-nav-menu-btn" aria-haspopup="menu" aria-expanded="false" aria-controls="tags-nav-menu-menu" class="inline-flex items-center gap-1 cursor-pointer transition-colors" aria-label="タグ"><span>タグ</span><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mt-0.5" aria-hidden="true"><title>タグ menu</title><path d="m6 9 6 6 6-6"></path></svg></button><div id="tags-nav-menu-menu" role="menu" class="ds-menu-surface fixed min-w-40 hidden z-50 origin-top-left transform transition-all duration-200"><a href="/tags/" role="menuitem" class="ds-menu-item">タグ</a><a href="/blog/tags/" role="menuitem" class="ds-menu-item">ブログタグ</a><a href="/snippets/tags/" role="menuitem" class="ds-menu-item">スニペットタグ</a><a href="/glossary/tags/" role="menuitem" class="ds-menu-item">用語集タグ</a></div></div>
100<script>(function(){const buttonId = "tags-nav-menu-btn";
101const menuId = "tags-nav-menu-menu";
102const align = "left";
103const offsetY = 8;
104
105const menuBtn = document.getElementById(buttonId);
106const menu = document.getElementById(menuId);
107
108const closeMenu = () => {
109  if (!menuBtn || !menu) return;
110  menu.classList.add('hidden');
111  menuBtn.setAttribute('aria-expanded', 'false');
112};
113
114const positionMenu = () => {
115  if (!menuBtn || !menu) return;
116  const wasHidden = menu.classList.contains('hidden');
117  if (wasHidden) {
118    menu.classList.remove('hidden');
119    menu.style.visibility = 'hidden';
120  }
121
122  const rect = menuBtn.getBoundingClientRect();
123  const menuRect = menu.getBoundingClientRect();
124  let left = rect.left;
125
126  if (align === 'right') {
127    left = rect.right - menuRect.width;
128  }
129
130  left = Math.max(8, Math.min(left, window.innerWidth - menuRect.width - 8));
131  let top = rect.bottom + offsetY;
132
133  if (top + menuRect.height > window.innerHeight - 8) {
134    top = Math.max(8, rect.top - menuRect.height - offsetY);
135  }
136
137  menu.style.left = `${Math.round(left)}px`;
138  menu.style.top = `${Math.round(top)}px`;
139
140  if (wasHidden) {
141    menu.classList.add('hidden');
142    menu.style.visibility = '';
143  }
144};
145
146if (menuBtn && menu) {
147  menuBtn.addEventListener('click', (event) => {
148    event.stopPropagation();
149    const isHidden = menu.classList.contains('hidden');
150    if (isHidden) {
151      menu.classList.remove('hidden');
152      positionMenu();
153      menuBtn.setAttribute('aria-expanded', 'true');
154      return;
155    }
156    closeMenu();
157  });
158
159  menu.addEventListener('click', (event) => {
160    const target = event.target;
161    if (target instanceof HTMLElement && target.closest('a')) {
162      closeMenu();
163    }
164  });
165
166  document.addEventListener('click', (event) => {
167    const target = event.target;
168    if (!(target instanceof Node)) return;
169    if (!menuBtn.contains(target) && !menu.contains(target)) {
170      closeMenu();
171    }
172  });
173
174  document.addEventListener('keydown', (event) => {
175    if (event.key === 'Escape') {
176      closeMenu();
177    }
178  });
179
180  window.addEventListener('resize', () => {
181    if (!menu.classList.contains('hidden')) {
182      positionMenu();
183    }
184  });
185  window.addEventListener('scroll', closeMenu, true);
186}
187})();</script>
187</li></ul></nav></div></header>
187<script type="module">var e=`navPosition`,t=document.getElementById(`nav-toggle-btn`);function n(n){t&&(n===`right`?t.classList.add(`order-last`):t.classList.remove(`order-last`),localStorage.setItem(e,n))}var r=localStorage.getItem(e)||`left`;r&&n(r);var i=document.getElementById(`lang-menu-btn`),a=document.getElementById(`lang-menu`);i&&a&&(i.addEventListener(`click`,e=>{e.stopPropagation(),a.classList.toggle(`hidden`)}),document.addEventListener(`click`,e=>{let t=e.target;t instanceof Node&&!i.contains(t)&&!a.contains(t)&&a.classList.add(`hidden`)}));</script>
187</div><main class="flex-1"><section class="ds-page">
187<script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"ホーム","item":"https://hsb.horse/"},{"@type":"ListItem","position":2,"name":"ブログ","item":"https://hsb.horse/blog/"}]}</script>
187<p class="ds-heading-eyebrow">ブログ</p><h1 class="ds-heading-title">ブログ</h1><p class="ds-heading-description">技術メモや開発の知見をまとめた記事一覧です。</p><a href="/blog/tags/" class="ds-action-link">ブログタグ</a><ol class="ds-list-stack"><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-07-18T00:00:00.000Z">2026-07-18</time><a href="/blog/header-relay-published/" class="ds-list-title">MV3でレスポンスヘッダーを次のリクエストへ中継するChrome拡張「Header Relay」を公開した</a><p class="ds-list-description">API開発と検証のためのChrome拡張 Header Relay を公開しました。レスポンスヘッダーから値をキャプチャして、以降の対象originへのリクエストに自動で付与する拡張です。仕組みだけ聞くと簡単そうですが、Manifest V3にはこれをやるための素直な実装手段が最初から存在しません。この記事では、何を解決するものか、その制約下でどう設計したかを書きます。</p><div class="ds-tag-list"><a href="/tags/webext/" class="ds-tag-link">#webext</a><a href="/tags/productivity/" class="ds-tag-link">#productivity</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-04-23T00:00:00.000Z">2026-04-23</time><a href="/blog/microcms-cli/" class="ds-list-title">Go で microCMS CLI を作った</a><p class="ds-list-description">管理画面を開かずにターミナルから microCMS のコンテンツを操作できる CLI ツールを Go で作った。複数サービス管理・スキャンキャッシュ・バックアップなど主要機能と、設計で判断したことをまとめる。</p><div class="ds-tag-list"><a href="/tags/go/" class="ds-tag-link">#Go</a><a href="/tags/cli/" class="ds-tag-link">#CLI</a><a href="/tags/microcms/" class="ds-tag-link">#microCMS</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-13T00:00:00.000Z">2026-03-13</time><a href="/blog/golang-api-server-implementation-guidelines/" class="ds-list-title">GolangでAPIサーバを実装するときの基本方針</a><p class="ds-list-description">単一バイナリ + cobra、Operation中心、HTTP/CLI分離、secure error handling、graceful shutdown まで、GoでAPIサーバを書くときの自分用の基本方針を整理した。</p><div class="ds-tag-list"><a href="/tags/golang/" class="ds-tag-link">#golang</a><a href="/tags/api/" class="ds-tag-link">#api</a><a href="/tags/architecture/" class="ds-tag-link">#architecture</a><a href="/tags/http/" class="ds-tag-link">#http</a><a href="/tags/graceful-shutdown/" class="ds-tag-link">#graceful-shutdown</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-12T00:00:00.000Z">2026-03-12</time><a href="/blog/ios-screen-time-api-self-control-app-guide/" class="ds-list-title">iOS セルフコントロールアプリを Screen Time API で作るための設計ガイド</a><p class="ds-list-description">NEDNSProxyProviderの落とし穴からFamilyControls+ManagedSettingsの設計、Shield Extension 2本構成、App Groupアンロックハンドオフ、Personal Team制限まで。Screen Time APIを使ったiOSブロックアプリ開発の全体像をまとめた。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/swift/" class="ds-tag-link">#Swift</a><a href="/tags/screen-time-api/" class="ds-tag-link">#Screen Time API</a><a href="/tags/familycontrols/" class="ds-tag-link">#FamilyControls</a><a href="/tags/managedsettings/" class="ds-tag-link">#ManagedSettings</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-12T00:00:00.000Z">2026-03-12</time><a href="/blog/kpop-for-it-engineers/" class="ds-list-title">ITエンジニアが聞くべきK-POP楽曲</a><p class="ds-list-description">IT用語やサイバーコンセプトを取り入れたおすすめK-POP5選。aespaのメタバース、NCT DREAMのグリッチ、STAYCの「GPT」など、視覚とサウンドでテクノロジーを感じる楽曲をピックアップ。</p><div class="ds-tag-list"><a href="/tags/k-pop/" class="ds-tag-link">#K-POP</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-12T00:00:00.000Z">2026-03-12</time><a href="/blog/typescript-monorepo-best-practice-2026/" class="ds-list-title">
187TypeScript Monorepo 最適解を整理する 2026年版</a><p class="ds-list-description">pnpm workspace、Turborepo、TypeScript Project References、Biome、WXT の役割を整理し、汎用の無難解と 4 パッケージ構成での実務解を分けてまとめた。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#TypeScript</a><a href="/tags/monorepo/" class="ds-tag-link">#Monorepo</a><a href="/tags/pnpm/" class="ds-tag-link">#pnpm</a><a href="/tags/turborepo/" class="ds-tag-link">#Turborepo</a><a href="/tags/biome/" class="ds-tag-link">#Biome</a><a href="/tags/wxt/" class="ds-tag-link">#WXT</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-11T00:00:00.000Z">2026-03-11</time><a href="/blog/app-group-unlock-handoff/" class="ds-list-title">Shield ExtensionからメインアプリへのアンロックをApp Group経由で実装する</a><p class="ds-list-description">ShieldActionDelegateからメインアプリを直接起動することはできない。App Groupの共有ストレージに「アンロック要求」を書き込み、アプリ側で読み取るハンドオフパターンの設計を整理した。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/swift/" class="ds-tag-link">#Swift</a><a href="/tags/managedsettings/" class="ds-tag-link">#ManagedSettings</a><a href="/tags/shieldactiondelegate/" class="ds-tag-link">#ShieldActionDelegate</a><a href="/tags/app-group/" class="ds-tag-link">#App Group</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-11T00:00:00.000Z">2026-03-11</time><a href="/blog/family-controls-managed-settings-basics/" class="ds-list-title">FamilyControls + ManagedSettings で iOS の自己制限ブロックを設計する</a><p class="ds-list-description">AuthorizationCenter、FamilyActivityPicker、ManagedSettingsの3要素がどう組み合わさるか。フリーテキストのドメイン入力ではなくトークンベース設計にすべき理由と、その実装の骨格を整理した。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/swift/" class="ds-tag-link">#Swift</a><a href="/tags/familycontrols/" class="ds-tag-link">#FamilyControls</a><a href="/tags/managedsettings/" class="ds-tag-link">#ManagedSettings</a><a href="/tags/screen-time-api/" class="ds-tag-link">#Screen Time API</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-11T00:00:00.000Z">2026-03-11</time><a href="/blog/nednsproxyprovider-wrong-choice-for-app-store/" class="ds-list-title">iOSでサイトブロックを作るとき、NEDNSProxyProviderを選んではいけない</a><p class="ds-list-description">App Store配布のセルフコントロール系iOSアプリでNEDNSProxyProviderを使うことが技術的・ポリシー的にどう問題なのか、Screen Time APIへの移行判断に至るまでの調査を記録した。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/swift/" class="ds-tag-link">#Swift</a><a href="/tags/networkextension/" class="ds-tag-link">#NetworkExtension</a><a href="/tags/nednsproxyprovider/" class="ds-tag-link">#NEDNSProxyProvider</a><a href="/tags/screen-time-api/" class="ds-tag-link">#Screen Time API</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-11T00:00:00.000Z">2026-03-11</time><a href="/blog/personal-team-family-controls-limitation/" class="ds-list-title">Personal Team では Family Controls の実機テストができない</a><p class="ds-list-description">無償のPersonal TeamアカウントではFamily Controls Developmentエンタイトルメントを取得できない。Apple Developer Programへの加入が実機テストの前提条件になる理由と影響範囲を整理した。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/xcode/" class="ds-tag-link">#Xcode</a><a href="/tags/familycontrols/" class="ds-tag-link">
187#FamilyControls</a><a href="/tags/apple-developer-program/" class="ds-tag-link">#Apple Developer Program</a><a href="/tags/30b3-30fc-30c9-7f72-540d/" class="ds-tag-link">#コード署名</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-11T00:00:00.000Z">2026-03-11</time><a href="/blog/shield-extension-two-targets/" class="ds-list-title">iOS Shield Extension は1本ではなく2本必要という話</a><p class="ds-list-description">ManagedSettingsでブロックを設定したとき表示されるシールド画面は、Action ExtensionとConfiguration Extensionの2つの別ターゲットで成り立っている。正しいNSExtensionPointIdentifierとプリンシパルクラスを整理した。</p><div class="ds-tag-list"><a href="/tags/ios/" class="ds-tag-link">#iOS</a><a href="/tags/swift/" class="ds-tag-link">#Swift</a><a href="/tags/managedsettings/" class="ds-tag-link">#ManagedSettings</a><a href="/tags/managedsettingsui/" class="ds-tag-link">#ManagedSettingsUI</a><a href="/tags/shieldactiondelegate/" class="ds-tag-link">#ShieldActionDelegate</a><a href="/tags/shieldconfigurationdatasource/" class="ds-tag-link">#ShieldConfigurationDataSource</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-07T00:00:00.000Z">2026-03-07</time><a href="/blog/solidjs-best-practices-skill-on-skills-sh/" class="ds-list-title">SolidJSのベストプラクティスをskills.shでスキルとして公開した</a><p class="ds-list-description">Claude Codeのスキル機能を使い、SolidJSのベストプラクティスをskills.shで公開した記録。descriptionの精度がトリガー精度を決めること、with/withoutテストで差分を確認するプロセスについて書いた。</p><div class="ds-tag-list"><a href="/tags/claude/" class="ds-tag-link">#claude</a><a href="/tags/ai/" class="ds-tag-link">#ai</a><a href="/tags/solidjs/" class="ds-tag-link">#solidjs</a><a href="/tags/skills-sh/" class="ds-tag-link">#skills-sh</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-05T00:00:00.000Z">2026-03-05</time><a href="/blog/2026-02-21-mac-mini-setup/" class="ds-list-title">M4 Mac Miniセットアップ</a><p class="ds-list-description">Amazonの謎セールでM4 Mac miniが安くなっていたので貯まっていたポイントも使ってさらに値引きして買ってしまった。NixOSベースの管理も見据えて色々作業した記録をここに残す。</p><div class="ds-tag-list"><a href="/tags/mac/" class="ds-tag-link">#mac</a><a href="/tags/setup/" class="ds-tag-link">#setup</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-05T00:00:00.000Z">2026-03-05</time><a href="/blog/npm-publish-web-stream-extras/" class="ds-list-title">@hsblabs/web-stream-extras を npm publish するまでに詰まったところ全部</a><p class="ds-list-description">手動 publish から GitHub Actions + npm Trusted Publisher への移行で実際に踏んだ失敗を時系列でまとめた。provenance の 422、tag と version の順序、publish 前検証の整備まで。</p><div class="ds-tag-list"><a href="/tags/npm/" class="ds-tag-link">#npm</a><a href="/tags/github-actions/" class="ds-tag-link">#GitHub Actions</a><a href="/tags/oss/" class="ds-tag-link">#OSS</a><a href="/tags/ci-cd/" class="ds-tag-link">#CI/CD</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-05T00:00:00.000Z">2026-03-05</time><a href="/blog/web-stream-extras/" class="ds-list-title">@hsblabs/web-stream-extras をリリースした</a><p class="ds-list-description">ブラウザと Node.js で ReadableStream&lt;Uint8Array&gt; を扱うための小さな TypeScript ユーティリティライブラリ。バイト変換ヘルパー、トランスフォームストリーム、Web Crypto API を使ったストリーム暗号化まで。</p><div class="ds-tag-list"><a href="/tags/npm/" class="ds-tag-link">#npm</a><a href="/tags/typescript/" class="ds-tag-link">#TypeScript</a><a href="/tags/oss/" class="ds-tag-link">#OSS</a><a href="/tags/webstreams/" class="ds-tag-link">#WebStreams</a><a href="/tags/webcrypto/" class="ds-tag-link">#WebCrypto</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-05T00:00:00.000Z">2026-03-05</time><a href="/blog/web-stream-extras-encryption/" class="ds-list-title">Web Crypto でバイトストリームをブラウザ上で暗号化する</a><p class="ds-list-description">Web Crypto は強力な暗号プリミティブを提供するが、ストリーム向けのインターフェースはない。@hsblabs/web-stream-extras の encryption サブパスを使って ReadableStream&lt;Uint8Array&gt; を暗号化する方法 — ファイル暗号化、ストリームごとの鍵管理、パイプライン合成まで。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#TypeScript</a><a href="/tags/webstreams/" class="ds-tag-link">#WebStreams</a><a href="/tags/webcrypto/" class="ds-tag-link">#WebCrypto</a><a href="/tags/security/" class="ds-tag-link">#Security</a><a href="/tags/oss/" class="ds-tag-link">#OSS</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-03-05T00:00:00.000Z">2026-03-05</time><a href="/blog/web-stream-extras-why/" class="ds-list-title">同じ Web Streams のボイラープレートを書き続けるのに飽きた</a><p class="ds-list-description">何度も書き直していた ReadableStream&lt;Uint8Array&gt; のユーティリティを @hsblabs/web-stream-extras にまとめた理由。繰り返し書いていた3つのパターン、ByteTransformStream 基底クラス、Web Crypto を使ったストリーム暗号化まで。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#TypeScript</a><a href="/tags/webstreams/" class="ds-tag-link">#WebStreams</a><a href="/tags/webcrypto/" class="ds-tag-link">#WebCrypto</a><a href="/tags/oss/" class="ds-tag-link">#OSS</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-17T00:00:00.000Z">2026-02-17</time><a href="/blog/aws-cross-region-vpc-peering-peer-region-trap/" class="ds-list-title">AWSクロスリージョンVPCピアリング 失敗ログ</a><p class="ds-list-description">
187大阪-東京間のVPCピアリング設定でハマった。--peer-region忘れでfailed状態になり、削除もできなくなった件。</p><div class="ds-tag-list"><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/vpc-peering/" class="ds-tag-link">#vpc-peering</a><a href="/tags/cross-region/" class="ds-tag-link">#cross-region</a><a href="/tags/troubleshooting/" class="ds-tag-link">#troubleshooting</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-17T00:00:00.000Z">2026-02-17</time><a href="/blog/cache-control-micro-caching-strategy/" class="ds-list-title">Cache-Control: max-age=3, must-revalidateを自分なりに整理する</a><p class="ds-list-description">マイクロキャッシングと呼ばれるCache-Control設定の挙動を整理。max-age=3とmust-revalidateの組み合わせがもたらす効果、時系列での動作フロー、具体的なユースケースをまとめる。</p><div class="ds-tag-list"><a href="/tags/http/" class="ds-tag-link">#http</a><a href="/tags/cache/" class="ds-tag-link">#cache</a><a href="/tags/performance/" class="ds-tag-link">#performance</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-14T00:00:00.000Z">2026-02-14</time><a href="/blog/astro-live-editor-implementation/" class="ds-list-title">Astroプロジェクトに開発専用Liveエディタを実装する</a><p class="ds-list-description">Astroで開発モード限定のブラウザエディタを実装する方法。ViteミドルウェアとReactで、本番ビルドに一切影響を及ぼさずに、開発中のコンテンツ管理を快適にする手順を整理。</p><div class="ds-tag-list"><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/react/" class="ds-tag-link">#react</a><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/vite/" class="ds-tag-link">#vite</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-14T00:00:00.000Z">2026-02-14</time><a href="/blog/mobile-app-metadata-config-approach/" class="ds-list-title">Webエンジニアが考えるモバイルアプリの設定管理:起動時メタデータ取得アプローチ</a><p class="ds-list-description">Webフロントエンドとインフラの経験から、モバイルアプリのドメイン管理やバージョニングの課題を整理。CloudFront+S3による起動時メタデータ取得方式を提案し、Firebase Remote Configなどの既存ソリューションとの比較も交えて考察する。</p><div class="ds-tag-list"><a href="/tags/mobile/" class="ds-tag-link">#mobile</a><a href="/tags/infrastructure/" class="ds-tag-link">#infrastructure</a><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/architecture/" class="ds-tag-link">#architecture</a><a href="/tags/app-configuration/" class="ds-tag-link">#app-configuration</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-07T00:00:00.000Z">2026-02-07</time><a href="/blog/astro-dev-blog-404-directory-collision/" class="ds-list-title">Astro + bun dev で /blog が 404 になる原因と解決手順</a><p class="ds-list-description">Astro + bun dev で /blog が 404 になる問題を、リンク確認・ルート生成・配信経路の順で切り分けて、ディレクトリ名衝突が原因であったことを突き止めるまで。</p><div class="ds-tag-list"><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/bun/" class="ds-tag-link">#bun</a><a href="/tags/troubleshooting/" class="ds-tag-link">#troubleshooting</a><a href="/tags/routing/" class="ds-tag-link">#routing</a><a href="/tags/i18n/" class="ds-tag-link">#i18n</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-07T00:00:00.000Z">2026-02-07</time><a href="/blog/codex-cloud-mise-bun-troubleshooting/" class="ds-list-title">Codex Cloudでmiseのバージョン固定が効かない時の対処法</a><p class="ds-list-description">Codex Cloudでmise install bunが成功してもcommand not foundになる原因と、setup.shでmise use -gとmise activateを使って解決した手順の記録。</p><div class="ds-tag-list"><a href="/tags/codex/" class="ds-tag-link">#codex</a><a href="/tags/mise/" class="ds-tag-link">#mise</a><a href="/tags/bun/" class="ds-tag-link">#bun</a><a href="/tags/troubleshooting/" class="ds-tag-link">#troubleshooting</a><a href="/tags/devenv/" class="ds-tag-link">#devenv</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-06T00:00:00.000Z">2026-02-06</time><a href="/blog/lefthook-introduction-procedure/" class="ds-list-title">lefthook導入手順: pre-commitとpre-pushでBiomeを自動実行する</a><p class="ds-list-description">lefthook を使って、コミット時とPush時に Biome のフォーマットとリントを自動実行する導入手順を整理。ADR作成から設定、インストール、動作確認までの流れを1本で追える。</p><div class="ds-tag-list"><a href="/tags/lefthook/" class="ds-tag-link">#lefthook</a><a href="/tags/git-hook/" class="ds-tag-link">#git-hook</a><a href="/tags/biome/" class="ds-tag-link">#biome</a><a href="/tags/bun/" class="ds-tag-link">#bun</a><a href="/tags/quality/" class="ds-tag-link">#quality</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-02-01T00:00:00.000Z">2026-02-01</time><a href="/blog/golang-analyze-deps-included-build-artifacts/" class="ds-list-title">ビルド成果物に含まれる依存関係を調べる</a><p class="ds-list-description">Goでビルドされたバイナリには、使用された依存関係やビルド設定の情報が埋め込まれている。</p><div class="ds-tag-list"><a href="/tags/golang/" class="ds-tag-link">#golang</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-01-26T00:00:00.000Z">2026-01-26</time><a href="/blog/personality-analyzer-skill-for-claude/" class="ds-list-title">Claude用性格分析スキルpersonality-analyzerを作った話</a><p class="ds-list-description">Claudeとのチャット内容からHEXACO・MBTIスコアをYAML形式で出力するスキルの開発記録。AIとの会話から性格特性を定量化する試み。</p><div class="ds-tag-list"><a href="/tags/claude/" class="ds-tag-link">#claude</a><a href="/tags/ai/" class="ds-tag-link">#ai</a><a href="/tags/personality-analysis/" class="ds-tag-link">#personality-analysis</a><a href="/tags/hexaco/" class="ds-tag-link">#hexaco</a><a href="/tags/mbti/" class="ds-tag-link">#mbti</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-01-18T00:00:00.000Z">2026-01-18</time><a href="/blog/age-tar-encrypted-backup/" class="ds-list-title">age-tarでディレクトリ単位の暗号化バックアップを自動化する</a><p class="ds-list-description">ageとtarを組み合わせて、サブディレクトリごとに暗号化アーカイブを作るシェルスクリプト。日本語ディレクトリ名を含む運用でもCLI互換性を保てる。</p><div class="ds-tag-list"><a href="/tags/shell/" class="ds-tag-link">#shell</a><a href="/tags/age/" class="ds-tag-link">#age</a><a href="/tags/encryption/" class="ds-tag-link">#encryption</a><a href="/tags/backup/" class="ds-tag-link">#backup</a><a href="/tags/cli/" class="ds-tag-link">#cli</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2026-01-10T00:00:00.000Z">2026-01-10</time><a href="/blog/uuid-v7/" class="ds-list-title">UUID v7 実装ガイド: JavaScript・Go・Shellで作る</a><p class="ds-list-description">UUID v7 のビット構造、version/variant のビット設定、JavaScript・Go・Shellでの最小実装、実装時ã
187®ãƒã‚§ãƒƒã‚¯é …目、RFCの参照先を1ページで整理。時系列ソートしやすいIDを安全に導入したい開発者向けに、生成ロジックの確認ポイントをまとめた実践ガイド。実装前の確認に使える。</p><div class="ds-tag-list"><a href="/tags/uuid/" class="ds-tag-link">#uuid</a><a href="/tags/javascript/" class="ds-tag-link">#javascript</a><a href="/tags/golang/" class="ds-tag-link">#golang</a><a href="/tags/shell/" class="ds-tag-link">#shell</a><a href="/tags/cli/" class="ds-tag-link">#cli</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-12-21T00:00:00.000Z">2025-12-21</time><a href="/blog/glossary-vs-dictionary/" class="ds-list-title">GlossaryとDictionaryの違いを整理する</a><p class="ds-list-description">Glossary(用語集)とDictionary(辞書)の違いを、対象範囲、目的、掲載される言葉、作成者の観点から比較整理。</p><div class="ds-tag-list"><a href="/tags/terminology/" class="ds-tag-link">#terminology</a><a href="/tags/documentation/" class="ds-tag-link">#documentation</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-12-07T00:00:00.000Z">2025-12-07</time><a href="/blog/esbuild-vite-remove-license-comments/" class="ds-list-title">esbuild/ViteでライセンスコメントをバンドルJSから削除する</a><p class="ds-list-description">esbuildやViteでビルドするとき、バンドルファイルに含まれるライセンスコメントを削除してファイルサイズを削減する方法。</p><div class="ds-tag-list"><a href="/tags/esbuild/" class="ds-tag-link">#esbuild</a><a href="/tags/vite/" class="ds-tag-link">#vite</a><a href="/tags/optimization/" class="ds-tag-link">#optimization</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-12-07T00:00:00.000Z">2025-12-07</time><a href="/blog/wxt-enable-react-compiler/" class="ds-list-title">WXTでReact Compilerを有効化する設定手順</a><p class="ds-list-description">WXTフレームワークでReact Compilerを有効化し、自動最適化を行う設定方法。babel-plugin-react-compilerのインストールと設定を整理。</p><div class="ds-tag-list"><a href="/tags/react/" class="ds-tag-link">#react</a><a href="/tags/wxt/" class="ds-tag-link">#wxt</a><a href="/tags/optimization/" class="ds-tag-link">#optimization</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-28T00:00:00.000Z">2025-11-28</time><a href="/blog/cloudflare-workers-subdirectory-routing/" class="ds-list-title">Cloudflare Workersでサブディレクトリをルーティングする設定方法</a><p class="ds-list-description">Cloudflare Workersでサブディレクトリ配下にAstroプロジェクトをデプロイする際の設定手順。wrangler.jsonとastro.configの調整方法を整理。</p><div class="ds-tag-list"><a href="/tags/cloudflare/" class="ds-tag-link">#cloudflare</a><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/deployment/" class="ds-tag-link">#deployment</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-28T00:00:00.000Z">2025-11-28</time><a href="/blog/sh-c-behavior-and-zsh-config-loading/" class="ds-list-title">sh -cの挙動とzsh設定ファイルの読み込みルール</a><p class="ds-list-description">sh -cコマンドの挙動、環境変数の継承ルール、shebangの有効性、zsh設定ファイル(.zshrc, .zshenv等)がスクリプト実行時にどう読み込まれるかを整理。</p><div class="ds-tag-list"><a href="/tags/shell/" class="ds-tag-link">#shell</a><a href="/tags/zsh/" class="ds-tag-link">#zsh</a><a href="/tags/unix/" class="ds-tag-link">#unix</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-23T00:00:00.000Z">2025-11-23</time><a href="/blog/typescript-crc32-implementation/" class="ds-list-title">TypeScriptでCRC32ハッシュを実装する</a><p class="ds-list-description">CRC32ハッシュアルゴリズムをTypeScriptで実装する手順。ルックアップテーブルを使った高速化手法と、実際の使用例を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/algorithm/" class="ds-tag-link">#algorithm</a><a href="/tags/hash/" class="ds-tag-link">#hash</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-22T00:00:00.000Z">2025-11-22</time><a href="/blog/biome-disable-unused-warnings-for-frameworks/" class="ds-list-title">Biomeの未使用変数警告をAstro/Vue/Svelteファイルで無効化する</a><p class="ds-list-description">AstroやVue、Svelteファイルで発生するBiomeの未使用インポート・変数警告を、overrides設定で無効化する方法。</p><div class="ds-tag-list"><a href="/tags/biome/" class="ds-tag-link">#biome</a><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/vue/" class="ds-tag-link">#vue</a><a href="/tags/svelte/" class="ds-tag-link">#svelte</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-22T00:00:00.000Z">2025-11-22</time><a href="/blog/golang-web-framework-and-tools/" class="ds-list-title">Goの環境構築メモ: WebフレームワークとDev Tools</a><p class="ds-list-description">Goの開発環境構築時に調べたWebフレームワークと開発ツールのメモ。EchoフレームワークとAir(Live Reload)ã
187«ã¤ã„て整理。</p><div class="ds-tag-list"><a href="/tags/golang/" class="ds-tag-link">#golang</a><a href="/tags/echo/" class="ds-tag-link">#echo</a><a href="/tags/air/" class="ds-tag-link">#air</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-22T00:00:00.000Z">2025-11-22</time><a href="/blog/libavif-image-conversion/" class="ds-list-title">libavifで画像をAVIF形式に変換する</a><p class="ds-list-description">HomebrewでlibavifをインストールしてJPEG/PNGをAVIF形式に変換する方法。シングル変換とバッチ処理に対応。</p><div class="ds-tag-list"><a href="/tags/avif/" class="ds-tag-link">#avif</a><a href="/tags/image/" class="ds-tag-link">#image</a><a href="/tags/macos/" class="ds-tag-link">#macos</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-22T00:00:00.000Z">2025-11-22</time><a href="/blog/macos-launchd-cron-like/" class="ds-list-title">macOS Launchdで定期実行スクリプトを動かす</a><p class="ds-list-description">macOS LaunchdでCronのように毎分実行するスクリプトを構築する方法。環境変数の問題を回避し、ログ管理も含めたセットアップ手順。</p><div class="ds-tag-list"><a href="/tags/macos/" class="ds-tag-link">#macos</a><a href="/tags/launchd/" class="ds-tag-link">#launchd</a><a href="/tags/automation/" class="ds-tag-link">#automation</a><a href="/tags/shell/" class="ds-tag-link">#shell</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-11-22T00:00:00.000Z">2025-11-22</time><a href="/blog/zabrze-shell-abbreviation-introduction/" class="ds-list-title">zabrzeで始めるシェル略語展開: zsh-abbrよりシンプルな選択肢</a><p class="ds-list-description">シェルの略語展開ツールzabrzeの導入手順と使い方。zsh-abbrの代替として、よりシンプルな設定で効率的なコマンド入力を実現する方法を紹介。</p><div class="ds-tag-list"><a href="/tags/cli/" class="ds-tag-link">#cli</a><a href="/tags/zsh/" class="ds-tag-link">#zsh</a><a href="/tags/productivity/" class="ds-tag-link">#productivity</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-02-14T00:00:00.000Z">2025-02-14</time><a href="/blog/line-split-transform-stream/" class="ds-list-title">TransformStreamで行単位のテキスト分割を実装: LineSplitTransform</a><p class="ds-list-description">ストリームから受け取ったバイナリデータを行単位に分割するTransformStream実装。ログファイルやテキストファイルの逐次処理に活用できる手法を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/streams/" class="ds-tag-link">#streams</a><a href="/tags/text-processing/" class="ds-tag-link">#text-processing</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2025-01-18T00:00:00.000Z">2025-01-18</time><a href="/blog/offscreencanvas-web-worker-image-processing/" class="ds-list-title">OffscreenCanvasとWeb Workerで画像処理を実装する</a><p class="ds-list-description">Web Workerとoffscreen Canvasを使って、メインスレッドをブロックせずに画像処理を行う実装方法。WebP変換の実例を整理。</p><div class="ds-tag-list"><a href="/tags/webworker/" class="ds-tag-link">#webworker</a><a href="/tags/canvas/" class="ds-tag-link">#canvas</a><a href="/tags/image-processing/" class="ds-tag-link">#image-processing</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-21T00:00:00.000Z">2024-09-21</time><a href="/blog/to-readablestream-utility-typescript/" class="ds-list-title">値をReadableStreamに変換するTypeScriptユーティリティ関数</a><p class="ds-list-description">StringやオブジェクトなどをReadableStreamに変換するユーティリティ関数。Blob経由ではなく任意の型でストリームを扱う実装を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/streams/" class="ds-tag-link">#streams</a><a href="/tags/utility/" class="ds-tag-link">#utility</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-11T00:00:00.000Z">2024-09-11</time><a href="/blog/typescript-cidr-ip-address-validation/" class="ds-list-title">TypeScriptでCIDR範囲のIPアドレス判定を実装する</a><p class="ds-list-description">CloudFront Functionなど外部モジュールが使えない環境向けに、CIDR範囲内のIPアドレス判定を自前実装。ビット演算を使ったクラス構文の実装を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/networking/" class="ds-tag-link">#networking</a><a href="/tags/cloudfront/" class="ds-tag-link">#cloudfront</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-08T00:00:00.000Z">2024-09-08</time><a href="/blog/bun-hono-api-on-aws-lambda/" class="ds-list-title">Bun+HonoでビルドしたAPIサーバをAWS Lambdaで動かす方法</a><p class="ds-list-description">BunランタイムでHono APIサーバをAWS Lambda上で動作させる手順。Single File Executableとしてコンパイルし、Lambda Web Adapterで動かす構成を解説。</p><div class="ds-tag-list"><a href="/tags/bun/" class="ds-tag-link">#bun</a><a href="/tags/hono/" class="ds-tag-link">#hono</a><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/lambda/" class="ds-tag-link">#lambda</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-07T00:00:00.000Z">2024-09-07</time><a href="/blog/generate-project-summary-bun-typescript/" class="ds-list-title">generate-project-summary.pyをBun+TypeScriptで再実装した話</a><p class="ds-list-description">Pythonで書かれたプロジェクト構造要約スクリプトを、Bun+TypeScriptで再実装。LLMにリポジトリ全体を読ませるための実行可能バイナリ作成の経緯と実装を紹介。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/bun/" class="ds-tag-link">#bun</a><a href="/tags/llm/" class="ds-tag-link">#llm</a><a href="/tags/cli/" class="ds-tag-link">#cli</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-07T00:00:00.000Z">2024-09-07</time><a href="/blog/sort-media-by-resolution-typescript/" class="ds-list-title">メディアファイルを解像度でソートするTypeScript実装</a><p class="ds-list-description">
187動画や画像の解像度(幅×高さ)でソートする簡潔なTypeScript実装。Twitter動画メディアのソート処理に活用。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/media/" class="ds-tag-link">#media</a><a href="/tags/sorting/" class="ds-tag-link">#sorting</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-09-03T00:00:00.000Z">2024-09-03</time><a href="/blog/cloudfront-s3-spa-deployment-script/" class="ds-list-title">CloudFront + S3でSPAを配信するデプロイスクリプト</a><p class="ds-list-description">CloudFront経由でS3にホストしたSPAを配信する際のデプロイスクリプト。キャッシュコントロール設定、ブルーグリーンデプロイ、切り戻し手順を整理。</p><div class="ds-tag-list"><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/cloudfront/" class="ds-tag-link">#cloudfront</a><a href="/tags/s3/" class="ds-tag-link">#s3</a><a href="/tags/deployment/" class="ds-tag-link">#deployment</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-29T00:00:00.000Z">2024-08-29</time><a href="/blog/athena-drop-database-cascade/" class="ds-list-title">AWS Athenaで空でないデータベースを削除する方法</a><p class="ds-list-description">AWS Athenaで空でないデータベースを削除しようとしてエラーになったときの対処法。DROP DATABASE CASCADEコマンドの使い方。</p><div class="ds-tag-list"><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/athena/" class="ds-tag-link">#athena</a><a href="/tags/sql/" class="ds-tag-link">#sql</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-29T00:00:00.000Z">2024-08-29</time><a href="/blog/typescript-lotto-number-generator/" class="ds-list-title">TypeScriptでロト6・ロト7の抽選数字を生成する</a><p class="ds-list-description">メルセンヌ・ツイスターを使ってロト6・ロト7の抽選番号を生成するTypeScript実装。疑似乱数生成器の実践例。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/algorithm/" class="ds-tag-link">#algorithm</a><a href="/tags/random/" class="ds-tag-link">#random</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-28T00:00:00.000Z">2024-08-28</time><a href="/blog/api-gateway-iam-auth-typescript-request/" class="ds-list-title">IAM認証有効なAPI GatewayにTypeScriptでリクエストする実装</a><p class="ds-list-description">AWS SDK for JavaScriptのSigV4を使って、IAM認証が有効なAPI Gatewayにローカル環境からTypeScriptでリクエストする実装手順。</p><div class="ds-tag-list"><a href="/tags/aws/" class="ds-tag-link">#aws</a><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/api-gateway/" class="ds-tag-link">#api-gateway</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-28T00:00:00.000Z">2024-08-28</time><a href="/blog/markdown-confluence-cli-sync/" class="ds-list-title">markdown-confluence CLIでMarkdownをConfluenceに同期する運用</a><p class="ds-list-description">Gitリポジトリ内のMarkdownドキュメントをConfluenceに同期する@markdown-confluence/cliの導入と運用方法。ADRやドキュメントをConfluenceで共有する手順を整理。</p><div class="ds-tag-list"><a href="/tags/confluence/" class="ds-tag-link">#confluence</a><a href="/tags/markdown/" class="ds-tag-link">#markdown</a><a href="/tags/documentation/" class="ds-tag-link">#documentation</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-28T00:00:00.000Z">2024-08-28</time><a href="/blog/vue-global-state-without-pinia/" class="ds-list-title">Piniaなしでグローバルステートを管理するVue Composable</a><p class="ds-list-description">Piniaを使わずにVueのreactiveとtoRefsでグローバルステート管理を実現する軽量な実装方法。</p><div class="ds-tag-list"><a href="/tags/vue/" class="ds-tag-link">#vue</a><a href="/tags/composable/" class="ds-tag-link">#composable</a><a href="/tags/state-management/" class="ds-tag-link">#state-management</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-28T00:00:00.000Z">2024-08-28</time><a href="/blog/vue-strategy-pattern-dynamic-component/" class="ds-list-title">Vueでv-ifの分岐が増えたらストラテジーパターンを検討する</a><p class="ds-list-description">v-ifによる条件分岐が3パターン以上になったら、ストラテジーパターンでコンポーネントを動的に切り替える実装を検討。可読性とメンテナンス性の向上。</p><div class="ds-tag-list"><a href="/tags/vue/" class="ds-tag-link">#vue</a><a href="/tags/design-pattern/" class="ds-tag-link">#design-pattern</a><a href="/tags/best-practice/" class="ds-tag-link">#best-practice</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-25T00:00:00.000Z">2024-08-25</time><a href="/blog/generate-image-from-html-video-element/" class="ds-list-title">HTMLVideoElementから画像を生成するTypeScript実装</a><p class="ds-list-description">CanvasとVideoElementを使って、動画の現在フレームを画像として抽出するTypeScript実装。Promiseベースでblob生成する方法を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/canvas/" class="ds-tag-link">#canvas</a><a href="/tags/video/" class="ds-tag-link">#video</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-25T00:00:00.000Z">2024-08-25</time><a href="/blog/pm2-cron-job-setup/" class="ds-list-title">PM2でCronジョブを作成する: launchdの代替として</a><p class="ds-list-description">macOSでlaunchdの代わりにPM2を使ってCronジョブを作成する方法。設定ファイルとBunを使った定期実行の実装例を整理。</p><div class="ds-tag-list"><a href="/tags/pm2/" class="ds-tag-link">#pm2</a><a href="/tags/cron/" class="ds-tag-link">#cron</a><a href="/tags/nodejs/" class="ds-tag-link">#nodejs</a><a href="/tags/bun/" class="ds-tag-link">#bun</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-19T00:00:00.000Z">2024-08-19</time><a href="/blog/my-first-vite-plugin-implementation/" class="ds-list-title">初めてのViteプラグイン実装: ビルドメタデータを自動生成する</a><p class="ds-list-description">Vite Plugin APIを使ってビルドライフサイクルに独自処理を組み込む方法。時刻ベースのセマンティックバージョンを生成するプラグイン実装を通じて学ぶ。</p><div class="ds-tag-list"><a href="/tags/vite/" class="ds-tag-link">#vite</a><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/build-tool/" class="ds-tag-link">#build-tool</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-19T00:00:00.000Z">2024-08-19</time><a href="/blog/starlight-obsidian-vault-root-directory/" class="ds-list-title">starlight-obsidianプラグインでVaultのコピー先をルートに変更する</a><p class="ds-list-description">starlight-obsidianプラグインのデフォルト動作を変更し、Obsidian Vaultをsrc/content/docs直下にコピーする方法。サイドバー階層構造の差異を解消する実装を整理。</p><div class="ds-tag-list"><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/starlight/" class="ds-tag-link">#starlight</a><a href="/tags/obsidian/" class="ds-tag-link">#obsidian</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-19T00:00:00.000Z">2024-08-19</time><a href="/blog/variation-selectors-steganography-typescript/" class="ds-list-title">異体字セレクターを使ったステガノグラフィのTypeScript実装</a><p class="ds-list-description">Unicodeの異体字セレクターを活用して、見た目には見えない秘密の文字列をテキストに埋め込むステガノグラフィ手法のTypeScript実装。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/unicode/" class="ds-tag-link">#unicode</a><a href="/tags/steganography/" class="ds-tag-link">#steganography</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-08-17T00:00:00.000Z">2024-08-17</time><a href="/blog/extract-video-size-from-twitter-url/" class="ds-list-title">Twitter動画URLからサイズ情報を抽出するTypeScript実装</a><p class="ds-list-description">Twitter(X)の動画URLに含まれる解像度情報を正規表現で抽出するTypeScript実装。URLパターンの解析とサイズ取得方法を整理。</p><div class="ds-tag-list"><a href="/tags/typescript/" class="ds-tag-link">#typescript</a><a href="/tags/regex/" class="ds-tag-link">#regex</a><a href="/tags/twitter/" class="ds-tag-link">#twitter</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-01-27T00:00:00.000Z">2024-01-27</time><a href="/blog/fetch-google-fonts-in-browser-failed/" class="ds-list-title">ブラウザでGoogle FontsからWOFFファイルを取得しようとして失敗した備忘録</a><p class="ds-list-description">クライアントサイドでGoogle Fontsからttf/otf/woffファイルを取得して@vercel/satoriで使おうとした試行錯誤の記録。User
187-Agentヘッダー制限とwoff2非対応問題について整理。</p><div class="ds-tag-list"><a href="/tags/google-fonts/" class="ds-tag-link">#google-fonts</a><a href="/tags/satori/" class="ds-tag-link">#satori</a><a href="/tags/browser/" class="ds-tag-link">#browser</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2024-01-21T00:00:00.000Z">2024-01-21</time><a href="/blog/bitbucket-pipelines-yarn-v4-pnp/" class="ds-list-title">Bitbucket PipelinesでYarn v4 (PnPモード)を動かす設定</a><p class="ds-list-description">Bitbucket PipelinesでYarn v4のPnPモードを使ったプロジェクトをビルドする設定方法。キャッシュ設定とcorepackの有効化手順を整理。</p><div class="ds-tag-list"><a href="/tags/bitbucket/" class="ds-tag-link">#bitbucket</a><a href="/tags/yarn/" class="ds-tag-link">#yarn</a><a href="/tags/ci-cd/" class="ds-tag-link">#ci-cd</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2023-11-15T00:00:00.000Z">2023-11-15</time><a href="/blog/pagefind-cloudflare-pages-rocket-loader-issue/" class="ds-list-title">Pagefind検索がCloudflare Pagesで動かない問題: Rocket Loaderが原因だった</a><p class="ds-list-description">Astro+PagefindをCloudflare Pagesにデプロイした際に検索機能が動作しない問題の原因と解決策。Rocket Loader設定の影響について整理。</p><div class="ds-tag-list"><a href="/tags/pagefind/" class="ds-tag-link">#pagefind</a><a href="/tags/cloudflare/" class="ds-tag-link">#cloudflare</a><a href="/tags/astro/" class="ds-tag-link">#astro</a><a href="/tags/troubleshooting/" class="ds-tag-link">#troubleshooting</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2023-02-26T00:00:00.000Z">2023-02-26</time><a href="/blog/eagle-app-review-and-purchase-reasons/" class="ds-list-title">画像管理アプリEagleを購入して感じたこと</a><p class="ds-list-description">買い切り型の画像管理アプリEagleを購入した理由とレビュー。Mac/Win両対応、ライブラリ管理、ブラウザ拡張機能の使い勝手を紹介。</p><div class="ds-tag-list"><a href="/tags/eagle/" class="ds-tag-link">#eagle</a><a href="/tags/productivity/" class="ds-tag-link">#productivity</a><a href="/tags/tool-review/" class="ds-tag-link">#tool-review</a></div></li><li class="ds-list-card"><time class="ds-list-meta" datetime="2022-12-26T00:00:00.000Z">2022-12-26</time><a href="/blog/svelte-spa-on-gas-webapp/" class="ds-list-title">SvelteでGoogle Apps Scriptの高機能Webアプリを作る</a><p class="ds-list-description">Google Apps ScriptのWebアプリ公開機能とSvelteを組み合わせて、スプレッドシートをデータベースとする高機能SPAを構築する方法。</p><div class="ds-tag-list"><a href="/tags/google-apps-script/" class="ds-tag-link">#google-apps-script</a><a href="/tags/svelte/" class="ds-tag-link">#svelte</a><a href="/tags/vite/" class="ds-tag-link">#vite</a></div></li></ol></section></main><footer class="border-t border-white/10 bg-canvas text-slate-400"><div class="max-w-4xl mx-auto px-6 py-8 flex flex-col gap-3 md:flex-row md:items-center md:justify-between"><p class="text-xs">© 2026 hsb.horse. All rights reserved.</p><a href="/legal/" class="ds-link-subtle text-xs"> 法的情報 </a></div></footer>
187<script>
188    if ('modelContext' in navigator) {
189      const mc = navigator.modelContext;
190      mc.registerTool({
191        name: 'site-search',
192        description:
193          'Search blog posts, code snippets, glossary entries, and tools on hsb.horse using full-text search.',
194        inputSchema: {
195          type: 'object',
196          properties: {
197            query: { type: 'string', description: 'Search query' },
198          },
199          required: ['query'],
200        },
201        async execute({ query }) {
202          const pf = await import('/pagefind/pagefind.js');
203          await pf.init();
204          const { results } = await pf.search(query);
205          const items = await Promise.all(
206            results.slice(0, 10).map((r) => r.data()),
207          );
208          return items.map((d) => ({
209            title: d.meta?.title,
210            url: d.url,
211            excerpt: d.excerpt,
212          }));
213        },
214      });
215      mc.registerTool({
216        name: 'list-sections',
217        description:
218          'List the main content sections of hsb.horse with their paths.',
219        inputSchema: { type: 'object', properties: {} },
220        async execute() {
221          return [
222            { name: 'Blog', path: '/blog', description: 'Technical articles' },
223            {
224              name: 'Snippets',
225              path: '/snippets',
226              description: 'Reusable code patterns',
227            },
228            {
229              name: 'Glossary',
230              path: '/glossary',
231              description: 'Developer terminology',
232            },
233            {
234              name: 'Tools',
235              path: '/tools/',
236              description: 'Browser-based utilities',
237            },
238            {
239              name: 'Tags',
240              path: '/tags',
241              description: 'Cross-section tag navigation',
242            },
243          ];
244        },
245      });
246    }
247    </script>
247<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a43ccee67c1abcbc',t:'MTc5MDg3MTc2OA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
247</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.