皆さんこんにちは、暑い中ですがお元気ですか?おっくんです。
去る 2021 年 7 月 13 日に Deno 1.12 がリリースされました。 今回も、リリースノートを参考に 変更事項の気になるところを紹介したいと思います。
実行環境
- macOS Catalina 10.15.7
- Docker イメージ denoland/deno:centos(確認時点では Deno 1.12.0 でした)
Deno 1.12
Deno 1.12 での変更事項をDeno 1.12 リリースノートを元に確認します。
Web Crypto API の対応メソッドが増えました
Deno 1.12 では、 以下のWeb Crypto APIの 3 つの実装が追加されました。
- crypto.subtle.generateKey キー生成
- crypto.subtle.sign 署名
- crypto.subtle.verify 署名の検証
以下のサンプルコードがリリースノートで公開されていますが、動作を確認できませんでした。継続調査中ですが、そのまま共有したいと思います。
// キー生成 const keyPair = await crypto.subtle.generateKey( { name: "ECDSA", namedCurve: "P-384" }, true, ["sign", "verify"] ); // データに署名 const data = new TextEncode.encode("hello world"); const signature = await crypto.subtle.sign( { name: "ECDSA", hash: "SHA-256" }, keyPair.private, data ); // 署名を確認 const isValid = await crypto.subtle.verify( { name: "ECDSA", hash: "SHA-256" }, keyPair.public, signature, data );
つぎのリリースでも Web Crypto API の実装がさらに追加されるそうです。
次のプルリクがありました。
HMAC に関連した機能実装が増えるようですね。
Deno のネイティブウェブサーバーでWebSocket をサポートするようになりました
Deno 1.12 では、HTTP リクエストを WebSocket 接続にアップグレードするためのサポートとして、Deno.upgradeWebSocket
関数が追加されました。
この機能は、テストが不足している不安定なもので、実行には --unstable が必要なものになります。
Deno 1.13 での安定化を目指しているそうです。
「アップグレード」というのが、聞きなれないかもしれません。
ここでのアップグレードとは、「上位のものへの更新」ではなく、「異なるプロトコルへの変更」と、とらえるのがわかりやすいと思います。
Deno.upgradeWebSocket
は、リクエストを引数に、Response オブジェクトと、WebSocket オブジェクトを返します。
これらを使用して、リクエストの応答と、WebSocket を有効化します。
リリースノートで、示されているサンプルの https://deno.com/v1.12/ws_server.js の内容は以下の通りです。
[ws_server.js]
async function handleConn(conn) { const httpConn = Deno.serveHttp(conn); for await (const e of httpConn) { e.respondWith(handle(e.request)); } } function handle(req) { if (req.headers.get("upgrade") != "websocket") { return new Response("not trying to upgrade as websocket."); } const { websocket, response } = Deno.upgradeWebSocket(req); websocket.onopen = () => console.log("socket opened"); websocket.onmessage = (e) => { console.log("socket message:", e.data); websocket.send(new Date().toString()); }; websocket.onerror = (e) => console.log("socket errored:", e.message); websocket.onclose = () => console.log("socket closed"); return response; } const listener = Deno.listen({ port: 8080 }); console.log("listening on http://localhost:8080"); for await (const conn of listener) { handleConn(conn); }
サンプルコードでは、メッセージを受け取ると、サーバーは現在時刻を返すようになっています。 コンソールを 2 つ用意して、実行し動作を確認します。
[Web サーバー側コンソール]
$ deno run --allow-net --unstable https://deno.com/v1.12/ws_server.js Download https://deno.com/v1.12/ws_server.js listening on http://localhost:8080
[クライアント側コンソール]
$ Deno Deno 1.12.0 exit using ctrl+d or close() > const ws = new WebSocket("ws://localhost:8080/"); undefined > ws.onmessage = (e) => console.log(`message=>${e.data}`); [Function] > ws.send("hello") undefined > message=>Wed Jul 14 2021 03:08:03 GMT+0000 (Coordinated Universal Time)
クライアントからのリクエストに、WebSocket のメッセージで応答していることが確認できました。
これらで使用した WebSocket オブジェクトは、使いにくいものとして認識していて、WebSocketStream が安定して使えるようになれば、切り替える予定があるそうです。
REPL での TypeScript のサポート
REPL(deno
とだけ打って起動したときのもの) が、Deno 1.12 から TypeScript を受け付けるようになりました。
これまでは、TypeScript のコードを貼り付けても、構文エラーなど実行できませんでした。
これからは、都度トランスパイルされ実行されるようになります。
[Deno 1.11 の場合]
$ deno Deno 1.11.0 exit using ctrl+d or close() // 関数定義 > function log(message: string) { console.log(message) } Uncaught SyntaxError: Unexpected token ':' // 型定義 > interface User { name:string; age: number; } Uncaught SyntaxError: Unexpected strict mode reserved word
[Deno 1.12 の場合]
$ deno Deno 1.12.0 exit using ctrl+d or close() // 関数定義 > function log(message: string) { console.log(message) } undefined > log("test") test undefined // 型定義 > interface User { name: string; age: number; } undefined
型注釈だけでなく、型定義でもエラーにならないことが確認できました。 ただし、REPL では型のチェックが効いていないことは注意です。
Deno 1.12.0 exit using ctrl+d or close() > interface User{ name: string; age: number; } > function callUser(user: User){ console.log(user)} undefined // 誤った型の引数で実行する<=エラーになりません > callUser("hogehoge") hogehoge undefined
REPL で実行した内容を、repl.ts としてファイルに起こして、実行してみます。 [repl.ts]
interface User { name: string; age: number; } function callUser(user: User) { console.log(user); } callUser("hogehoge");
$ deno run repl.ts Check file:///usr/src/app/repl.ts error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'User'. callUser("hogehoge"); ~~~~~~~~~~ at file:///usr/src/app/repl.ts:11:10
こちらでは型チェックが動作します。
REPL での静的インポートをサポート
これまで REPL では、外部モジュールの静的インポート構文(import ... from ...;
)が使用できませんでした。
Deno 1.12 から、静的インポートが動的インポートへの自動変換をすることにより、REPL でも静的インポート構文が使用できます。
確認してみます。
[Deno 1.11 の場合]
Deno 1.11.0 exit using ctrl+d or close() // 静的インポート=>NG > import { serve } from "https://deno.land/std@0.101.0/http/server.ts"; Uncaught SyntaxError: Cannot use import statement outside a module // 動的インポート=>OK > const { server } = await import("https://deno.land/std@0.101.0/http/server.ts") Download https://deno.land/std@0.101.0/http/server.ts undefined > Download https://deno.land/std@0.101.0/io/bufio.ts Download https://deno.land/std@0.101.0/_util/assert.ts // 省略 Download https://deno.land/std@0.101.0/testing/_diff.ts Check https://deno.land/std@0.101.0/http/server.ts undefined
[Deno 1.12 の場合]
$ deno Deno 1.12.0 exit using ctrl+d or close() // 静的インポート=>OK > import { serve } from "https://deno.land/std@0.101.0/http/server.ts"; undefined // 動的インポート=>OK > const { server } = await import("https://deno.land/std@0.101.0/http/server.ts") Download https://deno.land/std@0.101.0/http/server.ts undefined > Download https://deno.land/std@0.101.0/async/mod.ts Download https://deno.land/std@0.101.0/_util/assert.ts // 省略 Download https://deno.land/std@0.101.0/testing/_diff.ts Check https://deno.land/std@0.101.0/http/server.ts undefined
Deno 1.12 では、静的インポート構文がエラーにならず使えていることがわかります。
タブ補完で、利用可能なプロパティリストが提供されるようになりました
タブ補完は、REPL でのクイックナビゲーションのための重要な機能です。 これまでは、タブ補完を行うと候補をひとつづつ表示していましたが、Deno 1.12 から 利用可能なプロパティのリストを表示するようになりました。
$ deno Deno 1.12.0 exit using ctrl+d or close() // Deno.hogehoge のリストを表示 > Deno. Display all 116 possibilities? (y or n) core chmod remove readAll seek pid internal chown renameSync readAllSync seekSync ppid resources chownSync rename writeAll connect noColor close copyFileSync version writeAllSync listen args memoryUsage cwd build copy connectTls mainModule test makeTempDirSync statSync iter listenTls constructor metrics makeTempDir lstatSync iterSync shutdown __defineGetter__ Process makeTempFileSync stat SeekMode fstatSync __defineSetter__ run makeTempFile lstat read fstat hasOwnProperty isatty mkdirSync truncateSync readSync fsyncSync __lookupGetter__ writeFileSync mkdir truncate write fsync __lookupSetter__ writeFile chdir ftruncateSync writeSync fdatasyncSync isPrototypeOf writeTextFileSync copyFile ftruncate File fdatasync propertyIsEnumerable writeTextFile readDirSync errors open symlink toString readTextFile readDir customInspect openSync symlinkSync valueOf readTextFileSync readLinkSync inspect create link toLocaleString readFile readLink env createSync linkSync readFileSync realPathSync exit stdin permissions watchFs realPath execPath stdout Permissions chmodSync removeSync Buffer stderr PermissionStatus // 自前で作成したオブジェクトでもリストで、表示可能 > const v = { a:1, b:2, c:3, d:4, e:5 } undefined > v. a d __defineGetter__ __lookupGetter__ propertyIsEnumerable toLocaleString b e __defineSetter__ __lookupSetter__ toString c constructor hasOwnProperty isPrototypeOf valueOf
REPL が、使いやすくなるのは、ありがたいですね。
MessageChannel と MessagePort のサポートが追加
Deno 1.12 から、Channel Messaging APIから MessageChannel
と MessagePort
がサポートされるようになりました。
Channel Messaging API を使用することで、2 つの MessagePort(port1 と port2) を作成し、複雑な JavaScript のオブジェクトのコピーや送受信ができます。
リリースノートでは、MessagePort を利用する例として、comlinkが紹介されています。
リリースノートにも記載されているサンプルを動作確認してみます。
https://deno.com/v1.12/comlink/main.js
import * as Comlink from "https://cdn.skypack.dev/comlink@4.3.1?dts"; // Start a worker with the code in ./worker.js const url = new URL("./worker.js", import.meta.url); const worker = new Worker(url, { type: "module" }); // Let comlink wrap this worker to provide a nice API const obj = Comlink.wrap(worker); // Call methods and get properties on the object exposed from the worker, as if // it is a local value. console.log(`Counter: ${await obj.counter}`); await obj.inc(); console.log(`Counter: ${await obj.counter}`); worker.terminate();
https://deno.com/v1.12/comlink/worker.js
import * as Comlink from "https://cdn.skypack.dev/comlink@4.3.1?dts"; // Create the object to expose. It has a counter property, and a method to // increment that counter. const obj = { counter: 0, inc() { this.counter++; }, }; // Expose the object to the host side using comlink. Comlink.expose(obj);
動作させると次のようになります。
$ deno run --allow-net https://deno.com/v1.12/comlink/main.js Download https://deno.com/v1.12/comlink/main.js Download https://cdn.skypack.dev/comlink@4.3.1?dts Download https://cdn.skypack.dev/-/comlink@v4.3.1-ebLSsXPUzhGrZgtPT5jX/dist=es2020,mode=imports/optimized/comlink.js Download https://cdn.skypack.dev/-/comlink@v4.3.1-ebLSsXPUzhGrZgtPT5jX/dist=es2020,mode=types/dist/umd/comlink.d.ts Download https://cdn.skypack.dev/-/comlink@v4.3.1-ebLSsXPUzhGrZgtPT5jX/dist=es2020,mode=types/dist/umd/protocol.d.ts Download https://deno.com/v1.12/comlink/worker.js Counter: 0 Counter: 1
Comlink を使用しないパターンも試してみました。
[main.ts]
const channel = new MessageChannel(); const url = new URL("./worker.js", import.meta.url); const worker = new Worker(url, { type: "module" }); // Workerスレッドへ使用するポートを送信 worker.postMessage({ port: channel.port2 }, [channel.port2]); // port1 での onmessage の処理を定義 channel.port1.onmessage = (event: MessageEvent) => { console.log(`Counter: ${event.data}`); }; // workerへ通信 channel.port1.postMessage({ param: "inc0" }); channel.port1.postMessage({ param: "inc1" }); channel.port1.postMessage({ param: "inc2" }); channel.port1.postMessage({ param: "inc3" }); channel.port1.postMessage({ param: "inc4" }); channel.port1.postMessage({ param: "inc5" });
[worker.js]
const _worker = self; const obj = { counter: 0, inc() { this.counter++; }, }; _worker.onmessage = (event) => { // ポートを受信し const { port } = event.data; // ポートとしてonmessage に対応する関数を実装 port.onmessage = (portEvent) => { const { param } = portEvent.data; console.log(param); port.postMessage(obj.counter); obj.inc(); }; };
実行すると次のようになります。(非同期の処理になるので、表示の順番は保証されません。)
$ deno run --allow-net --allow-read main.ts inc0 inc1 inc2 inc3 Counter: 0 Counter: 1 inc4 Counter: 2 Counter: 3 Counter: 4 inc5 Counter: 5
- メインスレッドから MessagePort を介して送信したメッセージをワーカーで表示、
- ワーカーが MessagePort を介して送信してきた counter の値をメインスレッドで表示
この二つを確認することができました。
WebAssembly 向け、ストリーミングのインスタンス化と非同期コンパイルのサポート
Deno は、v1.0.0 から WebAssembly を扱えますが、これまでは、バッファに読み込みされたインスタンス化 API だけが稼働していました。
Deno 1.12 では、これらのストリーミング対応版となる、、Response
または、Promise<Response>
引数にとる二つの API をサポートするようになります。
WebAssembly.compileStreaming() WebAssembly.instantiateStreaming()
ワーカー間での Atomics と SharedArrayBuffer の共有
マルチスレッドプログラミングに対応した多くの言語では、共有したデータにアクセスし、片一方のスレッドでの変更をもう一方のスレッドで反映できるようになっています。
JavaScript は、シングルスレッドで動作するように設計された言語のため、オブジェクトなどへのアクセスは、一つのスレッドから行われることを考慮されていました。
Web Worker により、メインスレッド(UI スレッド)と別に複数のスレッドを扱えるようになりましたが、同一のオブジェクトにアクセスはできず、
スレッド間のデータの送受信は、postMessage などメソッドを用いてオブジェクトのコピーを送受信しているだけでした。
SharedArrayBufferを用いることで、スレッド間で共有データを扱うことができます。
また、この SharedArrayBuffer へ同期的に読み書きをする仕組みとしてAtomics オブジェクトがあります。
Deno 1.12 では、これら SharedArrayBuffer と Atomics を使えるようになります。
このことにより、C, C++, Rust などから作られた、スレッド処理が実装された WebAssembly に対応できるようになります。 実用例として、ffmpeg.wasmがあります。
WebAssembly でのスレッド処理については、web.dev の Ingvar Stepanyan さんの投稿が参考になるとのことです。
注意事項として、SharedArrayBuffer を fetch や Deno.read といった API では、扱えません。 将来のリリースで、追加される可能性があるそうですが、現在のところ関連プルリクは無さそうでした。
テストランナーの改善
deno test
コマンドに、2 種類のオプションフラグが追加になりました。
--shuffle=<SEED>
テストケースの実行順序をランダムにすることで、実行順序への依存をキャッチしやすくします。
--fail-fast=<N>
deno test
実行時 N 回失敗したら テスト自体を終了させるオプションが追加されました。
fetch API にカスタムプロキシのサポートを追加
これまでネットワークプロキシは、HTTP_PROXY, HTTPS_PROXY, NO_PROXY といった環境変数で設定してきました。 Deno 1.12 から、fetch API へのパラメータとしてネットワークプロキシを設定できるようになりました。
リリースノートの記載から紹介します。
// Deno.createHttpClient を作成 const client = Deno.createHttpClient({ proxy: { url: "http://myproxy.com:8080", basicAuth: { username: "deno", password: "***" }, }, }); // fetch API のオプションとして適用 const response = await fetch("https://myserver.com", { client });
特定の API へのアクセスについてのみ、プロキシを経由させるなどの運用があれば、有効に使用できる内容になりそうです。
Deno.readFile が、AbortSignal をサポート
Deno 1.12 では、Deno.readFile が、AbortSignal をサポートするようになりました。 ファイルの読み取りに想定以上の時間がかかった際のタイムアウトに使用できます。
リリースノートの記載から紹介します。
const aborter = new AbortController(); Deno.readFile("./super_large_file.txt", { signal: aborter.signal }) .then((data) => console.log("File read:", data.length)) .catch((err) => console.error("File read failed:", err)); setTimeout(() => aborter.abort(), 1000);
tsconfig の types オプションが使用可能に
Deno の TypeScript のオプションのうち一部をサポートしています。 Deno 1.12 より、プログラムのチェックに使用する為の、任意の型定義ファイルを含める為に使用できる types プロパティをサポートするようになったそうです。 試してみます。
[tsconfig.json]
{ "compilerOptions": { "types": ["./types/types.d.ts"] } }
ディレクトリを指定してその配下すべてを参照先にすることはできません。 なので、複数有れば列挙します。
[./types/types.d.ts]
interface User { name: string; age: number; }
これらが用意できたら、以下のソースコードを用意し実行します。
[types_test.ts]
function callUser(user: User) { console.log(user); } callUser("test");
実行すると次のようになります。
# 普段のように実行 # => User の定義が無く関数宣言時点でエラーに! $ deno run type_test.ts Check file:///usr/src/app/type_test.ts error: TS2304 [ERROR]: Cannot find name 'User'. function callUser(user: User) { ~~~~ at file:///usr/src/app/type_test.ts:1:25 # --config オプションを使い tsconfig.json を指定 # => 型User の引数を指定すべき箇所に文字列を string 型で引数を与えたことによるエラーになる # => ./types/types.d.ts の内容が読み込まれている! $ deno run --config tsconfig.json type_test.ts Check file:///usr/src/app/type_test.ts error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'User'. callUser("test"); ~~~~~~ at file:///usr/src/app/type_test.ts:5:10
./types/types.d.ts に記述された型定義を参照してエラーを出してくれました。 types プロパティは、プログラム実行前の型チェック、言語サーバーと unstable となっている Deno.emit() ランタイム API でサポートされます。
ちなみに、型定義の参照については、tsconfig.json の types オプションを使用せずに記述可能です。
///
トリプルスラッシュディレクティブを使用します。
[types_test.ts(トリプルスラッシュディレクティブを使う)]
/// <reference types="./types/types.d.ts" /> function callUser(user: User) { console.log(user); } callUser("test");
JavaScript の新しい言語機能のサポート
Deno のビルドする V8 が 9.2 にアップデートされました。 これにより言語機能が、追加されます。
at()
メソッドがサポートされます
指定したインデックスの値を取得できる at()
メソッドが追加されます。
動かしてみると以下の通りです。
$ deno Deno 1.12.0 exit using ctrl+d or close() > let arr = [1, 2, 3, 4]; undefined > arr.at(0); 1 > arr.at(1); 2 > arr.at(-2); 3 > arr.at(-1); 4
不の数を index に指定できます。
こちらの at()
メソッドですが、手元の端末の Chrome 91.0.4472.124 では、動作しませんでした。
MDN - Array.prototype.at()にて確認すると Chrome92 より動作するようです。
Intl.DateTimeFormat の dayPeriod オプションが追加
Deno 1.12 よりIntl.DateTimeFormatに、文字列のフォーマットのオプションとして、dayPeriod オプションが追加されました。
# deno Deno 1.12.0 exit using ctrl+d or close() > let options = {hour: "numeric", dayPeriod: "short"}; undefined > let date = new Date(Date.UTC(2021, 7, 10, 3, 0, 0, 0)); undefined > console.log(new Intl.DateTimeFormat('en-US', options).format(date)); 3 at night undefined > date = new Date(Date.UTC(2021, 7, 10, 15, 0, 0, 0)); 2021-08-10T15:00:00.000Z > console.log(new Intl.DateTimeFormat('en-US', options).format(date)); 3 in the afternoon undefined
ちなみにこちらの dayPeriod ですが、ja-JP
をロケールに設定した場合には、変化はありませんでした。
Deno 名前空間が、使用可能に
これまでリリースでは、Deno 名前空間を開発者が任意に変更することは、できませんでした。 Deno 1.12 より、Deno 名前空間のプロパティの書き換えが可能になり、任意のプロパティの変更、追加、削除が行えるようになっています。
[Deno 1.11 の場合]
$ deno Deno 1.11.0 exit using ctrl+d or close() // Deno.read メソッドを上書き > Deno.read = () => console.log("read") Uncaught TypeError: Cannot assign to read only property 'read' of object '#<Object>' at <anonymous>:2:11 // プロパティの追加 > Deno.hoge = "hoge" Uncaught TypeError: Cannot add property hoge, object is not extensible at <anonymous>:2:11 // プロパティの削除 delete Deno.read Uncaught TypeError: Cannot delete property 'read' of #<Object> at <anonymous>:2:1
[Deno 1.12 の場合]
$ deno Deno 1.12.0 exit using ctrl+d or close() // Deno.read メソッドを上書き > Deno.read = ()=> console.log("read") [Function] > Deno.read() read undefined // プロパティの追加 > Deno.hoge = "hoge" "hoge" // プロパティの削除 delete Deno.read // Deno.rea を補完して プロパティ一覧を表示しても read は出てこなくなる > Deno.rea readTextFile readDirSync realPathSync readSync readTextFileSync readDir realPath readFile readLinkSync readAll readFileSync readLink readAllSync
リリースノートによると、この機能の多用は推奨しないそうですが、テストの為のネット API のモックなどに役立つことを想定しているようです。 こちらの想定されている 「ネット API」 の動作については、fetch API のことかと考えたのですが Deno オブジェクトのメソッドではないので、ランタイム API のモックとして読むのがいいかと思います。
Web プラットフォームでの互換性ステータスの更新
数カ月にわたり、Chrome や、FireFox などで使用されているテストスイートでのクロスブラウザテストに Deno が連携できるように取り組んだそうです。 wpt.fyiで結果が参照できます。
こちらの結果ですが、有名ブラウザでも WEB 標準に合致しておらずパスしていないテスト項目もあったりするので、眺めてみると面白いかもしれません。
その他
- ChromeDevTools でのデバッグサポートの改善
- ChromeDevTools のコンソールへの、コンソール出力
Deno 1.12 では、コンソール出力を ChromeDevTools のコンソールへも出力できるようになりました。 - about://inspect で表示される Deno プロセス情報の表示が改善されました。
- ChromeDevTools のコンソールへの、コンソール出力
- FinalizationRegistry と WeakRef のバグ解消
以前より実装されていた FinalizationRegistry と WeakRef が、正しく動作しないという報告があったようです。
現在、これらは正しく動作するとのことです。 - Deno.copy が非推奨になりました
代替として、deno.land/std@0.101.0/io/util.tsの copy を使用できます。 - 言語サーバーが改善されました
- 動的インポートでの、循環依存に関するバグの修正
Deno 1.12 のリリースノートを追ってきました。 WebAssembly に関連したリリースがやや多かったかな?という印象でした。 既存の Nodo.js 向けライブラリで Deno 向けになっておらず、適用できないといった悩みがたまにありますが、WebAssembly を使って例えば Rust のライブラリで機能補完するなどと最近考えています。WebAssembly の知識も拡充していく必要がありますね。 また今回のリリースでは、REPL での静的インポートが書けるようになったのはありがたい限りです。
終わりに、リリースノートにて、deno の第 3 クォーターのロードマップが共有されています。
気になるところは、ネイティブプラグインが不安定なまま削除になってしまうこと、クロスプロセスでの BroadcastChannel 利用でしょうか。 ネイティブプラグインは、どのような使用例が見られるかなと興味が有ったこともあり、少し残念です。 クロスプロセスでの BroadcastChannel については、マルチスレッドだけでなく、マルチプロセスでの協調的なシステムの構築に使用できるかなと考えています。 例えば、遅延実行させる処理などメインのプロセスとは別に、BroadcastChannel を介してパラメータを送って処理させるなども考えられると思っています。
次の リリースは 8 月 10 日予定です。またそのころ Deno 1.13 でお会いしましょう。
P.S.
■とらのあなラボ Tech Day #2 開催します。
虎の穴ラボ株式会社」が主催する、技術カンファレンスです。お申し込みはこちら!
yumenosora.connpass.com
■ 採用情報
とらのあなでは、オタクなエンジニアを募集しています。
yumenosora.co.jp
カジュアル面談も随時開催中です。お申し込みはこちら!
news.toranoana.jp
■ ToraLab.fmスタートしました!
メンバーによるPodcastを配信中!是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm
■ Twitterもフォローしてくださいね!
ツイッターでも随時情報発信をしています。
twitter.com