GraphQL の startCursor/endCursor は nullable でもよい
GraphQL で ページネーションを実装するとき、Relay GraphQL Cursor Connections Specification に則ると、 startCursor/endCursor は non-null
にする必要がある。
GraphQL Cursor Connections Specification
PageInfo must contain fields hasPreviousPage and hasNextPage, both of which return non-null booleans. It must also contain fields startCursor and endCursor, both of which return non-null opaque strings.
ただ、要素が空の場合は指し示す cursor 自体存在しないので “”
のような空の文字列を返すことになり、これがちょっと気持ち悪い。
2019年頃から startCursor/endCursor は nullable にしてはどうかという issue が経って議論されているんだけど、最近どうやら進展があって nullable が許容された様子。 現実的には nullable で実装されていることが多いので、仕様もそっちに寄せましたみたいな内容。
https://github.com/facebook/relay/pull/2655#pullrequestreview-857972245
It's clear to me that startCursor/endCursor should be nullable, are treated as nullable by Relay, and are generally implemented in the community as nullable. The specification should reflect this de facto reality.
ドキュメント自体はまだ更新されていない様子だけど、そのうち多分更新されそう。
2021年競馬振り返り
2021年4月に初めて競馬をやったんだけど、未だに毎週を楽しみに続けている(きっかけとなったウマ娘はもはやプレイしていなくてただの競馬おじさんに成り果てている)。今日今年最後のG1レースも終わったのでこの1年の収支を振り返ってみる。
最終収支
これに12/26, 12/28 のレースの収支を加えると購入「126,800」, 払戻「221,800」になって回収率はなんと「175%」 となる。
競馬漫画「ウィナーズサークルへようこそ」では、回収率120~150%の人間は通称「馬券仙人」と呼ばれ、これは競馬ファンのわずか0.09%にあたり、今の仕事を辞めて、競馬予想に専念する者もでてくる。
とされているらしい。
競馬予想師初めて、オンラインサロン作って、そのうち映画でも作って信者にチケット売ってもらおうと思ったけど、まぁ続けていく以上最終的に回収率は75%付近に落ち着くはずなので、来年も趣味で無理のない範囲で楽しく続けていこうと思います。
browserslist と UA を比較するための正規表現を生成するグッズ browserslist-useragent-regexp
browserslist でサポートブラウザを定義しているんだけど、 Sentry で browserslist にないブラウザの場合エラーを送らないようなことがしたいなと考えていた。
browserslist では chrome 96
みたいにバージョンが定義されているけど、 UA はMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
みたいな形になっている。比較するのには正規表現書いたりする必要がありそうで、なんかいいグッズがないかなと探していた。
で、browserslist-useragent-regexp というbrowserslistを元にUAと比較するための正規表現を生成する
グッズがあるのを見つけた。
使ってみるとこういう一見異常な正規表現が出力される。
module.exports = /((CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(10[_.]3|10[_.]([4-9]|\d{2,})|(1[1-9]|[2-9]\d|\d{3,})[_.]\d+|11[_.]0|11[_.]([1-9]|\d{2,})|11[_.]2|11[_.]([3-9]|\d{2,})|(1[2-9]|[2-9]\d|\d{3,})[_.]\d+|12[_.]0|12[_.]([1-9]|\d{2,})|12[_.]5|12[_.]([6-9]|\d{2,})|(1[3-9]|[2-9]\d|\d{3,})[_.]\d+|13[_.]0|13[_.]([1-9]|\d{2,})|13[_.]7|13[_.]([8-9]|\d{2,})|(1[4-9]|[2-9]\d|\d{3,})[_.]\d+|14[_.]0|14[_.]([1-9]|\d{2,})|14[_.]4|14[_.]([5-9]|\d{2,})|14[_.]8|14[_.](9|\d{2,})|(1[5-9]|[2-9]\d|\d{3,})[_.]\d+|15[_.]0|15[_.]([1-9]|\d{2,})|(1[6-9]|[2-9]\d|\d{3,})[_.]\d+)(?:[_.]\d+)?)|(CFNetwork\/8.* Darwin\/16\.5\.\d+)|(CFNetwork\/8.* Darwin\/16\.6\.\d+)|(CFNetwork\/8.* Darwin\/16\.7\.\d+)|(CFNetwork\/8.* Darwin\/17\.0\.\d+)|(CFNetwork\/8.* Darwin\/17\.2\.\d+)|(CFNetwork\/8.* Darwin\/17\.3\.\d+)|(CFNetwork\/8.* Darwin\/17\.\d+)|(Edge\/(92(?:\.0)?|92(?:\.([1-9]|\d{2,}))?|(9[3-9]|\d{3,})(?:\.\d+)?))|((Chromium|Chrome)\/(61\.0|61\.([1-9]|\d{2,})|(6[2-9]|[7-9]\d|\d{3,})\.\d+|79\.0|79\.([1-9]|\d{2,})|([8-9]\d|\d{3,})\.\d+|91\.0|91\.([1-9]|\d{2,})|(9[2-9]|\d{3,})\.\d+)(?:\.\d+)?)|(Version\/(10\.1|10\.([2-9]|\d{2,})|(1[1-9]|[2-9]\d|\d{3,})\.\d+|11\.0|11\.([1-9]|\d{2,})|(1[2-9]|[2-9]\d|\d{3,})\.\d+|12\.0|12\.([1-9]|\d{2,})|(1[3-9]|[2-9]\d|\d{3,})\.\d+|13\.0|13\.([1-9]|\d{2,})|(1[4-9]|[2-9]\d|\d{3,})\.\d+|14\.0|14\.([1-9]|\d{2,})|(1[5-9]|[2-9]\d|\d{3,})\.\d+|15\.0|15\.([1-9]|\d{2,})|(1[6-9]|[2-9]\d|\d{3,})\.\d+)(?:\.\d+)? Safari\/)|(Firefox\/(78\.0|78\.([1-9]|\d{2,})|(79|[8-9]\d|\d{3,})\.\d+|91\.0|91\.([1-9]|\d{2,})|(9[2-9]|\d{3,})\.\d+)\.\d+)|(Firefox\/(78\.0|78\.([1-9]|\d{2,})|(79|[8-9]\d|\d{3,})\.\d+|91\.0|91\.([1-9]|\d{2,})|(9[2-9]|\d{3,})\.\d+)(pre|[ab]\d+[a-z]*)?)/;
これだけ見るとわけわからんという感じだけど、少なくとも BrowserStack で古いバージョンのブラウザでいくつか試してみたところ普通に動いてそう。
Sentry だけではなく、サポート外のブラウザならメッセージを出す、みたいなこととかに使えるのかも。
ゲーミングディスプレイ買った
動機
数ヶ月前からめちゃめちゃに Apex をやっているんだけど、ずっとリフレッシュレートが 75Hz のディスプレイ1でプレイしていた。
ゲーミングディスプレイにして世界が変わった!
みたいな発言をインターネットでよく見るので前から 144Hz 以上のディスプレイが気になっていたんだけど、ブラックフライデーでめっちゃ安くなっていたので思い切って買ってみた。
lenovo にしたのは、今持ってるディスプレイとデザイン合わせたいからだけでそれ以上の理由は特にない。
ちなみに今見たらブラックフライデー終わっても新たにサイバーマンデーセールが始まって価格は据え置きなので、多分一生安くなっていると思う。
使用感
なんかヌルヌルする!!!!
劇的な変化はない。若干当て感良くなったのと気がするのと、インファイト強くなった気がするけどあくまで気がする程度。
そもそももう30過ぎなので反射神経のほうが付いてこないのかもしれない。
無情。
-
ちなみにこれは仕事用で、USB Type-C に対応していて、外部電源供給容量が十分だったので買った。↩
MFA アプリのデータが消滅すると破滅する
もう4年くらい iPhone7 を使っていたんだけど、 最近 Lightning 端子の接触が悪くケーブルの下に何か物をおいて角度を付けないと充電できないという不便極まりない状態だったので iPhone SE(第二世代)に機種変更した。
クイックスタート機能でデータ移行をしていたところ、残り4分のところで一生進まずどうしたものかと思っていたけど、写真とかメールとかの重要なデータは移行されていそうなのでまぁいいかと強制停止させた。
で、よく見たら一部のアプリのデータがちゃんと移行されていなかった。そのうち、MFA認証に使っている Google Authenticator のデータが消滅しているのがめっちゃ困った。
AWS(IAMユーザ)
- 自分ではどうすることもできないので, 管理者の人に頼んで一旦 MFA 無効にしてもらってログインして再設定
-
- 他のPCにセッションが残ってたのでそこから再設定
- 今回はなんとかなったけど、もしもセッション残ってなかったら身分証送って解除してもらう…というフローが必要らしい?大変そう… https://www.amazon.co.jp/gp/help/customer/display.html?nodeId=GU3SL3GTHLHPDQ2H
-
- 同じく他のPCにセッション残っていたので再設定
- こちらは電話番号登録していたらそちらに認証コード送ることもできそうなのでなんとかなりそう
-
- 同じく他のPC(以下同文)
- そもそも使ってないので消してもいいんだけど, 昔 Facebook でアカウント登録したサービスがあってそれが何か把握していないのでうかつに消せない…
すべてが無に返されたので、他にどのサービスで MFA 有効にしてたのかすら把握できていない状況…
多分しばらくしてなにかのサービスにログインしようとしたら、 MFA 要求されて積みそう。
調べてみると同じ事象に遭遇した人結構居るようで、Mirosoft Authenticator ならマイクロソフトアカウントにデータ保存してくれるらしいのでそっちに移行しようかな…と思っている。
npm パッケージを beta 版で publish する
開発中の npm パッケージを publish して動作確認する必要があったのでその時の手順メモ.
バージョン指定
npm パッケージの package.json
のversion
を, 1.0.0-beta.0
のように末尾に-beta-?
が付くように書き換える.
またはnpm version premajor --preid beta
コマンド打つと上記と同じような感じに書き換えてくれる.
https://docs.npmjs.com/cli/v6/commands/npm-version
特にbeta
という文字列でなければいけないということではないので, next
でもnightly
でも開発中バージョンとわかるものであれば良さそうだけど様々なリポジトリ見る限りはbeta
が多そう?
publish
npm publish --tag beta
のようにタグを指定して publish する.
ちなみに, タグを付けないと default でlatest
タグが設定される.
https://docs.npmjs.com/cli/v6/commands/npm-publish
使うとき
npm install hogehoge@beta
のようにタグ指定でインストールすればよい.
またはnpm install hogehoge@1.0.0-beta.0
のようにバージョン直指定でもよい.
在宅ワーク中に家族に会議中だとアピールするライフハック
現在在宅ワーク中なわけだけど, 家族にミーティング中ですとアピールするために, ミーティングはいい感じのランプを点灯させたりしている。
ただミーティング始まる/終わったときに手動でON/OFFするの面倒なので, 自動化してみた。
事前準備
まずは遠隔で操作できるように, スマートプラグを用意。
TP-Link のやつが安いのでそれにした。IFTTT 対応しているなら別なやつでもよさそう。Amazon でポチったら速攻届いたので, Webhook の設定して HTTP request 投げて点いたり消えたりしたのを確認。
Chrome 拡張を作る
弊社ではミーティングに Google Meets を使用しているので, meet.google.com
ドメインを開いたときにランプを点灯, 閉じたら消灯, みたいにしたい。今回はそれ用の Chrome 拡張を作った。どうやら chrome.tabs API を使えば現在開いているタブ一覧とかを取得できるので, それを使ってみる。
manifes.json はこんな感じ。
{ "manifest_version": 2, "name": "Google Meets 開いたらランプつけるくん", "description": "Google Meets 開いたらランプつけてくれます", "version": "1.0", "background": { "scripts": ["event.js"], "persistent": false }, "permissions": ["tabs", "*://maker.ifttt.com/*"] }
permissions に ifttt のドメインを指定しているのは, こうしないと CORS エラーで怒られるため。あとは適当に js 本体を用意。
const googleMeetsDomain = "meet.google.com"; const turnOnUrl = "https://maker.ifttt.com/hogehogehogel"; const turnOffUrl = "https://maker.ifttt.com/fugafugafuga"; var isTurnOn = 0; function watchTabs() { chrome.tabs.query({}, (tabs) => { const isOpeningMeets = tabs.some((tab) => { return tab.url.match(/meet.google.com/); }); if (isOpeningMeets && !isTurnOn) { request(turnOnUrl); isTurnOn = 1; } if (!isOpeningMeets && isTurnOn) { request(turnOffUrl); isTurnOn = 0; } }); } function request(url) { var req = new XMLHttpRequest(); req.open("GET", url); req.send(); } chrome.tabs.onUpdated.addListener(watchTabs); chrome.tabs.onRemoved.addListener(watchTabs);
chrome.tabs.query
で全タブの url をなめて, Meets の url が含まれていれば ON, 含まれていないなら OFF する感じ。onRemoved
はタブを閉じたとき、onUpdated
はタブが更新されたときのイベント。Chrome 自体を閉じたときはこれだとダメだけど, あんまり困ってないのでまぁよいか, となっている。
実際の様子
Meets だけじゃなくて, 例えば youtube でご機嫌な動画を流したときにミラーボールを点灯するとか, 仕事中に Twitter を見始めたら吹き矢が飛んでくる, みたいなことに応用が効きそう。