application/json-seq
皆さんは RFC7464
... JavaScript Object Notation (JSON) Text Sequences は知っておられますでしょうか? mime-type
だと application/json-seq
です。
原文はここ
日本語訳はここ
で、これ 何が嬉しいかというと JSON の配列だと シリアライザーが有限処理するのでこの形式なら 無限生成を逐次処理しやすい…… ということです。
※ .NET だと JsonSerializerが Stream を元にして 無限非同期ジェネレータ (IAsyncEnumerable<T>
) できるのですが、ここではおいておきます。
JavaScript だと asyncIterator という IFがありまます。これを使うと
for await (const item of items)
{
// TODO...
}
の様に非同期にデータを受け取ることができます。
過去作品だとこういうのもありましたね。
See the Pen Lorem Picsum Client by juner clarinet (@juner) on CodePen.
画像の読み込みが完了したら次のものを toplevel で for await して追加していくタイプのページング実装に async iterator を使っています。
いや、欄外なので
で、で、で RFC7464 という仕様がわかっていて、 使えそうな機能があるなら 実装しましょう。ということで実装しました。
本題
で、本題 application/json-seq 形式に対応した TransformStream
こと JsonSequenceStream<T>
を json-seq-stream
の default export および JsonSequenceStream として公開しました。
今回開発したのは次の機能です。
-
文字列データから
RS(0xe1)
から始まってLF(0x0a)
で終わるレコードを 抽出して 文字列として列挙するSequenceStream
-
json文字列のレコードをそれぞれ処理して
T
オブジェクトとして 列挙するJsonSequenceFormatStream<T>
-
上記の機能を元に
TextDecoderStream
→SequenceStream
→JsonSequenceFormatStream<T>
する処理をまとめたJsonSequenceStream<T>
の三つです。基本的には 一番下の JsonSequenceStream<T>
しか使わないつもりです。
リポジトリ
次のコマンドでインストールできます。
npm install json-seq-stream
サンプル
使い方は次の様な感じです。
import { JsonSequenceStream } from "json-seq-stream";
type Value = {
value: number;
};
const response = await fetch(url);
if (!response.ok) throw new Error();
const readable = response.body!
.pipeThrough(new JsonSequenceStream<Value>());
for await (const value of readable) {
console.log(value);
}
ちなみに JsonSequenceStream<T>
を使わないのであれば次の様な感じです。
const readable = response.body!
.pipeThrough(new TextDecoderStream())
.pipeThrough(new SequenceStream())
.pipeThrough(new JsonSequenceStream<Value>());
実装見たらまんまそうです。
オプション
JsonSequenceStream<T>
の コンストラクタの第一引数は JsonSequenceStreamOptions<T>
型を用意しています。
これは次のプロパティを持った Partial な型です。
- label -
TextDecoderStream
のコンストラクタ第一引数の label にあたります。 規定はutf-8
です。
https://developer.mozilla.org/ja/docs/Web/API/TextDecoderStream/TextDecoderStream#label - fatal -
TextDecoderStream
のコンストラクタの第二引数 option の fatal プロパティにあたります。文字シーケンスの解析に失敗した場合にエラーとするか否かを指します。デフォルトfalse
です。
https://developer.mozilla.org/ja/docs/Web/API/TextDecoderStream/TextDecoderStream#fatal - lineBegin -
SequenceStream
のコンストラクタの第一期数 options の lineBegin プロパティにあたります。行開始判定文字列です。デフォルトはRS(0xe1)
... つまり\u00e1
です。
https://github.com/juner/json-seq-stream/blob/bda11834464f86589b650d6349d021ac1b29eec1/src/SequenceStream.ts - lineEnd -
SequenceStream
のコンストラクタの第一期数 options の lineEnd プロパティにあたります。行開始判定文字列です。デフォルトはLF(0x0a)
... つまり\n
です。
https://github.com/juner/json-seq-stream/blob/bda11834464f86589b650d6349d021ac1b29eec1/src/SequenceStream.ts - parse -
JsonSequenceFormatStream
のコンストラクタの第一引数 options の parse プロパティにあたります。初期値はJSON.parse
が割り当てられています。
https://github.com/juner/json-seq-stream/blob/bda11834464f86589b650d6349d021ac1b29eec1/src/JsonSequenceFormatStream.ts - errorFallback -
JsonSEquenceFormatStreaim<T>
のコンストラクタの第一引数 options の errorFallback プロパティです。 初期値はfalse
(エラーはスキップする) です。
https://github.com/juner/json-seq-stream/blob/bda11834464f86589b650d6349d021ac1b29eec1/src/JsonSequenceFormatStream.ts
参考文献
- rfc7464 (原文)
https://datatracker.ietf.org/doc/html/rfc7464 - rfc7464 (日本語訳)
https://tex2e.github.io/rfc-translater/html/rfc7464.html - npm:json-seq-stream
https://www.npmjs.com/package/json-seq-stream/v/1.0.5 - juner/json-seq-stream (github)
https://github.com/juner/json-seq-stream/tree/v1.0.5
以上。