OmniAICreator/Qiita-1.07M huggingface dataset:
RoborazziとGitHub Actionsを使って、PRに画像差分を出力させる時詰まったこと

# この記事で伝えたいこと
私はAndroidアプリを個人開発しています。

PRを出す時に、ターゲットブランチとソースブランチに画像差分があればスクリーンショットを出力するVRTを実現しようとしました。ワークフローの内容的はほぼコピペですが、ちょっとしたことで詰まったので記事に起こしておきます。

この記事が誰かの悩みの解消になれば幸いです。

# この記事の対象者
- VRTに興味がある人
- VRTの導入で困っている人

# 問題の発生
[サンプルリポジトリ](https://github.com/takahirom/roborazzi-compare-on-github-comment-sample)を参考に、以下の3つのワークフローをリポジトリに設定しました。
- StoreScreenshot.yml
- CompareScreenshot.yml
- CompareScreenShotComment.yml

それでfeatureブランチからmainブランチにPRを出したところ、なぜかmainブランチのスクリーンショットの中身が空っぽになってしまっていました。

![スクリーンショット 2024-12-28 15.55.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524029/cb9460e2-9085-205b-8e7e-206c11d76944.png)

当然、問答無用で全ての画像が差分として出てしまうので、VRTが機能していない状態です。

GitHub Actionsのワークフロー実行ログを確認してみると、compareScrenshotでStoreScreenShotのワークフロー(run id)を見つけられていないことがわかりました。

![スクリーンショット 2024-12-29 10.41.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524029/5fe1b874-9ed7-3990-046f-bbf5b09c651b.png)

```shell:該当のエラーログ
Run dawidd6/action-download-artifact@v3
==> Repository: Nagumo-7960/pokemon-stats-quiz
==> Artifact name: screenshot
==> Local path: ./
==> Workflow name: StoreScreenshot.yml
==> Workflow conclusion: success
==> Branch: main
==> Allow forks: true
Error: Not Found - https://docs.github.com/rest/actions/workflow-runs#list-workflow-runs-for-a-workflow
```

こういうエラーになることもありました。どっちにしてもワークフローが見つかっていません。

```shell:該当のエラーログ
Run dawidd6/action-download-artifact@v3
==> Repository: Nagumo-7960/pokemon-stats-quiz
==> Artifact name: screenshot
==> Local path: ./
==> Workflow name: StoreScreenshot.yml
==> Workflow conclusion: success
==> Branch: main
==> Allow forks: true
Error: no matching workflow run found with any artifacts?
```

# 調査
いくつか試しました。

### 別の新規プロジェクトで試してみる
どこが原因か確かめるために、別の新規プロジェクトを作って同様のワークフローを実行してみたところ、問題なく差分が出力されました。そのため、ワークフローの内容自体には問題がなく、該当のリポジトリのコードやGitHubのリポジトリの設定に問題があるのではないかと思いました。

### 別のGitHubのリポジトリで試してみる
リポジトリのどこで問題が発生しているのか確かめるため、該当リポジトリをコピーして、新しく作ったGitHubのリポジトリ上でワークフローを実行してみました。
ここでも問題なく差分が出力されたため、コード上には問題がなく、リポジトリの設定などに問題がありそうだということがわかりました。

### ワークフローのファイル名の大文字と小文字を修正
`.github/workflows/storeScreenShot.yml`
↑StoreScreenShotのShotのSが大文字になっていたので、ファイル認識に問題が発生しているのかと思い、修正しました。
`.github/workflows/storeScreenshot.yml`

しかし、結果は変わらず。

### GitHubのActionsの設定を見直す
権限が上手くいってないのではないかと思い、
GitHubのsetting -> Actions -> GeneralのWorkflow permissionを、

`Read repository contents and packages permissions`から
`Read and write permissions`
に変えてみました。が、これも結果は変わらず

# 原因
過去にmainで実行されたStoreScreenShotのアーティファクトが期限切れになっており、ワークフローを参照できなくなっていたことが原因でした。

mainで一度StoreScreenshotが実行されるようにワークフローを動かした結果、無事差分が出力されるようになりました。

![Gf3TYsDaIAAy75E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524029/3ae225d8-ca88-8dc7-82e8-874e9a54f8ac.jpeg)
vcsimの小ネタ: 時間のかかるタスクを再現する

久しぶりのvcsimの記事です。過去の記事はこちら。

[vcsimの小ネタ: デバッグ情報を確認する](https://qiita.com/Syuparn/items/b44d7e2a586018fb4e0f)
[vcsimの小ネタ: ID生成の仕組み](https://qiita.com/Syuparn/items/82191a55e314005481d2)
[vcsimの小ネタ: disk作成](https://qiita.com/Syuparn/items/5bafc3d5315a2b53f17f)
[vcsimの小ネタ: vCenterをコピーして再現](https://qiita.com/Syuparn/items/ceafefa867551337697e)

# TL; DR

```bash
$ vcsim -method-delay 'PowerOnVM_Task:5000'

$ time govc vm.power -on DC0_C0_RP0_VM0
Powering on VirtualMachine:vm-68... OK

real    0m5.024s
user    0m0.000s
sys     0m0.018s
```

# はじめに

vCenter上のオブジェクト操作をリクエストした場合、操作完了を待たずタスクを返してくるものがあります。
本記事では、vcsim上で完了に時間がかかるタスクを再現する方法を紹介します。タスク完了待機でタイムアウトする場合等を再現したいときに使ってみてください。

# バージョン

```bash
$ vcsim version
Build Version: 0.46.3
Build Commit: e5dcb5f7
Build Date: 2024-12-12T20:11:46Z
```

# タスクを遅延させる

`-method-delay` オプションを使用します。カンマ区切りで `{メソッド名}:{遅延時間(ms)}` を指定することで、指定したメソッドのタスク完了を遅延させます。

試しに、VM起動を遅延させてみます。

```bash:vm起動を5秒遅延
$ vcsim -method-delay 'PowerOnVM_Task:5000'
```

想定通り、完了に5秒かかりました。

```bash
$ time govc vm.power -on DC0_C0_RP0_VM0
Powering on VirtualMachine:vm-68... OK

real    0m5.024s
user    0m0.000s
sys     0m0.018s
```

一方、指定していないメソッドについてはデフォルトの挙動で即時完了します。

```bash
$ time govc vm.power -off DC0_C0_RP0_VM0
Powering off VirtualMachine:vm-68... OK

real    0m0.022s
user    0m0.018s
sys     0m0.000s
```
Garnetでevalを使用するためのメモ

# TL; DR

`--lua` が必要

# はじめに

GarnetはRedis互換を謳うキャッシュストアです。

https://microsoft.github.io/garnet/

一方で、互換性が無い部分やデフォルトでは無効化されている機能もあります。
Luaスクリプトによる `EVAL` コマンドもそのひとつで、デフォルトでは無効化されています。

```bash
$ docker run -it --rm -p 6379:6379 ghcr.io/microsoft/garnet:1.0.48
```

```bash
$ redis-cli -p 6379
127.0.0.1:6379> EVAL 1
(error) ERR This instance has Lua scripting support disabled
```

# バージョン

- Garnet `1.0.48`

# 設定方法

Garnet 起動時に `--lua` オプションを指定してLuaスクリプトを有効化します。

```bash
$ docker run -it --rm -p 6379:6379 ghcr.io/microsoft/garnet:1.0.48 --lua
```

今度はスクリプトの実行結果が得られました。

```bash
127.0.0.1:6379> EVAL "return ARGV[1]" 0 hello
"hello"
```

# なぜ無効化されている？

パフォーマンスの課題があるため、Garnetとしては[extensibility](https://microsoft.github.io/garnet/docs/extensions/overview)が推奨されているようです。

https://microsoft.github.io/garnet/docs/welcome/compatibility

> Note however, that Lua scripts are slow in general, and if performance and scalability are important, you should instead use our server-side extensibility mechanisms instead.
令和6年度秋期 情報処理安全確保支援士試験に合格した感想


こんにちは。
現在20歳でSIer系の上場企業に従事している、sirroと申します。

この度、10月に受験した情報処理安全確保支援士試験に合格しましたので、その感想を記録として残しておきます。


# 筆者のスペック(資格面)
2022年10月、基本情報合格。
2023年4月、応用情報不合格。
2023年10月、データベーススペシャリスト合格。

# 筆者のスペック(業務面)
主にJava系案件の開発をメインに担当しており、セキュリティ関係の業務をした経験はなし。

# 試験結果
午前I → 昨年10月にデータベーススペシャリスト試験に合格していることにより、免除。
午前II → 60点。
午後II → 大問1 & 大問３を選択。73点。

![IMG_4755.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982162/0bf02792-feb7-c268-3b14-432f85ba9c2d.jpeg)


# 勉強法
午前問題は「徹底攻略 情報処理安全確保支援士教科書 令和6年度」を読んだ後にひたすら過去問道場を回して対策。試験1ヶ月前の時点では数十問といて8割解けるほどの理解度に達していた。

https://www.amazon.co.jp/%E5%85%A8%E6%96%87PDF%E3%83%BB%E5%8D%98%E8%AA%9E%E5%B8%B3%E3%82%A2%E3%83%97%E3%83%AA%E4%BB%98-%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5-%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E5%AE%89%E5%85%A8%E7%A2%BA%E4%BF%9D%E6%94%AF%E6%8F%B4%E5%A3%AB%E6%95%99%E7%A7%91%E6%9B%B8-%E4%BB%A4%E5%92%8C6%E5%B9%B4%E5%BA%A6/dp/4295017841/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=86NFATNBNKYY&dib=eyJ2IjoiMSJ9.UXL4Hm7dtNClEngd1EOo0I265oICGy9zyicc5sAFVYOUp-BSTsdVoC__3_bv8NdqGT_d3-1ydkhjpthMWVPSQe2WnaZ9Tdm2S5hEPfDBQgiW6uIcqIawlU3_hwPrEYFTxzNsxPqGvADLRIg4LnKBfK59eG9IyNVOTbLLqefNIGxNjSJ7VM3YcDoy_3CMpdbeBc-DlhLLkrvvDKbvP3pntVi0--XuyJduW748DxQbq2s.l9k1yafeVCAX94X9OX0KPm7RmPWuNg2RlvKhPZnJqDQ&dib_tag=se&keywords=%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5+%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E5%AE%89%E5%85%A8%E7%A2%BA%E4%BF%9D%E6%94%AF%E6%8F%B4%E5%A3%AB%E6%95%99%E7%A7%91%E6%9B%B8+%E4%BB%A4%E5%92%8C6%E5%B9%B4%E5%BA%A6&qid=1735446794&s=books&sprefix=%E5%BE%B9%E5%BA%95%E6%94%BB%E7%95%A5+%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E5%AE%89%E5%85%A8%E7%A2%BA%E4%BF%9D%E6%94%AF%E6%8F%B4%E5%A3%AB%E6%95%99%E7%A7%91%E6%9B%B8+%E4%BB%A4%E5%92%8C6%E5%B9%B4%E5%BA%A6%2Cstripbooks%2C207&sr=1-1


午後問題は「徹底攻略 情報処理安全確保支援士教科書 令和6年度」内の午後対策問題を解くのと、試験1ヶ月ほど前に「フロントエンド開発のためのセキュリティ入門 知らなかったでは済まされない脆弱性対策の必須知識」を購入しセキュアプログラミング系分野の対策。
午後のセキュアプログラミング系問題は過去10年分ほど全て解いた。

https://www.seshop.com/product/detail/24997

# 試験戦略
セキュアプログラミング系問題で8割、その他で最低4割取って合格を目論んだ。

https://x.com/DB_sirro_SE/status/1840031596675772742

# 試験直後の感想
以下記事にまとめている。
一言で言うと「受かってても落ちてても納得する」手応えだった。
戦略だった「セキュアプログラミングで8割」は達成を確信したが、「その他で4割」が微妙な感触だった。

https://qiita.com/sirro/items/4da3b6ffa79d56c04510

# 令和6年度秋季試験結果詳細
応募者数 → 24032人。
受験者数 → 17324人。
合格者数 → 2615人。
合格率 → 15.1%。

合格率15%という数字は、平成29年に「情報処理安全確保支援士」が創設されるなどして試験区分が変更されてから最低の結果となった。


# 試験結果を受けての感想
午前問題は正解が一つしかなく、自己採点が可能なのでマークミスさえなければ合格していることはわかっていた。
午後問題は長文で答える問題も多く、部分点等がどれほどあるかも微妙だったので、結果を見て少し驚いた。
個人的には60前後だろうと見込んでいたのだが、予想外の70点代。
恐らく以下のように得点していたと思う。

・大問1(インシデントレスポンス) → 5割(25〜30点)
・大問3(クレジットカード情報の漏洩) → 9割(45〜50点) 

「セキュアプログラミング系問題で8割、その他で4割」という戦略はハマり、合格することが出来た。
大問3はともかく、大問1は予想以上に高得点だったので、恐らくテキトーに書いた長文回答がそれなりに部分点をもらったのだと思う。

合格率が近年に例を見ないほど低水準だったにも関わらず、しっかり合格することが出来たのはとても嬉しく思う。

これで、IPA国家試験には４戦3勝。このうち高度区分試験には2戦2勝と、まずまずの成績を残すことが出来た。
今の所他の高度区分試験を受験する予定はないが、次何か受けるとしても合格する自信が十分にある。

https://x.com/DB_sirro_SE/status/1872224934090727503

# まとめ
以上。令和6年度秋期 情報処理安全確保支援士試験に合格した感想でした。
本記事について質問等あればコメント頂けますと幸いです。
また、Twitterでも有益な情報を発信しているつもりですのでフォローして頂けますと幸いです。

https://x.com/DB_sirro_SE
そのAI活用はそそられないヽ(´Д`;)ﾉ

そのAI活用の未来に何があるのか？
その未来に人間が価値を感じるのか、というお話。

あなたは以下のものに**お金を払う**気持ちになれますか？
 - AIが作ったアイドルとそのコンサート
 - AIが作った料理のレシピとその料理
 - AIが作った音楽やオーケストラ
 - AIが作った小説やポエム
 - AIロボットが戦うボクシング（格闘技）
 - AIが自動運転でレースするサーキット
 - AIが書いた漫画や映画
 - AI声優のアニメ

どうでしょう？そそられますか？
私は全くそそられません。なぜでしょうね？

AI生成のクリエイティブ作品・サービスに「お金を払う気がしない」「そそられない」という感覚は、実は多くの人が（程度の差こそあれ）抱いているかもしれません。
なぜそう感じるか、いくつか考察してみます。

## 1. 「人間性」や「物語」がない

たとえば、人がアーティストに魅力を感じるとき、作品そのものが優れているという点だけでなく、そこに至るまでの“人生”や“物語”に惹かれている面が大きいですよね。  
- 苦労や失敗を重ねて成功したエピソード  
- 裏話や人生観、人柄  
- 生身の演奏家・役者が舞台で汗や息遣いを感じさせるライブ感  

そうした「人間味」と「背景にある物語」を買っていると言ってもいい。
AI生成物はどうしても“物語不在”になりやすい。レシピが美味しかったとしても、「その料理が生まれるまでのストーリー」に共感できず、愛着が湧きにくい。
どんなに音楽のクオリティが高くても、「演奏者の人生」がにじみ出ないとファンになりづらい。

## 2. 創作の“痛み”や“リスク”を感じられない

人間が何かを作るときには、試行錯誤、失敗、時間と労力の投入、あるいは恥をかくリスクなど、創作者の“痛み”が不可避です。
その“痛み”を感じ取れると共感が生まれ、「お金を払ってでも応援したい・体験したい」という気持ちが湧いてきます。

一方、AIは演算リソースの消費こそあれ、心身的な「痛み」を持ちません。
また敗北や恥といったリスクも限定的です。
ロボット同士の格闘やレースを見ても、“命がけ”とか“執念”といったヒリヒリ感は伝わってきません。
「がんばれ！」と思うのはロボット本体ではなく、開発者やエンジニアへ向けてになりがち。
観客としてはピンと来ないのです。

## 3. 作り手とのつながりが希薄

音楽でも料理でも本でも、実際のところ私たちは“作り手”とコミュニケーションしたいのかもしれません。  
- 料理なら「作ったシェフがどんな人か？」「こんなに大変な作業をしてるのか！」というドラマ性  
- アーティストならSNSやライブで直接語られる言葉や舞台裏  

AIキャラ（AIアイドル等）は、いくら見た目が可愛くても、そこに“共感”や“愛着”を抱けるかどうかは人それぞれ。もし人間がバックで運営しているにしても、「中の人」が感じられないと、ファンとアイドルの“人間関係”が生まれにくい。  
要するに、**AIの後ろにいる人間とのつながり**が見えにくいために価値を感じられず、お金を払おうという気持ちが起きにくいわけです。

## 4. 「希少性」が薄い

人間が手間暇かけた作品には、どうしても**ある種の希少性**が伴います。職人の手仕事、ライブの一回限りの演奏、アーティストの限りある人生から生まれた作品……これらは「同じものをまたつくれる」とは言いづらい。

しかしAI生成物は、もともとデジタルデータであるため、**いくらでも複製できる**という印象が強い。
何度でも新しいパターンを作れてしまうし、失敗してもすぐ修正が利く。
結果として、「このAI作品を逃したら二度と見られない」とは感じにくいので、“希少性を買う”というモチベーションも下がるのです。

## 5. 「本当に面白いのか？」という疑い

まだ現時点では、AIが自律的に生み出す作品やエンタメが「人を心から楽しませるレベル」に達しているかどうかは意見が割れるところです。  
- クオリティは一定水準を超えているが、どこか“機械的”で感動が薄い  
- “人間に受ける”というデータをベースに作られているから、トレンドに迎合しすぎて型にはまる  

こうしたことから、「AI作品は本当に面白いのか？」という疑問が生まれ、そこにお金を払おうと思わない人もいるでしょう。つまり**AIが描く意外性**にまだあまり期待できないわけです。

## 6. 「人間から奪うのか？」という抵抗感

最後に、モラルや経済的な観点として「AIが普及すると、人間の仕事や創作の機会が奪われてしまう」という懸念が根底にあるかもしれません。
AIが作ったものに積極的にお金を払うのは、なんとなく**自分たち人間の首を絞める**ような印象を抱かせる、と感じる人もいるでしょう。

音声合成（AI声優）などは、声優という職業へのリスペクトがあると抵抗を覚える可能性もある。
「この声、実は人間の役者じゃなくて全部AIだったの？」と知ったら興ざめしてしまう……そのような思いが購買意欲を削ぐ要因にもなります。

## まとめ

- 人間の“物語”や“痛み”が感じられない  
- 作り手とのつながりが希薄  
- 希少性が薄い  
- 本当に面白いのか・意外性があるのか疑問  
- 人間の仕事や文化を脅かすかもしれない不安  

AI製コンテンツやAIアイドルに対して、「そそられない」理由としては、こうした心理的要因が絡み合っていると考えられます。

もちろん、将来的にはAIを逆手に取って“人間のドラマ”をもっと引き出す仕掛け（裏にいるクリエイターを前面に出す運用など）や、希少性を生み出す仕組み（NFTのような形[^1]）も登場しつつあります。
しかし、現時点では**AIが作ったものそのものに熱狂的な価値を感じにくい**という見方は、ごく自然だと言えるでしょう。

次回は「そそられるAI活用とは？」を書きます。

[^1]:「NFT（Non-Fungible Token）のような形」というのは、簡単に言えば、AIが作りだす無数のコピー可能な“データ”に、“唯一無二の証明”や“限定的な価値”を付与する仕組みのこと。
[Rails][Nokogiri][Glibc] version `GLIBC_2.28' not found が出た場合 対処法




## 概要

丁度今週の 12/24に
gem Nokogiri 最新版1.18.0 が出たようで
Railsなどで ~~bundle update(install)すると~~  <- でなくて、railsを読み込むと(require 'nokogiri') だった..
以下エラーで止まってしまう

```
...
bin/rails aborted!
LoadError: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by /home/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/nokogiri-1.18.0-x86_64-linux-gnu/lib/nokogiri/3.2/nokogiri.so) - /home/user/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/nokogiri-1.18.0-x86_64-linux-gnu/lib/nokogiri/3.2/nokogiri.so (LoadError)

```

参考
https://travis-ci.community/t/glibc-2-28-not-found-reported-after-nokogiri-1-18-0-released/14494


折角なので、CentOS7 のglibc をupdateしようと思ったが..

## NokogiriのGlibc 問題

結論からいうと、
glibcはシステムのベースになっており、多くの基本ライブラリが依存しているため
変更するとシステムが壊れる可能性が高い
(`ls` などほとんどのコマンドが使えなくなり、更にkernel panicで 立ち上げ=ログインもできなくなる..)

壊れた場合、
別のOSを立ち上げて ファイルシステムをmount して調整となるが..
そう簡単にいかないようだ..

## 結論

Glibc は上げずに、Nokogiri をdowngrade(version 下げる) 指定を加える ほうが一旦はよいと思われる。

(Glibc 問題は Nokogiriに限らず、だが..)

```:Gemfile
gem 'nokogiri', '~> 1.17.0' # 1.17.x の最新バージョン
```
https://rubygems.org/gems/nokogiri/versions/


(Glibc のversion 調整は、難易度が高いため、
仮想環境であれば OS自体を 新しいGlibc対応している新しいものに変えたほうがよい(早い) と思われる)
データエンジニアリングを学び、共有し、共に成長する場、datatech-jpに参加しませんか？

## datatech-jpとは

データエンジニアやデータ活用に関心のある人たちが集まり、知見や経験を共有し合うデータコミュニティです。現在 (2024/12/29時点) で1562人が参加していて、輪読会やカジュアルなトークイベント、もくもく会、アドベントカレンダーなど、多彩な活動を通して、参加者同士が学び合い、成長できる場を提供しています。

「データ基盤やエンジニアリングについて語れる場がもっと欲しい」「自分の経験や課題を共有し、他の人の知見を得たい」といったニーズを持つ方にとって、datatech-jpはまさに理想的な場です。


https://datatech-jp.github.io/

## datatech-jpで得られたメリットと学び
自分自身もdatatech-jpに2021年頃前から参加させてもらっていて、多くの学び、経験を得ることができたので、自分が実際に感じたメリットを3つ紹介できればと思います。

### 1.  1人データエンジニアをやっている時の支え
データエンジニアは会社において、そんなに多い人数いないことが多いと思います。特にデータ基盤を作り始めた頃は1人だけしかいないということは多いでしょう。自分も例外なくデータエンジニアを始めてからの最初の数年間は正社員としては1人データエンジニアでした。

このとき、データ基盤構成を相談したり、レビューしてもらったりということをしたかったんですが社内では難しく、datatech-jpに積極的に参加始めました。最初は、「The Self-service Data Roadmap」の輪読会に参加させてもらいつつ、輪読会を通して参加者同士でデータ基盤のあれこれを壁打ちしたり議論できるようになりました。

そうして、1人では解決が難しかった課題を他の参加者と一緒に解きほぐしていけるようになりました。何より、同じような状況で頑張っている人たちと繋がることで、「自分だけじゃない」と感じられたのは大きな精神的支えになりました。


### 2.  輪読会の意義
上記と被りますが、輪読会は特に印象的で価値の高い取り組みだと思っています。輪読会では、基本的に10名ほどの参加者が書籍を分担して読み、それぞれが解釈をスライドやNotionで共有しながら議論を深めます。この形式により、個人で読むよりも効率的に学びを深めることができます。

これまで自分は、「the self-service Data Roadmap」、「実践的データ基盤への処方箋」、「Data Quality Fundamentals」などデータエンジニアリング、データマネジメントに渡る幅広いジャンルの輪読会に参加してきました。

異なる業界や規模の会社で働くデータエンジニアの方々と、輪読会を通して議論できるのは、とても貴重で素晴らしい機会であり、自分では気づけなかった視点や他社事例を学べることで、1人で読むときよりも解釈も深まるし、その場でslackやmeetのコメントを通じて質問すると誰かが答えてくれるため、疑問点などの解消もすぐできることが多かったです。
    
### 3. 発信のきっかけ
datatech-jpでは輪読会だけじゃなく、様々な発信の機会があります。今書いているアドベントカレンダーもその一部ですが、[yoshidaさん](https://x.com/syou6162)が企画してくださる「Casual Talks」や、[みっつさん](https://x.com/kaz3284)らが企画してくださる「みんなの考えた最強のデータアーキテクチャ」も定期的に開催されていて、とても多くの参加者が集まっています。

発信は、自分の考えを整理できるだけでなく、他の参加者からフィードバックを得ることで、さらにアイデアがブラッシュアップされるというメリットがあります。

自分は最初、輪読会で小さな部分の発表から始め、次第に「Casual Talks」で自社のデータ基盤についてプレゼンするなど、より広範な内容に挑戦するようになりました。人数が少ない頃から発信をすることで、自分のデータに関する解釈や考えに対してよりフィードバックが得られ、自社でデータ基盤アーキテクチャや、データのロードマップへの改善へと繋げることができました。

## 2025年のdatatech-jp
2025年もdatatech-jpでは、さまざまな輪読会やイベントが予定されています。
すでに、「datatech-jp Casual Talks #6」と「みんなの考えた最強のデータアーキテクチャ〜2025もやってきましょうSP！」は決まっていて面白そうな発表がたくさんなのでぜひ参加してみてください。

https://datatech-jp.connpass.com/event/337374/

https://datatech-jp.connpass.com/event/335207/

また、[tsuda](https://x.com/tendon0)さんが月曜の夜にもくもく会を企画してくださっています。Xのスペースから気軽に参加できるのでぜひ参加してみてください!

https://bynatures.hatenadiary.jp/entry/2024/12/03/022545

ということで、自分は2025年もdatatech-jpを通してデータエンジニアリングを学び、共有し、成長していこうと思います。オーガナイザとしてイベントもいくつか企画できたらと思ってます!

ぜひこれを読んでdatatech-jpへ興味を持っていただけたら嬉しいです!参加をお待ちしています!
いつも参加してくださっている方々は2025年もよろしくお願いします!

datatech-jpへの参加はこちらの[リンク](https://datatech-jp.slack.com/join/shared_invite/zt-xczl1j9v-smCNqbFw0tSN~ZFxuAQA3g#/shared-invite/email)から!
Goにおけるマップ

## Maps

キーと値とを関連付ける。

```Go
package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m map[string]Vertex

func main() {
	m = make(map[string]Vertex)
	m["Bell Labs"] = Vertex{
		40.68433, -74.39967,
	}
	fmt.Println(m["Bell Labs"])
}
// {40.68433 -74.39967}
```

#### マップの初期化

```Go
package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}

func main() {
	fmt.Println(m)
}
// map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
```

### 挿入、更新

```Go
m[key] = elem
```

### 要素の取得

```Go
elem = m[key]
```

### 要素の削除

```
delete(m, key)
```

### 要素が存在確認

2つの目の値で要素の存在確認の結果を取得できる。
```m```に```key```があれば、変数```ok```は```true```となり、存在しなければ、```ok```は ```false```となる。
mapに```key```が存在しない場合、```elem```は```map```の要素の型のゼロ値となる。

```
elem, ok = m[key]
```
【Web】サーバーの負荷分散

# はじめに
こんにちは。アメリカで独学でエンジニアを目指している者です。

現在、Webについて勉強しており、特にシステムのスケーラビリティと可用性を高める技術として負荷分散について学んでいます。負荷分散に関連する技術として、ロードバランサーが果たす役割や代表的な分散方式であるラウンドロビン方式、動的分散方式、およびパーシステンスについてまとめました。

## 負荷分散とは？

負荷分散とは、複数のサーバーやリソースに対してクライアントからのリクエストを効果的に分配する技術です。
これにより、各サーバーの負荷を均等化し、システム全体のパフォーマンス向上や可用性の確保が可能になります。
ロードバランサーは、この負荷分散を実現するための専用のデバイスまたはソフトウェアです。

## ロードバランサーの主要な方式

ロードバランサーにはさまざまな分散方式が存在しますが、ここでは代表的なラウンドロビン方式、動的分散方式、およびこれらのデメリットをカバーするパーシステンスについて詳しく説明します。

### 1. ラウンドロビン方式

ラウンドロビン方式は、最もシンプルで広く使用されている負荷分散方式の一つです。この方式では、ロードバランサーが受け取ったリクエストを順番に各サーバーに割り当てていきます。具体的には、最初のリクエストをサーバーAに、次のリクエストをサーバーBに、その次をサーバーCに…という具合に繰り返します。

#### 特徴:

- **シンプルで実装が容易**: 特別な設定や状態管理が不要。
- **均等な負荷分散**: 各サーバーに均等にリクエストが分配される。

#### 欠点:

- **サーバーの性能差を考慮しない**: 高性能なサーバーと低性能なサーバーが混在する環境では最適な負荷分散ができない。
- **セッションの一貫性が確保されない**: 特定のユーザーセッションが複数のサーバーに分散される可能性がある。

### 2. 動的分散方式

動的分散方式（ダイナミックロードバランシング）は、各サーバーの現在の負荷状況や応答時間などをリアルタイムで監視し、それに基づいてリクエストを動的に分配する方式です。この方式では、負荷が高いサーバーにはリクエストを少なくし、負荷が低いサーバーに多くのリクエストを割り当てることで、全体のパフォーマンスを最適化します。

#### 特徴:

- **負荷状況に応じた最適な分散**: サーバーの状態を反映してリクエストを分配するため、効率的な負荷分散が可能。
- **柔軟性が高い**: サーバーの追加や削除が容易に反映される。

#### 欠点:

- **実装が複雑**: リアルタイムでの負荷監視と判断が必要。
- **オーバーヘッドが増加**: 負荷情報の収集と処理にリソースが必要。

### 3. パーシステンス（セッション持続性）

パーシステンス（セッション持続性）は、特定のクライアントからのリクエストを常に同じサーバーに割り当てる方式です。ラウンドロビン方式や動的分散方式では、同じクライアントのリクエストが異なるサーバーに分散される可能性があり、これがセッション情報の管理を困難にします。パーシステンスを導入することで、ユーザーセッションの一貫性が保たれ、状態を保持したサービス提供が可能となります。パーシステンスは、ユーザー認証やカート情報の保持など、セッション情報を必要とするアプリケーションにおいて重要です。

#### 実装方法:

- **IPアドレスによる方法**: クライアントのIPアドレスを基に特定のサーバーに割り当てる。
- **クッキーによる方法**: クライアントにクッキーを設定し、その情報を基にサーバーを選択する。

#### 特徴:

- **セッションの一貫性**: 同一ユーザーのリクエストが常に同じサーバーに届くため、セッション管理が容易。
- **ユーザー体験の向上**: 状態を保持したサービス提供が可能

#### 欠点:

- **負荷分散の不均一化**: 特定のサーバーにリクエストが集中する可能性がある。
- **スケーラビリティの制限**: サーバーの追加や削除がパーシステンスの設定に影響を与える場合がある。


## まとめ
負荷分散には主にラウンドロビン方式と動的分散方式があり、サーバーの処理能力が同等であれば前者、サーバーごとに処理能力が異なる場合は後者を選ぶのが適しています。しかし、これらの分散方式を単独で使用すると、ユーザーセッションの一貫性を保つことが難しくなることがあります。そこで、パーシステンスを併用することで、セッション情報の管理を容易にし、ユーザー体験を向上させることが可能です。
テクニカルライティングについて真面目に考える 2/n

# はじめに

このエントリは、（ソフトウェアの）テクニカルライティングの文章について考えた結果をまとめる連載の2つ目である。
なお、このエントリは勤務先で新人や若手向けに行おうとしているテクニカルライティング講義の予習としてまとめているものである。

* イントロ：テクニカルライティングに求められる文章とはなにか https://qiita.com/pujeba/items/b4be9417c018b66c1f82

# なぜソフトウェア技術者は日本語を書かねばならないのか

連載の2つ目では、「そもそもなぜソフトウェア技術者は日本語を書かねばならないのか」という問いについて考える。
単に「エンジニア」といった際に「ソフトウェアエンジニア」を暗黙的に指すことも多くなったが、いったん漢語の「技術者」に読み替えて論を進めてみる。

最近Twitterの一部界隈にウケている書籍「誇り高き技術者になろう」（名古屋大学出版会）では「技術者の仕事は、自分の判断に基づいて他の人の生活に大きな影響を与えることができる」としている。すると、「私はXXという根拠を持ってYYという判断をして、ZZという製品（あるいはコンポーネント）を作りました」と説明する責任が生じる。このとき、「ソースコードを読んでください」で責任が果たせればよいのだが、残念ながらそうはいかない。この理由は大きく2つある。

1. 誰もがソースコードを読めるわけではない
1. ソースコードの背景にある思想や使い方をソースコード自身は説明しない

お仕事としてあなたが作るソフトウェアに関わる人はコーダーだけではない。エンドユーザーや営業、それにヘルプデスクといった比較的内部構造に詳しくない人も多くいる。そういった人に「ソースコードを読め」というわけにはいかない。作った人が作ったものを日本語で伝える責任を負う。
更に、ソースコード自身は「なぜその設計を選択したか」「プログラム全体としてどのように使うか」ということに関しては説明してくれない。背景や前提、それにボツとなった実装案を全部ソースコードコメントに記録するというゴリ押し案も理論上はありえるが、そんなことをしたらコードの保守性は壊滅的に悪くなってしまう。何より、プロプライエタリソフトウェアの場合、社外の人間にソースコードを開示するわけにはいかない。

ソフトウェアの背景や使い方をソースコードから自然言語で書かれたドキュメントとして分離することで、ソフトウェアのコアであるソースコードと説明を分離し、ドキュメントは性質に応じて技術者からの距離が遠い立場の人たちにも公開するのである。

先の説明でも少し触れたが、お仕事としてのソフトウェアで最も重要なことは「様々な立場や技能を持った人が集まって一つのものを作り上げている」という事実を常に心の何処かにもっておくことである、と私は考える。これはコーダーのみに限った話ではなく、SEや営業、ヘルプデスク、マネージャーに対してもいえる。
特に難度の高い製品を作るとき、誰かを動かさなければ良い仕事はできないし、良い仕事ができなければ修正や作り直し、最悪の場合は勤務先の経営難という形で将来の自分たちの首を絞めることになる。そして誰かを動かすためには自分の思っていることを整理して、相手に寄り添った説明をして、納得してもらうことが不可欠なのだ。その意味で、テクニカルライティングはビジネス文書に通じるところがあると考えるし、技術者のためだけのものではないとも言える。

# 参考文献

* 黒田光太郎, 戸田山和久, 伊勢田哲治(2004), 「誇り高い技術者になろう」, 名古屋大学出版会
* 高橋麻奈(2005), 「入門テクニカルライティング」, 朝倉書店
nandgameでOS作ってみよう　まとめ

https://qiita.com/yamina-Tjatmg-Gui-Creater/items/1fc9141c45466f79b0e6

https://qiita.com/yamina-Tjatmg-Gui-Creater/items/62c9ac4b87bb3e42957f

https://qiita.com/yamina-Tjatmg-Gui-Creater/items/0dceeaf68082faff4b38

https://qiita.com/yamina-Tjatmg-Gui-Creater/items/b408a5e4287823b6ee9f
nandgameでOS作ってみよう　#0

# サイト
https://nandgame.com/

# 作るものは何？
OSを作りたいです！　そのための小さな処理を実装していきたいです！

# ～作る～
全加算器のレベルを終わると、「playground」が上に出てくるのでそれで作っていきます。
create first programを押すとこんなのが出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3907325/95df3c48-598f-37b9-feed-4174161d5189.png)

これで作っていきます。

# まとめ
https://qiita.com/yamina-Tjatmg-Gui-Creater/items/d21d7ab2065663d9dde2
wgetで出力先を指定する

いつも忘れるのでメモ

# `-P` 出力ディレクトリを指定する

すでに同名のファイルが存在する場合はファイルの末尾に `.連番` を付加して保存

```bash
# /tmp/terraform_1.10.3_linux_amd64.zip に出力
$ wget https://releases.hashicorp.com/terraform/1.10.3/terraform_1.10.3_linux_amd64.zip \
  -P /tmp

$ ls /tmp/terraform_1.10.3_linux_amd64.zip
```

# `-O` 出力パスを指定する

すでに同名のファイルが存在する場合は上書き

```bash
# /tmp/terraform.zipに出力
$ wget https://releases.hashicorp.com/terraform/1.10.3/terraform_1.10.3_linux_amd64.zip \
  -O /tmp/terraform.zip

$ ls /tmp/terraform.zip
```
next.jsのpage routerで未保存確認処理

## 概要
入力項目がいっぱいある画面でブラウザバックや不意に画面を閉じる場合、入力内容が保存されない場合は、確認せず閉じてしまうと、UX的にまずい場面が多い。

途中の入力が失わないように確認モーダルを表示した方が親切に思われる。

ただ、next.jsのpage routerではそいう機能がないので、自力で実装する

## 詳細

概ね、RouteLeaveHook登録処理と画面遷移時のチェック処理との２つの部分がある

### 1.RouteLeaveHook登録処理

- beforeUnloadHandler

```tsx
  const handleBeforeUnload = (e: BeforeUnloadEvent) => {
    if (condition) {
      // 仕様標準のメソッドを呼び出す
      e.preventDefault();
      // 離脱時に表示するメッセージを設定
      e.returnValue = '保存されてませんが、本当に閉じますか？';
    }
  };

  // didMount時に登録
  window.addEventListener('beforeunload', handleBeforeUnload);
  // willUnmount時に解除
  window.removeEventListener('beforeunload', handleBeforeUnload);
```

### 2.RouteLeaveHook

各Reactコンポーネントで

- RouteLeaveHookのコールバックをSetに登録
- beforePopStateとhandleRouteStartでSet中のコールバックで確認

```ts
const routeLeaveCallback = (url: string, action: LocationChangeAction) => {
  return routeLeaveCallbackImtOrderedSet.every((callback) => {
    return callback(url, action);
  });
};

  const currentLocationRef = useRef<string>('');
  const currentQueryParamRef = useRef<Record<string, string>>({});
  previousLocationImtMap = useSelector((state) => state.app.getPreviousLocationMap());

  useEffect(() => {
    router.beforePopState(({ as }) => {
      if (currentLocationRef.current !== as && !routeLeaveCallback(as, 'POP')) {
        const historyState = window.history.state as NextHistoryState;
        const isBackClicked =
          historyState?.as === previousLocationImtMap?.get('asPath') &&
          historyState?.key === previousLocationImtMap?.get('key');
        window.history.go(isBackClicked ? 1 : -1);
        // 遷移しない
        return false;
      }

      historyAction = 'POP';
      // 遷移する
      return true;
    });

    const handleRouteStart = (asPath: string) => {
      if (currentLocationRef.current !== asPath && !routeLeaveCallback(asPath, historyAction)) {
        router.events.emit('routeChangeError');
        throw 'Abort route change. Please ignore this error.';
      }
    };
    ...
```

#### 解説

- handleRouteStartは通常の画面遷移時にrouteLeaveCallbackで確認して、遷移してはいけない場合、routeChangeErrorイベントを発生させて、異常をスローすると、画面遷移が止まる

- beforePopStateはブラウザのForward/backボタンをクリックした時に呼ばれる
  - そこでrouteLeaveCallbackを呼出して確認して遷移してはいけない場合
    - `return false`することで遷移が止まる
    - ブラウザのアドレスが既に変わってしまったので、window.history.goで逆操作により元に取り戻す
      - 公開apiではないが、next.jsのpage routerにはwindow.history.stateに値が設定されている
        - asはurl、keyは一意的なURLのhistoryキー
        - 覚えた前画面のasとkeyが一致であれば、戻ると、そうでなれば、進むと判断する
        - 戻るがクリックされた場合は、go(1)で、進むがクリックされた場合は、go(-1)でブラウザの URLを取り戻す
Tauriでinvokeを使用した際、「TypeError: Cannot read properties of undefined (reading 'invoke')」と出てinvokeが動作しない

上記エラーが出た際の解決法です。

## 解決法

開発者ツールを使用するため、ブラウザからUIの動作確認を行っていたことが原因だった。
そのため、実際のアプリケーションから実行したら、正常に動作した。

## 結論

エラーの要因がはっきりしていない時は、まず環境などのコード以外の差をなくしてから確認すべきだった。
JISOUに参加しました！

# アウトプットをするぞ！

自己啓発本や自身の経験、ブログや記事を見てきてアウトプットが一番大事なことはよくよく分かっているはずなのに全然出来ていない！多少はするようになってきたもののまだまだ足りない！
と感じていたのでQiitaでいつも参考にさせていただいている渡邉さんが運営するJISOUに参加させていただきました。

# 自己紹介
28歳でIT業界に入って3年目になります。
もともとは事務的な仕事をしながら将来になんの希望も持たずにただ日々を過ごしているような人間でした。コロナなどの環境の変化から転職をすることになり、未経験でしたが、ちょっとしたこねで自社開発の企業にエンジニアとして入社しました。
最初はなにもわからないし、全然馴染めずやる気もあまりない状態という最悪な始まりでした。しかし同時期に入社した若い方々の影響を受けてモチベーションが次第にあがっていき、スキルもみるみる成長していくのを感じていました。
やる気をもって楽しくやっていると、もっとスキルアップやキャリアアップをしていきたいという欲求もでてきて、今年に入ってからSES企業に転職をしました。しかしそれは失敗でした。エンジニアとして入社したものの開発の案件には触れさせてもらえず、嘘を平気でつくような人間ばかりで精神的にかなり苦痛を受けました。ネットにはSESはやめとけなどとよく書かれていましたがそこまで酷くはないだろうと安易に考えていたことを非常に悔いています。
ただ、こんなところで終わりたくないので怒りを糧にして頑張っていきます！
# 目標
個人開発で生活できるくらいの技術者を目指す！
まだ具体的にこれがやりたいといったものがないので具体的な目標をたてる！

## PS
これだけで15分を軽く超えていました！
しっかり時間も考えて行動していきます。

# JISOUのメンバー募集中！
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
興味のある方は、ぜひホームページをのぞいてみてくださ！
▼▼▼

https://projisou.jp
バイト詰め (byte stuffing)

### フラグバイトの前にエスケープ・バイトを詰めて通常フレーム化と区別する
>特に写真や歌のような２進データが伝送されている時には、データの中にフラグ・バイトが出現することが起こりうる。このような状況は通常フレーム化と干渉してしまうだろう。この問題を解決する一つの方法は、送信側のデータ・リンク層がデータ中に「たまたま」現れるフラグ・バイトの直前に特別なエスケープ・バイト(ESC)を挿入することである。したがって、フレーム化のためのフラグ・バイトは、その前にエスケープ・バイトがあるかないかでデータ中のものと区別される。受信側のデータ・リンク層はネットワークそうにデータを渡す前にエスケープ・バイトを削除する。この技法をバイト詰め（byte stuffing)と呼ばれる。

### フラグ・バイト(flag byte)　フレームごとに区切りをつける
>第二のフレーム化手法は、それぞれのフレームが特別なバイトで始まり、終わるようにすることで、誤りが起こった場合はの再同期の問題を乗り越えている。しばしばフラグ・バイト(flag byte)と呼ばれる同じバイトが開始区切りと終了区切りの両方に用いられている。このバイトは図3−4(a)ではフラグと示されている。二つの連続するフラグ・バイトは一つのフレームの終わりと次の始まりを示している。このようにして、受信側が同期を失うようなことがあっても、二つのフラグ・バイトを探すことで現在のフレームの終わりと次のフレームの始まりを見出すことである。

https://bookplus.nikkei.com/atcl/catalog/23/03/16/00728/
【TypeScript】カレー作りで理解するZodライブラリ

# はじめに
Zodライブラリは、TypeScriptの型安全を保証するために、データの構造を定義し、それが正しいかどうかを検証するためのツールです。このライブラリは、型定義とバリデーションを簡潔に行うことができ、特にAPIとのやり取りやフォーム入力の検証に役立ちます。

今回は、Zodを使ってバリデーションを行う方法について、カレー作りの例を交えながら説明します。

# プログラミングは料理と同じ
料理をしたことがない人に、いきなり「フランス料理を作って」と言われても、それを実現するのはとても難しいでしょう。料理では、まず基本的な技術や知識を学ぶことが大切です。食材の切り方、調味料の使い方、火加減の調整など、一つひとつの要素を理解していくことで、徐々に複雑な料理にも挑戦できるようになります。

プログラミングも同じです。クラスや関数など、多くの概念を一度に理解するのは難しく感じるかもしれません。しかし、基本的な構文やロジック、ツールの使い方などを少しずつ学んでいくことで、次第に複雑なアプリケーションやシステムを構築できるようになります。

料理もプログラミングも、基礎を積み重ねることが成功の鍵です。一つひとつの要素を丁寧に学び、経験を積むことで、自分の手で創造的な成果を生み出せるようになるのです。焦らず、着実に取り組んでいきましょう！

# Zodライブラリとは
Zodは、TypeScriptで使用されるスキーマバリデーションライブラリです。Zodを使用すると、オブジェクトの構造やデータ型を事前に定義して、そのデータが正しいかどうかを簡単に検証できます。これにより、アプリケーションのエラーを減らし、開発効率を向上させることができます。

Zodの主な機能は以下の通りです：
- 型安全なバリデーション
- 複雑なデータ構造の定義
- エラーハンドリングの簡素化

Zodを使うことで、データの検証が簡単かつ効率的に行え、プログラムが予期しない動作をするリスクを減らすことができます。

# そもそもスキーマとは・・・
スキーマは、データの構造やルールを事前に定義する設計図のようなものです。プログラムでは、データが所定の形式に従っているかを検証するためにスキーマを使用します。例えば、ユーザーの入力内容が正しいかどうかをチェックする際に、スキーマを使って「このフィールドは文字列でなければならない」とか「このフィールドは必須である」といったルールを設定します。

スキーマを使うことで、データが期待通りの形になっているかを事前に確認でき、後でエラーが発生するリスクを減らせます。これが、特に大規模なアプリケーションや複数のシステム間でデータをやり取りする際に非常に重要になります。

# カレー作りで例えると・・・

Zodライブラリを使用した場合と使用しない場合の違いをカレー作りにたとえて説明してみます。

### Zodを使わない場合: 鍋に入れた後に気づく
材料を確認しないままカレー作りを進めてしまうイメージです。

1. 食材を鍋に投入し、煮込んでから「これ、じゃがいもじゃなくてサツマイモだった！」と気づく。
2. 調味料を入れた後に「塩じゃなくて砂糖を入れてしまった…」と発覚。
3. 煮込みが進んでから、「肉が鶏肉ではなく、魚だった」と判明する。

このように、バリデーションを行わないと、エラーに気づくのが工程の後半になり、修正が難しくなることがあります。結果として、カレーそのものが台無しになる可能性が高まります。

### Zodを使った場合: 鍋に入れる前に事前にチェック
Zodを使うことで、材料を鍋に入れる前にリストを確認し、問題がないことを事前にチェックするイメージです。

1. 鍋に入れる前に「じゃがいもであること」「肉が鶏肉・牛肉・豚肉のいずれかであること」を手作業で確認。
2. スパイスを加える前に、「これは塩だな」「これが適切なカレー用スパイスだな」と確認。
3. 全ての材料がチェック済みなので、安心してカレー作りを進められる。

以下は、実際のコード例です。

```typescript
import { z } from 'zod';

const currySchema = z.object({
  meat: z.enum(['chicken', 'beef', 'pork']),
  vegetables: z.array(z.string()).nonempty(),
  curryRue: z.string(),
});

// 材料データ
const ingredients = {
  meat: 'fish', // 不正なデータ
  vegetables: ['potato', 'carrot', 'onion'],
  curryRue: 'mild',
};

// チェック実行
const result = currySchema.safeParse(ingredients);

if (result.success) {
  console.log('材料はすべてOK！カレーを作り始めましょう。');
} else {
  console.log('材料に不備があります:', result.error.errors);
}
```
### コード解説
1. currySchemaの定義
    - z.object()を使って、材料の条件をオブジェクト形式で定義しています。
    - meatは3種類（chicken, beef, pork）のいずれかである必要があります。
    - vegetablesは文字列の配列で、空でないことをチェックしています。
    - curry文字列であることを指定しています。
2. 材料データの作成
    - ingredientsオブジェクトは実際のデータです。この例では、meatに誤った値（fish）が入っています。

3. バリデーションの実行

    - safeParseを使用して、ingredientsがcurrySchemaに合致するか確認します。
    - result.successがtrueなら条件を満たしており、問題なく作業を進められます。
    - 失敗した場合（result.successがfalse）、エラーの内容がresult.error.errorsに格納されます。
4. エラーハンドリング

    - 条件に合わない場合、エラー内容が表示され、適切な修正が可能になります。
# まとめ
スキーマは、データが適切かどうかをチェックするためのルールや枠組みを提供します。これにより、問題の早期発見が可能になり、後工程でのエラーやトラブルを未然に防ぐことができます。プログラミングにおいても、スキーマを活用することでコードの信頼性を高め、効率的な開発が実現します。
クラス・構造体の再利用の備忘録

## 関数の再利用を超えたところで

オブジェクト指向関連の本を読んだ際の個人備忘録(正確性はあまりないかも)

オブジェクトに留まらずクラスや構造体を利用することによって何が変わるか？

関数の場合は基本的に(※1)
    ・具体的な処理をまとめたもの

であるのに対してクラスや構造体では
    ・具体的な処理や変数を一か所にまとめ、
    ・かつそれらを組み合わせた処理を実現する
この組み合わせた処理が規模の差はあれどフレームワークと呼ばれるものとなる。

つまりクラスや構造体の恩恵は
　具体的な処理の差はあれど共通する処理手順についてまとめることができるようになった点
が最も大きい。(継承などはクラスにしかないが)

||特徴|
|:----|:-----|
|関数|具体的な処理の再利用|
|クラス・構造体|処理手順の再利用|

※1
関数自体も普通に引数として利用できるので、実際にクラス・構造体がないとできないことを考えると関連する処理を1つのオブジェクトにまとめるとかそれくらいなのかとも思ったりする。
vitestを実行したらMiniflareCoreErrorが発生したメモ

# 概要
下記のようなエラーが出て、原因になかなかたどり着けなかったので。

```
MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start. There is likely additional logging output above. 
```

原因は、wrangler.tomlファイルを誤って修正していたこと。

```diff
name = "odyssage-backend"
main = "src/index.ts"
- compatibility_date = "2024-12-27"
+ compatibility_date = "2024-12-18"
+ compatibility_flags = ["nodejs_compat"]
```

[ソースコード](https://github.com/hibohiboo/odyssage/blob/99576f09a5ca85033e894c6ce71aefa6b7b55744/apps/backend/src/index.ts)
第５世代コンピューター計画の続きを（３）

# 古い文献
N-Prologに対して自分なりに分散並列、マルチスレッド並列機能を追加しました。やり終えてみて1980年代のICOTの研究者はどのように考えてGHCを設計したのか？という疑問が湧いてきました。ICOTの主要メンバーの古川康一さんの文献を見つけました。以下はその文献からの一部引用です。

ー引用ー
第五世代コンピュータの 
プロジェクト運営と人工知能の未来  古川康一

4･3　研究の方向性の転換
ICOTは，前期から中期に進む過程で，Prologから並
行論理プログラミングへの大きな方向転換を行った．そ
の転換は，ICOTおよび関連メーカの研究従事者間で大
きな論争を生んだが，著者を中心とする核言語グループ
は，反対意見を抑えて，その方針転換を遂行した．その
根幹にあるのが，Prologと並行プログラミングとの相性
の悪さである．Prologの重要な機能に，バックトラック
による探索機能があるが，並行プログラミングでは現実
世界とのインタラクションが必要であり，後戻りできな
い並行現象を制御しなければならない．これらは相容れ
ないので，我々はPrologの探索機能を捨てて並行機能
を取った．この方針転換は，後述する海外研究者を含め
た並行論理プログラミングの開発競争の中でのICOTの
独創技術の確立を促した．一方，この方針転換は探索機
能を見掛け上犠牲にしたことになるが，この点も後に述
べるように，画期的な技術により取り戻した
ー引用終わりー


# 人間の表面意識と潜在意識
私はGHCの文献を持っています。これを理解したくてElixirの並列機能を利用して自分でもGHC処理系を作ってみようとしたことがありました。一応はできたのですが、どうも仕様が違っていたようです。なかなか理解困難な複雑な言語仕様のように思えました。そのときの経験から、もっと単純で理解しやすいProlog並列ができないものだろうかと考えるようになりました。


古川さんの文献からなぜGHCの方向へ向かったのかを理解しました。しかし、今のハードウェアならもっと違う方向もあったのではないだろうか？と思うようになりました。分散並列で現実世界とのインタラクションと現実世界とは切り離されたところでの深い推論とを別に分担させたらいいのではないだろうか？

人間は日々、活動をしつつ現実の問題に対応しています。リアルタイムで現実世界に対応しないといけませんので、その判断に深い推論をしている時間はありません。経験則などをもとに瞬時に判断をしていると思われます。

しかし、その判断がどうもしっくりせずに考え直し、一晩寝て、翌朝、それは違う！もっと良い方法がある！と閃くことが多々あります。潜在意識は意識の背後において時間をかけて推論を繰り返しているのだろうと思います。

![bandicam 2024-12-29 10-04-37-336.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33927/da32d35f-239d-392b-13b4-aaa4dd65738f.jpeg)


これを分散並列Prologに応用できないだろうか？と思っています。とりあえずは既存の知識ベースで対応するけれども、より深い推論を分散したProlog処理系に推論させておいて、その結果を待つというアイディアです。これならばPrologの特長であるバックトラック、深い推論を活かすことができます。

# 夢見るおじさん
1980年代と今とではハードウェアの質、量ともに大きな革新がありました。これを利用したらアマチュアであってもおもしろいことができるのではないか？と夢想しています。黎明期のAppleのウォズニアックのようなものです。アイディアと卓越した知識と、そして普及しはじめた電子部品をもってイノベーションを起こしました。
ICOTは莫大な資金がなければできない専用ハードウェアを作りました。21世紀の今なら私もウォズニアックのようなことができるのではないか、と夢想して楽しんでいます。挑戦を忘れた男はなんとも情けないです。いつまでも夢見て挑戦するおじさんでありたいと私は思っています。

https://github.com/sasagawa888/nprolog
SST with OpenAuth Betaでエラー "Client xxxx is not authorized to use this redirect_uri: https://xxxxxx.cloudfront.net/callback" を解消する

## 結論

[OpenAuthのLambda Authorizerのexample](https://github.com/openauthjs/openauth/tree/master/examples/authorizer/lambda)等を使って、「カスタムドメインなしで」認可サーバーとクライアントを実装し、AWSにデプロイした時に、認可サーバーとクライアント間でホスト名が異なると表題のエラーが発生する場合がある。

これを解決するにはOpenAuthで認可サーバー作成時に使っている、`@openauthjs/openauth` の `authorizerメソッド` のオプションで allowメソッドを上書きしてやるといい。

## もう少し詳しく

当該エラーは下記で発生している。

```js:node_modules/@openauthjs/openauth/dist/esm/authorizer.js
if (!await allow({
  clientID: client_id,
  redirectURI: redirect_uri,
  audience
}, c.req.raw))
  throw new UnauthorizedClientError(client_id, redirect_uri);
```

コードを追っていくと、ここで使っているallow()は下記の実装になっている。

```ts
const allow = input.allow ?? (async (input2, req) => {
  const redir = new URL(input2.redirectURI).hostname;
  if (redir === "localhost" || redir === "127.0.0.1") {
    return true;
  }
  const forwarded = req.headers.get("x-forwarded-host");
  const host = forwarded ? new URL(`https://` + forwarded).hostname : new URL(req.url).hostname;
  return isDomainMatch(redir, host);
});
```

見ての通り、「認可サーバーとリダイレクト先が同一のホスト名」であるか、あるいは「X-Forwarded-Host指定した元のホスト名とリダイレクト先が同一のホスト名」でないとfalseになるような実装になっている。

しかし、例えばブラウザから302で認可サーバーにリダイレクトする時には、仕様上X-Forwarded-Hostを勝手に追加するわけにはいかない。なので、既存のデフォルトのallowメソッドを上書きして、自分の環境に合わせたallowメソッドをauthorizer初期化時に指定してやる必要がある。

例えば下記のようになる。

```ts:OpenAuthを使ったLambda Authorizerの例
import { authorizer } from "@openauthjs/openauth"
import { handle } from "hono/aws-lambda"
import { DynamoStorage } from "@openauthjs/openauth/storage/dynamo"
import { PasswordAdapter } from "@openauthjs/openauth/adapter/password"
import { PasswordUI } from "@openauthjs/openauth/ui/password"
import { subjects } from "./subjects"
import { isDomainMatch } from "@openauthjs/openauth/util"

async function getUser(email: string) {
  // Get user from database
  // Return user ID
  return "123";
}

const app = authorizer({
  storage: DynamoStorage({
    table: Resource.FeldsparTable.name,
  }),
  subjects,
  providers: {
    password: PasswordAdapter(
      PasswordUI({
        sendCode: async (email, code) => {
          console.log(email, code)
        },
      }),
    ),
  },
  success: async (ctx, value) => {
    if (value.provider === "password") {
      return ctx.subject("user", {
        id: await getUser(value.email),
      })
    }
    throw new Error("Invalid provider")
  },
  // ---- デフォルト実装を使わず、自前で実装したallow()で上書きする ----
  allow: (async (input2, req) => {
    const redir = new URL(input2.redirectURI).hostname;
    if (redir === "localhost" || redir === "127.0.0.1") {
      return true;
    }
    // 例えば下記のような形でホワイトリスト方式で許可する
    const host = new URL("許可するURL").hostname;
    return isDomainMatch(redir, host);
  }),
})

// @ts-ignore
export const handler = handle(app)
```
猫背を改善するために姿勢を検知するWebアプリを作った話

最近パソコンばっかりやっていて**猫背が酷い**ので、何とか改善しようと試みました。
# 概要
![mosaic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899960/b35c8c27-82b1-2413-a36b-631bfc1c1c64.png)
#### Webカメラから姿勢を検知し、猫背だと警告するWebアプリです。

:::note warn
個人情報(顔)は隠してあります。右の青い色は体の部位を示すものがぼかされているので決して~~体調が悪いわけではありません~~
:::
# 技術とその他諸々
コード全文はGithubにあげてます。

https://github.com/taikun24/nekoze

一応Github Pagesもあります。試したい方はどうぞ。

https://taikun24.github.io/nekoze/

色々解説します。
## 画像取得
```navigator.mediaDevices.getUserMedia```でストリームを取得し、**300ms**毎に姿勢を検知しています。
## 姿勢検知 - Tensorflow.js / Posenet
Tensorflow.jsとPosenetを使いました。
Posenetの```estimateSinglePose```を使って体の各部位を検出しています。
その中から両目と両肩の座標を用いています。
~~Posenetを知った経緯が[これ](https://stretch3.github.io)だとは言えない~~
## 猫背度判定 - ゴリ押しアルゴリズム
肩の丸まりのスコアを
 $score_1 = 100 - 100sigmoid(\frac{肩幅}{目幅} - 3)$  
で、頭の近づきを
 $score_2 = 100sigmoid(\frac{3肩幅}{distance(両目の中心, 両肩の中心)-1.5})$  
 で計算し、最終スコアをそれぞれの平均％としています。
 ~~ほとんどChatGPTに考えてもらいました~~
 ## ジャッジ - ただのif文
 スコアが**35**以下の場合```健康的な姿勢です！```
 スコアが**45**以下の場合```少し危険です```
 スコアが**70**以下の場合```猫背です！```
 もっと高い場合の場合```今すぐ姿勢を正しましょう！```
というメッセージが表示されます。
## 通知- Notification API
猫背（スコアが45以上）の状態が10秒ほど続くと、通知されます。
1\. タブのタイトルが変わります。
2\. バイブレーションします。(モバイル端末のみ)
3\. 通知が送られます。 ~~Chromeの通知設定オフになっていた事に気づかなかった~~
# 最後に
今までで一番役に立つ(?)Webアプリを作った気がします。よければ使ってみてください。最後に、
### **通知を許可するのを忘れないで！**
とだけ言っておきます。では、さようなら！
シン・クラウド for FreeでNextcloudのインストール時の注意点

数年前に軽く試していたNextcloudの機能が格段にUPしていたので、改めてインストールして使ってみたのですが、その際に詰まったポイントについて残しておきます。

# インストール

下記URLを参考にさせてもらいました。詳しく書かれていますので、手順は下記URLを参考にしてください。
https://qiita.com/raayucorp/items/9494913e56de98260250

# インストール時の注意
``setup-nextcloud.php`` をダウンロードしてサーバにUP、その後インストール途中で下記２つのエラーが発生しました。

## ModPagespeedモジュールがない
```
.htaccess: Invalid command 'ModPagespeed', perhaps misspelled or defined by a module not included in the server configuration
```
.htaccessの該当部分をコメントアウトしてインストールを進めました。

## 不正なオプション
```
Options -Indexes#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
.htaccess: Illegal option Indexes####
```
コメントが改行されておらず、存在していないオプションとして認識されてしまったのでエラーが発生してしまったようです。改行して回避しました。

以下生ログ
```
[Sat Nov 02 14:20:34.524307 2024] [core:alert] [pid 3239121:tid 139935281526528] [client ZZZ.ZZZ.ZZZ.ZZZ:55166] /home/XXXXXXX/YYYYYYYY.cloudfree.jp/public_html/nextcloud/.htaccess: Invalid command 'ModPagespeed', perhaps misspelled or defined by a module not included in the server configuration
[Sat Nov 02 14:29:09.544184 2024] [core:alert] [pid 3239133:tid 139935340275456] [client ZZZ.ZZZ.ZZZ.ZZZ:52226] /home/XXXXXXX/YYYYYYYY.cloudfree.jp/public_html/nextcloud/.htaccess: Illegal option Indexes####
```

# パスワード忘れたとき
nextcloudをインストールしたフォルダで下記を実行すればOK
```
php occ user:resetpassword ユーザー名
```
詳細は下記URLを参照ください。
https://codit.pages.dev/notes/kysv5f47771qr399akiv/


## webdav
ネットワークの共有(webdav)も普通に使えました。
RTX1200でipv6対応のfqdnフィルタっぽいことをする

# 概要
RTX1200はfqdn非対応機種である。そこを何とかする。

我が家では、実家のみRTX1200が生き残っており、ほかの拠点は先日RTX1210へのリプレイスが終わっている。実家は帰省頻度も低く、パフォーマンス的にも困っておらず、フレッツ光 HGWの配下に存在するため自分でmap-eのセッションを張る必要もないため、リプレイスの動機が薄い。いつか電源が壊れるとは思いつつ。

しかし今回、別拠点へIPv6経由でL2TP/IPSecを張りたいという要件が出てきてしまって、困ったのである。


# RTX1200はfqdnに非対応

当初書いていたフィルタは下記の通り。

```
ipv6 filter 1040 pass * * udp * 500
ipv6 filter 1041 pass * * esp
ipv6 filter 1042 pass * * udp * 4500
```

この書式では、インターネットに向けてudp500,udp4500,espがご開帳する。上位のHGWにFirewallが設定されており、フレッツNGN外からのパケットはDropしているとはいえ、う～ん、微妙。

:::note info
udp500,4500はdynamicフィルタで許可しているので内→外への通信では不要と思うのだが、なぜかRejectされてしまうので入れてある。
:::


fqdnフィルタに対応したRTX1210では下記のように書ける。しかし、RTX1200にはfqdnフィルタは実装されていないので、こういった書き方はできない。そこで、Luaスクリプトを用いて何とかする。

```
ipv6 filter 1040 pass example.com * udp * 500
ipv6 filter 1041 pass example.com * esp
ipv6 filter 1042 pass example.com * udp * 4500
```

# luaスクリプト
luaスクリプトは、下記の方針で作る。
* 何らかの方法でfqdnからAAAAレコードを引く
* フィルタ書式を組み立てる
* フィルタを挿入する

IPv4ではあるが、Yamaha用鎖国フィルタを開発している方もいるので、まあ何とかなるのではないか。

https://wakwak-koba.hatenadiary.jp/entry/20180225/p1

## RTX1200でipv6名前解決をする
フィルタに挿入するipv6アドレスを得なければならないので、名前解決の方法を探す。まず、nslookupを試す。

```
# nslookup google.co.jp
172.217.25.163
```

なんとなくだが、ipv4にしか対応してない雰囲気がしてきた。そこで、IPv6（AAAAレコード）しか存在しないfqdnで引いてみる。


```
# nslookup example.com
Error: No such domain name
```

ドメインがないといわれた。おわり。

正攻法だとダメそうなので、次はpingを送ってみる。

```
# ping6 -c 1 example.com
received from 2001:db8::1234, icmp_seq=0 hlim=56 time=12.500ms

1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max = 12.500/12.500/12.500 ms
```

なんかいい感じにIPv6アドレスが取れそうなので、luaスクリプト with Chat GPTに何とかしてもらうことにする。

## luaスクリプト

```
rt.syslog("info", "[fqdn-filter-add] Program Start")
fqdn = "example.com"
rtn, str = rt.command("ping6 -c 1 " .. fqdn)

rt.syslog("info", "[fqdn-filter-add] response=" .. str)

local ipv6_address = str:match("received from ([%x:]+)")

if ipv6_address then
    ipv6_address = ipv6_address:gsub(",", "")
    rt.syslog("info", "[fqdn-filter-add] addr=" .. ipv6_address)
    rtn, str = rt.command("ipv6 filter 1999 pass * " .. ipv6_address .. " * * *")
    rtn, str = rt.command("ipv6 filter 1998 pass " .. ipv6_address .. " * * * *")
else
    rt.syslog("info", "[fqdn-filter-add] addr extraction failed")
end
```

あとはluaスクリプトをRTX1200に流し込み、毎分スケジューラで回せばいい感じにfqdnフィルタが完成する。

```
schedule at 5 */* *:*:00 * lua /filter-update.lua
```

こんな感じでlogに出てきたら完成

```
2024/12/29 11:33:02: [LUA] Task(1) executed rt.command("ipv6 filter 1999 pass * 2001:db8::1234 * * *"): true
```
Radix UIのToastを使ってSnackbarを実装する

## 概要

Radix UIのToastでSnackbarの実装をメモする

## 詳細

- Snackbar.tsx

```tsx
import { type ReactNode } from 'react';
import * as Toast from '@radix-ui/react-toast';

import styles from './Snackbar.module.css';

type Props = {
  /** 通知を表示するか */
  open: boolean;
  /** 通知メッセージ（文字列、またはReactコンポーネント） */
  message: ReactNode;
  /** 通知表示の継続時間（単位：ミリ秒） */
  duration?: number;
  /** 通知表示の状態が変更されたときのコールバック */
  onOpenChange: (open: boolean) => void;
};

/**
 * スナックバーのコンポーネント
 * 全画面の下部に通知メッセージを表示して、一定時間後に自動的に非表示になる
 */
export function Snackbar({ open, message, duration = 3000, onOpenChange }: Props) {
  const messageNode =
    typeof message === 'string' ? <p className={styles.defaultNode}>{message}</p> : message;

  return (
    <Toast.Provider duration={duration} swipeDirection="down">
      <Toast.Root className={styles.toastRoot} open={open} onOpenChange={onOpenChange}>
        <Toast.Description asChild>{messageNode}</Toast.Description>
      </Toast.Root>
      <Toast.Viewport />
    </Toast.Provider>
  );
}
```

- Snackbar.module.css

```tsx
/* layer定義により共通スタイルの優先度を下げて、layer未定義の利用側のスタイルに負けるようにする */
@layer snackbarComponent {
  .defaultNode {
    padding: 16px;
    font-size: 14px;
    font-weight: bold;
    color: #ffffff;
  }

  .toastRoot {
    position: fixed;
    bottom: 30px;
    left: 50%;
    z-index: 999;
    background-color: #26303b;
    border-radius: 4px;
    box-shadow: 0 4px 4px 0 rgba(38, 48, 59, 0.4);
    opacity: 0.95;
    list-style: none;
    transform: translateX(-50%);

    &[data-state='open'] {
      animation: fadein 0.3s;
    }

    &[data-state='closed'] {
      opacity: 0;
      animation: fadeout 0.3s;
    }
  }

  @keyframes fadein {
    from {
      bottom: 0;
      opacity: 0;
    }

    to {
      bottom: 30px;
      opacity: 1;
    }
  }

  @keyframes fadeout {
    from {
      bottom: 30px;
      opacity: 1;
    }

    to {
      bottom: 0;
      opacity: 0;
    }
  }
}

```

## 参考資料
- [radix uiのtoastオフィシャルdocs](https://www.radix-ui.com/primitives/docs/components/toast)
DockerでDifyをインストールしたときの修正点

```
TIDB_ON_QDRANT_API_KEY: ${TIDB_ON_QDRANT_API_KEY:-dify}
TIDB_ON_QDRANT_CLIENT_TIMEOUT: ${TIDB_ON_QDRANT_CLIENT_TIMEOUT:-20}
```
${TIDB_ON_QDRANT_CLIENT_TIMEOUT:-20}のTIDB_ON_QDRANT_CLIENT_TIMEOUTがなぜかTIDB_ON_QDRANT_API_KEYに置き換わっていた

nginxのPORTの変更、もともとのポートが80だが、別のWebサーバが使用していたので8080に変更したが、8080もdifydockerで使っていたため8081に変更した。

docker compose ps
STATUSが再起動を繰り返していた。
理由は忘れた・・・
導入手順

1.Menu.basをANSI形式で準備
2.SplitTextFile.vbsをANSI形式で準備
3.SplitTextFile.vbsにMenu.basをドラッグ＆ドロップして分解
4.↑の出力結果を別フォルダに保存
5.M93_一括インポート.basを手動インポート
6.出力結果フォルダを選択
7.Menu.xlsmにリネームして保存
8.開発タブ→マクロのセキュリティ→「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェック
9.csvファイルからシート復元
10.M97_シート復元実行
11.参照設定にチェック

Visual Basic For Applications
Microsoft Excel 16.0 Object Library
OLE Automation
Microsoft Office 16.0 Object Library
Microsoft Forms 2.0 Object Library →参照からC:\windows\system32\FM20.DLL
Microsoft ActiveX Data Objects 2.8 Library (最新でOK)
Microsoft Visual Basic for Application Extensibilly 5.3
Microsoft Scripting Runtime

12.Menu.xlsm.zipにリネームし、.relsを取り出す
13.一番最後の「</Relationship>」の前で"改行して"その行の頭から以下の分を記載する
<Relationship Id="customUI" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/>
14..relsを上書きコピーする
15.Menu.xlsmにリネームする
16.Menu.xlsmを開いて、「RibbonとcustomUI作成」ボタンを追加する(フォントサイズ8)
17.M90_RibbonとcustomUI作成の110行目を現パスに変更する(バックアップフォルダ作成する)
18.作成したボタンから実行
19.【ここにExcelをドラッグ&ドロップ】customUI差し替え&エクセルアドイン化&登録.vbsをANSI形式で準備
20.Menu.xlsmをドラッグ＆ドロップして実行すると完了

<ユーザーフォームの取り込み>
1.ユーザーフォームのcsvをMenu.xlsmに取り込む
2.取り込んだシートをアクティブにして「M95_ユーザーフォーム作成」を実行
3.文字化けを修正する
4.適当なボタンをダブルクリックし、ユーザーフォームの中身のコードを貼り付ける
Radix UIによるScrollArea

## 概要
Radix UIのはScrollAreaコンポーネントが色々あるが、それらをラップすることで、使いやすい形にする。

## 詳細

- ScrollArea

```ts
import { type CSSProperties, type ReactNode, type UIEventHandler } from 'react';
import * as RadixScrollArea from '@radix-ui/react-scroll-area';

import { clsx } from '@/utils/className';

import styles from './ScrollArea.module.css';

type Props = {
  className?: string;
  style?: CSSProperties;
  onScroll?: UIEventHandler<HTMLDivElement>;
  children: ReactNode;
};

export function ScrollArea({ className, style, onScroll, children }: Props) {
  return (
    <RadixScrollArea.Root className={clsx(styles.root, className)} style={style}>
      <RadixScrollArea.Viewport onScroll={onScroll} className={styles.viewport}>
        {children}
      </RadixScrollArea.Viewport>
      <RadixScrollArea.Scrollbar className={styles.scrollbar} orientation="vertical" forceMount>
        <RadixScrollArea.Thumb className={styles.thumb} />
      </RadixScrollArea.Scrollbar>
      <RadixScrollArea.Scrollbar className={styles.scrollbar} orientation="horizontal" forceMount>
        <RadixScrollArea.Thumb className={styles.thumb} />
      </RadixScrollArea.Scrollbar>
      <RadixScrollArea.Corner className={styles.corner} />
    </RadixScrollArea.Root>
  );
}

```

- ScrollArea.module.css

```scss
/* layer定義により共通スタイルの優先度を下げて、layer未定義の利用側のスタイルに負けるようにする */
@layer scrollAreaComponent {
  .root {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  .viewport {
    width: 100%;
    height: 100%;
  }

  .scrollbar {
    display: flex;
    padding: 4px;
    background: #fafafa;

    &[data-orientation='vertical'] {
      width: var(--scrollbar-size);
      border-bottom: 1px solid #e0e5ea;
      border-left: 1px solid #e0e5ea;
    }

    &[data-orientation='horizontal'] {
      flex-direction: column;
      height: var(--scrollbar-size);
      border-top: 1px solid #e0e5ea;
      border-right: 1px solid #e0e5ea;
    }
  }

  .thumb {
    flex-grow: 1;
    background: #b0bcc7;
    border-radius: 8px;
  }

  .corner {
    background-color: transparent;
  }
}

```


## 参考資料

- [radix scroll areaオフィシャルdocs](https://www.radix-ui.com/primitives/docs/components/scroll-area)
この拡張機能は、Chrome 拡張機能のベストプラクティスに沿わないため、 と表示される

## 問題

過去に審査を通過した拡張機能を使おうと思ったり、新たにインストールしようと思った時に以下のような ***この拡張機能は、Chrome 拡張機能のベストプラクティスに沿わないため、*** というエラーが表示される。

![スクリーンショット 2024-12-29 10.37.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520341/92898da1-2a72-27e0-ebc3-f2929466d2fd.png)

![スクリーンショット 2024-12-29 10.37.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520341/f5534153-6dfb-2118-e678-c68917b4b19e.png)

これはChromeのバージョンが
```
バージョン: 131.0.6778.205（Official Build） （arm64）
```
になった時、発生します。（ChromeのWindow再起動により自動でアップデートされます。）

ここでは、Chrome拡張機能の開発者がこの問題に直面した際の対応方法を解説します。

## 最初に疑うべきこと

最初に確認すべきは、拡張機能が`MV2 (Manifest V2) `から`MV3 (Manifest V3) `に移行しているかどうかです。2023年以降、Chrome 拡張機能のプラットフォームは段階的に `MV3` へ移行しており、`MV2` に準拠した拡張機能はサポートが終了しています。

対応手順：

manifest.json ファイルを開き、manifest_version が 3 に設定されているかを確認します。

```
{
  "manifest_version": 3,
  ...
}
```

まだ `MV2` の場合は、`MV3` への移行を検討すます。公式の移行ガイドを参考にすると良いです。（ [MV3 への移行ガイド](https://developer.chrome.com/docs/extensions/develop/migrate?hl=ja)）

## `MV3` に移行している場合

すでに `MV3` へ移行しているにもかかわらずエラーが表示される場合は、下記のチェックリストを確認します。リンクと概要を下にまとめてます。

[チェックリスト](https://developer.chrome.com/docs/extensions/develop/migrate/checklist?hl=ja)

### 権限の最小化

必要以上の権限をリクエストしていないか確認する。

例えば、`permissions` に過剰な設定がある場合は削除または修正する。

### サービスワーカーの実装

`background` のスクリプトがサービスワーカーとして正しく機能しているか。

### 非推奨 API の使用

MV2 で使用可能だったが、MV3 では廃止された API を利用していないか。

### セキュリティ要件の遵守

リモートコードの実行や動的なコード生成を行っていないか確認する。

詳細なチェック項目については、公式ドキュメントをご参照ください：

MV3 チェックリスト

私の場合、Firebase内でCDNを使用したことが原因でした。
[こちらの対処方](https://firebase.google.com/docs/auth/web/chrome-extension?hl=ja)で問題解決しました。

## それでも解決しない場合

上記の手順をすべて実施しても問題が解決しない場合は、ヘルプページでの診断やサポートチームに問い合わせることをお勧めします。

[Chrome ウェブストア サポート センター](https://support.google.com/chrome_webstore/contact/one_stop_support?hl=ja)

# まとめ

このエラーメッセージは、拡張機能の設計や設定に問題がある場合に発生することが多いですが、正しい手順で対応すれば解決に導けるはずです。焦らず、上記のステップを一つずつ確認することで、拡張機能の品質をさらに高めることができます。
Alfredのクリップボードヒストリーに1Passwordの内容が表示されない

### 困ったこと
プライベートのPCを更新して色々していたときに1passwordでコピーした内容がここに表示されなくなりました。
正直1Passwordが完璧にフォームに対応してくることは多くはなく、コピーして使うことも多いので正直困ってました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/b7763899-e85b-f0ec-b91a-00d28ef5d74a.png)

1Password側の設定かと思いましたがそうでもなさそうでした。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/a7bf6fe6-f266-7651-e4ab-70993e34d398.png)


### 解決方法
Alfred/Features/Clipboar History/Advanced/ignore Apps

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/665a0858-26d7-91e3-ec74-13b659e663d2.png)

1. ここに1passwordが入っていたので削除しました

2. 同じ設定ページの「Ignore clipboard data marked as Concealed」のチェックを外しました
この設定がチェックされている場合、アプリ（1Passwordなど）がクリップボードデータに「concealed（隠されるべき）」というフラグを付与している場合、そのデータがクリップボード履歴に保存されません。1Passwordはセキュリティ上、このフラグを設定しているようでした。

セキュリティの担保として、クリップボードの内容は90秒後に削除されるように設定しました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/031610aa-a100-43ee-8df7-b901bcccdf5d.png)

これにて解決しました。セキュリティ上非推奨なことをしている意識は持ちつつ色々と効率的にしていきたい。
PMP試験対策(12/29)開発ライフサイクル

開発ライフサイクルの特徴を分かりやすくするために、引き渡し頻度（成果物を渡すスピード）と変更頻度（計画の変更が起こるスピード）という2つのポイントを軸に整理します。それぞれのライフサイクルを日常生活の例にたとえながら説明します。

1. 漸進型ライフサイクル
特徴:

引き渡し頻度：高い（成果物を頻繁に渡す）

変更頻度：低い（計画を大きく変えない）

例: 本を分冊で書く方法

1章ずつ完成させて順番に読者に渡していきます。

最初に全体の目次（計画）をしっかり決めるので、大幅な変更はありません。

読者は1章ずつ完成した内容を読めるため、進捗が分かりやすいです。

2. 反復型ライフサイクル
特徴:

引き渡し頻度：低い（完成品が出るまで時間がかかる）

変更頻度：高い（計画を試しながら変えていく）

例: 彫刻を作る方法

最初は石をざっくり削って形を整える。

作っている途中で「もっとこうした方がいい」と考えが変わり、形を修正する。

完成品は一気に仕上がるが、その過程では何度も方向を変えることがある。

3. アジャイル型ライフサイクル
特徴:

引き渡し頻度：高い（成果物を小分けで頻繁に渡す）

変更頻度：高い（計画も柔軟に変える）

例: オーダーメイドのケーキ作り

「まずスポンジ部分を見てもらい、次にクリームのデザインを相談する」といった感じで、小さい単位で成果物を渡します。

「もっと甘くしてほしい」などの要望が途中で出ても、それに応じて柔軟に対応します。

最終的にお客さんが満足するケーキを作り上げます。

比較表

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783554/1d011d28-305f-079d-5fe2-5287f68508aa.png)


まとめ
漸進型は、計画を守りつつこまめに成果物を渡す。

反復型は、大きく変更しながらまとめて成果を出す。

アジャイル型は、柔軟に変更しながら小分けに成果物を渡す。

開発プロジェクトの性質やチームの特性に応じて、どのアプローチを使うかを選ぶのが重要です。