皆さんこんにちは。急に寒くなってきましたね。おっくんです。
去る 2021 年 10 月 13 日に Deno 1.15 がリリースされました。 今回も、リリースノートを参考に 変更事項の気になるところを紹介したいと思います。
実行環境
Docker イメージ denoland/deno:centos(確認時点では Deno 1.15.1 でした)を使用します。
Deno 1.15
Deno 1.15 での変更事項をDeno 1.15 リリースノートを元に確認します。
FFI が改善されました
Deno 1.13 でプラグインが廃止され、入れ替わりに導入された FFI API が改善されました。
今回のリリースで、「ノンブロッキングコール」と、「バッファ引数」の機能が追加されます。
ノンブロッキングコール
ノンブロッキングで関数を呼び出すには、Deno.dlopen のオプションとして nonblocking: true
を与える必要があります。
リリースノートの記述を参考にライブラリの作成から行ってみます。
まず gcc のインストール。
yum install -y gcc
続けて、ソースコードの作成。
// sleep.c #include <time.h> int sleep(unsigned int ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; nanosleep(&ts, NULL); }
共有ライブラリとして、コンパイルします。
cc -c -o sleep.o sleep.c cc -shared -o sleep.so sleep.o
// nonblocking_ffi.ts const library = Deno.dlopen("./sleep.so", { sleep: { parameters: ["usize"], result: "void", nonblocking: true, }, }); library.symbols.sleep(500).then(() => console.log("After")); console.log("Before");
実行すると次のようになります。
$ deno run --allow-ffi --unstable nonblocking_ffi.ts Before After
nonblocking: true
を与えることで、ノンブロッキング呼び出しとなり、Promise
を返すようになります。
Promise を返すので、Promise を返すのを待機する await にも対応します。
// nonblocking_ffi_await.ts const library = Deno.dlopen("./sleep.so", { sleep: { parameters: ["usize"], result: "void", nonblocking: true, }, }); console.log("Before"); await library.symbols.sleep(10000); console.log("After");
実行すると次のようになります。
$ deno run --allow-ffi --unstable nonblocking_ffi.ts Before # <= ここで10秒待つ After
ライブラリのビルド方法ではなく、利用側となる Deno.dlopen()
側でノンブロッキングコールになっているのがとても使い勝手がいいと感じます。
バッファ引数
このリリース以前の FFI API で呼び出しできる関数の引数は、プリミティブ型だけでした。 今回のリリースで、FFI APIで呼び出す関数へバッファ引数を与えられるようになります。
この変更もリリースノートを参考に確認してみます。 リリースノートでは Rust で共有ライブラリが作成されていますが、本記事では C 言語で記述してみます。
// print_buffer.c #include <stdio.h> #include <stdint.h> int print_buffer(unsigned char *arr, int len) { for (int i = 0; i < len; i++) { printf("%i", arr[i]); if (i < len - 1) { printf(", "); } } printf("\n"); }
できたらビルドします。
cc -c -fPIC -o print_buffer.o print_buffer.c cc -shared -o print_buffer.so print_buffer.o
呼び出し側は、リリースノートに従います。
// print_buffer.ts const library = Deno.dlopen("./print_buffer.so", { print_buffer: { parameters: ["buffer", "usize"], result: "void", }, }); const buffer = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); library.symbols.print_buffer(buffer, buffer.length);
実行してみます。
deno run --allow-ffi --unstable ./print_buffer.ts 1, 2, 3, 4, 5, 6, 7, 8
バッファ引数を与えて、FFI API 側で引数を利用できました。
ちなみにこちらを Deno 1.14.0 環境で実行すると次のようなエラーになります。
$ deno -V deno 1.14.0 $ deno run --allow-ffi --unstable ./print_buffer.ts Check file:///usr/src/app/print_buffer.ts error: TS2322 [ERROR]: Type '"buffer"' is not assignable to type 'NativeType'. parameters: ["buffer", "usize"], ~~~~~~~~ at file:///usr/src/app/print_buffer.ts:4:20
Node 互換モードオプションが追加されました
Deno で Node.js の向けのプログラムを実行しやすくする --compat オプションが追加されます。 deno run のヘルプの記載を見ると次のようになっています。
# --compat 以外の記載は省略しています $ deno run --help OPTIONS: --compat Node compatibility mode. Currently only enables built-in node modules like 'fs' and globals like 'process'.
Node 互換モードとして fs
モジュールと、グローバル変数 process
を Deno で使用できるようです。
リリースノートで示されているコードを参考に試してみます。
// node_compatible.js import { readFileSync } from "fs"; let passwd = readFileSync("./text.txt", "utf-8"); console.log(passwd); console.log(process.pid);
実行は、以下の通りです。
$ deno run --allow-read --compat --unstable node.js text 459
--compat は、--unstable の付与も要求します。
また、テストプログラムで readFileSync
API でファイルを読み込むので、--allow-read
を要求します。
Node との互換性がありつつ、Deno の強みであるリソースアクセスの管理の利点が生かされているのがわかります。
--compat オプションは、Node をエミュレーションに向け引き続き改善が予定されているそうです。
deno uninstall コマンドが追加されました
これまで、deno install はありましたが、今回のリリースで deno uninstall が追加されました。 ありそうでなかった機能が追加になります。
インストールからやってみます。
$ echo "console.log('welcome\!\!')" >> welcome.ts # 直接実行 $ deno run welcome.ts Check file:///usr/src/app/welcome.ts welcome!! # インストール $ deno install welcome.ts Check file:///usr/src/app/welcome.ts ✅ Successfully installed welcome /usr/local/bin/welcome $ cat /usr/local/bin/welcome #!/bin/sh # generated by deno install exec deno run 'file:///usr/src/app/welcome.ts' "$@" # /usr/local/bin/welcome を実行 $ welcome welcome!! # アンインストール $ deno uninstall welcome deleted /usr/local/bin/welcome ✅ Successfully uninstalled welcome # パスがないことを確認 $ which welcome which: no welcome in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin) # パスがないので実行できないように $ welcome bash: /usr/local/bin/welcome: No such file or directory
deno unistall が行っているのは、上の場合 /usr/local/bin/welcome
の削除です。
これまでも直接 /usr/local/bin/welcome
を削除することで、deno uninstall と同じことをできていました。
パスを確認して手で削除しに行かずに済むのが良くなった点です。
オンメモリで CA 証明書を取り扱えるようになりました
Deno には、TLS を扱える API が 3 つあります。
- Deno.connectTls
- Deno.startTls
- Deno.createHttpClient
それぞれ個別に CA 証明書を指定する方法がありましたが、caCerts オプションを追加する形に統一されます。 次の2つドキュメントのページを参考に紹介します。
統一前のインターフェースが調べられないものが有ったので一部省略です。
// 統一前 // Deno.createHttpClient Deno.createHttpClient({ caData: Deno.readFileSync("./custom_ca_cert.pem") });
// 統一後 const caCert = await Deno.readTextFile("./custom_ca_cert.pem"); // Deno.connectTls const client = Deno.connectTls({ hostname: "database.internal", port: 4443, caCerts: [caCert], }); // Deno.startTls const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); const tlsConn = await Deno.startTls(conn, { caCerts: [caCert], hostname: "localhost", }); // Deno.createHttpClient const client = Deno.createHttpClient({ caCerts: [caCert] });
近しいもののインターフェースが統一されるのは、わかりやすくなったと感じます。
上記の統一前の createHttpClient
で示したところで使用している caData オプションは削除になります。
Deno test がネストできるようになりました
Deno 1.15 から、新たな --unstable を要求する API が増えました。 ネストしたテストを記述できるようになります。
リリースノートでは、データベースへの接続とデータの登録のテストが示されていますが、ここでは絞った内容でテストを見てみます。
// nest_test.ts import { assertEquals } from "https://deno.land/std@0.65.0/testing/asserts.ts"; const sq = (src: number) => { return src ** 2; }; Deno.test("test1", async (t) => { await t.step("test1-1", async () => { assertEquals(sq(1), 1); }); await t.step("test1-1", async () => { assertEquals(sq(2), 4); }); });
テストを実行してみます。
deno test --unstable Check file:///usr/src/app/test.ts running 1 test from file:///usr/src/app/test.ts test test1 ... test test1-1 ... ok (9ms) test test1-1 ... ok (7ms) ok (24ms) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (43ms)
実験的な API なので、--unstable
をつけて実行する必要があります。
内部としてはテストを 2 件実行していますが、最終的な OK の件数は 1 件となります。
いくつかの API が安定化しました
Deno 1.15 でいくつかの Deno API が安定化しました。
- Deno.kill
- Deno.Process.kill
- Deno.resolveDns
これらの API は、--unstable を付与せず実行できるようになりました。
また、Deno 1.14 で利用可能になった URLPattern も予定されていた通り Deno 1.15 で --unstable が不要になりました。
Deno 1.14 の際の記事で、動作確認をしているので詳細はこちらを確認ください。
Deno 1.14 公開の際には、プレビュー版だった MDN の URLPattern のドキュメントも正式版になりました。
crypto API の実装が追加されました
Deno 1.11 から続く、crypto API の実装が今回も追加されました。
- crypto.subtle.exportKey():
- RSA キーを spki 形式でエクスポート機能を追加
- crypto.subtle.importKey():
- ECDSA キーを raw 形式でインポートする機能を追加
- crypto.subtle.deriveBits():
- 導出アルゴリズムとして ECDH のサポートを追加(P256 形式の鍵のみ対応)
- crypto.subtle.wrapKey():
- crypto.subtle.exportKey() で、サポートされている形式のキーとフォーマットで、キーラッピングに対応しました。
- crypto.subtle.encrypt():
- AES-CBC 暗号化 がサポートされるようになりました。
- crypto.subtle.decrypt():
- AES-CBC 復号化 がサポートされるようになりました。
年末までに WebCrypto API の実装完了の目標に向かって順調に進んでいるそうです。
その他
- deno lint に -- watch オプションが追加されました
- Deno 1.14 に引き続き Deno 1.15 でも、v8 が 9.5 へアップデートされます。
まとめ
Deno 1.15 のリリースノートを参照しながら、気になる機能を試してきました。 今回のリリースでエポックなものは、FFI API の改善と Node 互換モードでしょうか。 特に FFI を介してライブラリ資産をノンブロッキングで呼び出せるようになったのは、他の API と強い差別化をせずに使う環境が整っていると感じます。
現在 Deno 1.16 のマイルストーンとして、Deno.startTls
の安定化やコンソールの改善が入っていました。
また次のリリースが 4 週後くらいに来るはずです。
次のリリースも追いかけていきます。
P.S.
採用情報
■募集職種
yumenosora.co.jp
カジュアル面談も随時開催中です
■お申し込みはこちら!
news.toranoana.jp
■ToraLab.fmスタートしました!
メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm
■Twitterもフォローしてくださいね!
ツイッターでも随時情報発信をしています
twitter.com