虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Deno 1.15 へのアップデートと変更事項まとめ

皆さんこんにちは。急に寒くなってきましたね。おっくんです。

去る 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つドキュメントのページを参考に紹介します。

doc.deno.land

doc.deno.land

統一前のインターフェースが調べられないものが有ったので一部省略です。

// 統一前

// 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 のドキュメントも正式版になりました。

developer.mozilla.org

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 の安定化やコンソールの改善が入っていました。

github.com

また次のリリースが 4 週後くらいに来るはずです。
次のリリースも追いかけていきます。

P.S.

採用情報

■募集職種
yumenosora.co.jp

カジュアル面談も随時開催中です

■お申し込みはこちら!
news.toranoana.jp

■ToraLab.fmスタートしました!

メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm

■Twitterもフォローしてくださいね!

ツイッターでも随時情報発信をしています
twitter.com