ブログに戻る

フォロー&ご登録

JavaScript (または TypeScript) を AssemblyScript に移植する方法

Aaron Turner

Senior Engineer, Fastly

注 : Fastly Compute 向けの AssemblyScript SDK (@fastly/as-compute) は廃止され、より先進的で機能が豊富な JavaScript SDK (@fastly/js-compute) が代わりにリリースされています。

先日、Fastly のサーバーレスコンピューティング環境 ComputeAssemblyScript に対応することを発表しました。その後公開した私のブログ記事では、Compute と WebAssembly を初めて利用する JavaScript や TypeScript 開発者にとって AssemblyScript が便利な出発点になるという話をしました。今回は AssemblyScript と JavaScript の密接な関係性について、デモを用いて具体的にご紹介します。Compute 担当の Senior Software Engineer、そして AssemblyScript のコアチームの一員として、今回は一般的な JavaScript アプリケーションを AssemblyScript に移植する方法と、移植の際に配慮すべき点についてお話しします。

まずは最初に、移植するアプリケーションを決めましょう。先日、as-bind</u> のマークダウンパーサーの実験デモをもとに、簡単な Compute の AssemblyScript のマークダウンデモを作成しました。自宅でデモを実行した際のラウンドトリップタイムは約25ミリ秒でした。そのラウンドトリップタイムのうち、マークダウンのアップロードと HTML のダウンロードにかかった時間に対して、アプリケーションコードの実行にどれくらい時間がかかったか知りたいと思いました。またアプリケーションの他の部分に対して、マークダウンを HTML に変換するのにかかった時間も知りたいと思いました。これを Node.js アプリケーションとして構築する場合、次のようなコードを書くことができます。

const prettyMilliseconds = require("pretty-ms");

function getPrettyExecutionTime() {
  // Get our start time in milliseconds (Unix Timestamp)
  const start = Date.now();

  // Do a random amount of busy work
  let iterations = 100000 + Math.floor(Math.random() * 10000000);
  for (let i = 0; i < iterations; i++) {
    iterations -= 1;
  }

  // Get our start time in milliseconds (Unix Timestamp)
  const end = Date.now();

  // Log out the Execution time in a human readable format
let responseString = "";
responseString +=
  "Pretty Unix timestamp: " +
  prettyMilliseconds(start, { verbose: true }) +
  "\n";
responseString +=
  "Busy work execution time: " +
  prettyMilliseconds(end - start, {
    verbose: true,
    formatSubMilliseconds: true,
  });

return responseString;
}

console.log(getPrettyExecutionTime());

この Node.js コードでは、Node.js アプリケーション実行の開始と終了をマークする Unix タイムスタンプのような値 (1970年1月1日の 00:00:00 UTC から経過したミリ秒数) を取得するために、JavaScript Date global が利用されていることがわかります。そして npm で見つけた依存関係 pretty-ms undefined を使って、タイムスタンプを人間が判読可能な形式でログに記録します。2つのタイムスタンプの差をログに記録することで、実行時間の合計を割り出すことができます。

次に、この機能を Node.js アプリケーションから AssemblyScript アプリケーションに移植するとします。その場合のプロセスは通常、以下のようになります。

  1. ソースコードの JavaScript Globals (window または global のオブジェクトなど) が、AssemblyScript の標準ライブラリ、WASI ホストコール、または (通常 npm にある) サードパーティの AssemblyScript ライブラリと置き換えられるかどうか確認します。

  2. インポートされた JavaScript の依存関係 (および依存関係の依存関係) に同等の AssemblyScript の依存関係が存在するか確認します。存在しない場合はステップ1へ戻り、依存関係を移植します。

  3. AssemblyScript の型を JavaScript コードに追加、または TypeScript の型を AssemblyScript と互換性のある型に置き換えます</u>

  4. まだ開発段階にある AssemblyScript のライブラリやシンタックスで問題が生じる恐れのある JavaScript コードを調整・移動します。例えば AssemblyScript コンパイラに関して、2020年末現在の時点で開発中の主なコンポーネントは Closures と Regex だと思います。新たな代替 API に対応できるよう、JavaScript Globals または依存関係とやり取りをするコードを調整します。

ではこのプロセスを Node.js アプリケーションに適用してみましょう。ステップ1では、Node.js アプリケーションが <u>Math</u><u>Date</u> を利用しているのがわかります。具体的には Math.floor()Math.random()、そして Date.now() の機能を置き換える必要があります。AssemblyScript の標準ライブラリには AssemblyScript 独自のグローバルな</u> <u>Math.floor</u> があるので、ここでは何も変更する必要はありません。AssemblyScript 独自のグローバルな <u>Math.random()</u> もありますが、ランダムな数字を生成するために Compute がサポートする WASI バインディングを利用するのに、import “wasi” を追加する必要があると使用メモ</u>に書かれています。したがって、移植するのはコード1行のみとなります。最後に、若干厄介な Date.now() を移植します。AssemblyScript には Date のグローバルオブジェクト</u>がありますが、それを利用するためには Date オブジェクトを JavaScript ホストからインポートする必要があります。Compute は JavaScript Date オブジェクトを WebAssembly モジュールにインポートすることはできません。そのため、WASI またはサードパーティのライブラリを利用して置き換えを行います。 

Google で「assemblyscript wasi date」を検索すると、<u>as-wasi</u> が見つかります。as-wasi は WebAssembly System Interface (WASI) 向けの高レベル AssemblyScript レイヤーです。つまり、AssemblyScript WASI バインディングを使用して AssemblyScript アプリケーション向けに標準のシステムレベル機能を提供する高レベル API です。レファレンスドキュメントに独自の <u>Date.now()</u> があるので、これを JavaScript の Date.now() の代わりに使います。これで、アプリケーションのソースコードを Compute で実行できることがわかりました。しかし、同じプロセスを依存関係でも行う必要があります。Node.js アプリケーションには依存関係が1つあります。pretty-ms という、週に100万以上のダウンロード回数</u>を誇る人気の高い MIT ライセンスの npm パッケージです。pretty-ms のソースコードを見ると、parse-ms という依存関係がひとつあることと、MathNumber のグローバルオブジェクトを使用していることがわかります。再びステップ1に従うと、AssemblyScript の標準ライブラリの MathNumber が、現在のソースコードにぴったりはまります。 

parse-ms へ進む前に、シンタックスの変更についてご説明します。まず、入れ子関数で示されているように、ソースコードでクロージャが使用されていることがわかります。AssemblyScript では純粋関数のパスやネストが可能ですが、複雑化を避けるために関数をそれぞれ抜き出しましょう。さらに、このコードはモジュールの require() とエクスポートに CommonJS シンタックスを使用します。AssemblyScript は Typescript と同じように標準の ES Module の import/export シンタックス</u>を利用するので、この小さなシンタックスの変更も行います。次に、最後の依存関係である parse-ms を見ていきましょう。

<u>parse-ms</u> のソースコード</u>を見ればわかるように、これは最小限の依存関係です。先ほどと同じく、グローバルオブジェクトはすべて AssemblyScript の標準ライブラリで満たすことができます。エクスポート関数にパスした値が数字であることを確認する、型チェックのスニペットがありますが、今回は AssemblyScript のコンパイラが対処してくれるので必要ありません。

これで JavaScript のグローバルオブジェクトと依存関係を移植できることがわかりました。これはつまり、Node.js アプリケーションを移植できるということです。では、早速コードを書いてみましょう。まず Fastly CLI</u> を使用し、fastly compute init を実行して新しい AssemblyScript Compute アプリケーションを生成します。このブログの執筆時点では、これにより <u>@fastly/as-compute</u> 0.1.3</u>を使用する AssemblyScript スターターキット</u>が生成されます。次に、AssemblyScript アプリケーションへのコードの移植を開始します。以下のコードスニペットでは、JavaScript (および関連する TypeScript の型) に対するステップを細かく説明しています。では最終的なコードを見ていきましょう。まず、最も深い依存関係である parse-ms (JavaScript ソースコード</u>) から始めて、assembly/parse-ms.ts を作成します。

// This file is a port of:
// https://github.com/sindresorhus/parse-ms/blob/326500f7395fba4f47e73e36e6e770ad47c358d2/index.js
// The file is commented respective to how this is ported.

// As of 2020, AssemblyScript closure support is still in progress, and only supports pure nested functions
// Thus, we will pull out the nested function into its own function.
// This isn't required, but can avoid headaches in the future.
// This function takes in a float, but rounds it to an integer.
// In Typescript, `f64` would have been `number` types, but we must be explicit with the type of number in AS
// Ported from: https://github.com/sindresorhus/parse-ms/blob/326500f7395fba4f47e73e36e6e770ad47c358d2/index.js#L7
function roundTowardsZero(valueToRound: f64): f64 {
  if (valueToRound > 0) {
    return Math.floor(valueToRound);
  } else {
    return Math.ceil(valueToRound);
  }
}

// Define a class that represents the returned object fom the exported function
// We are exporting this as well, that way the type can be used
// Also, the `f64` type in TypeScript would be `number`, but in AS we must be explicit with the number type
// Ported from: https://github.com/sindresorhus/parse-ms/blob/326500f7395fba4f47e73e36e6e770ad47c358d2/index.js#L9
export class ParsedMilliseconds {
  days: f64;
  hours: f64;
  minutes: f64;
  seconds: f64;
  milliseconds: f64;
  microseconds: f64;
  nanoseconds: f64;
}

// Export a function to parse our milliseconds into our return type.
// Also, the `f64` type in TypeScript would be `number`, but in AS we must be explicit with the number type.
// Ported from: https://github.com/sindresorhus/parse-ms/blob/master/index.js#L2
export function parseMilliseconds(milliseconds: f64): ParsedMilliseconds {

  // We don't need to do a type check here, since we are using a typed language!
  // Referring to: https://github.com/sindresorhus/parse-ms/blob/326500f7395fba4f47e73e36e6e770ad47c358d2/index.js#L3

  // We moved roundTowardsZero into its own function
  // Referring to: https://github.com/sindresorhus/parse-ms/blob/326500f7395fba4f47e73e36e6e770ad47c358d2/index.js#L7

  // AssemblyScript will construct an instance of our return type (e.g new ParsedMilliseconds())
  // Since the return object has all the same properties of our return type.
  // This is so that we can pass a float into our `roundTowardsZero` function to handle the special rounding.
  return {
    days: roundTowardsZero(milliseconds / 86400000),
    hours: roundTowardsZero(milliseconds / 3600000) % 24,
    minutes: roundTowardsZero(milliseconds / 60000) % 60,
    seconds: roundTowardsZero(milliseconds / 1000) % 60,
    milliseconds: roundTowardsZero(milliseconds) % 1000,
    microseconds: roundTowardsZero(milliseconds * 1000) % 1000,
    nanoseconds: roundTowardsZero(milliseconds * 1e6) % 1000,
  };
}

parse-ms ができたら、pretty-ms (JavaScript ソースコード</u>) を移植します。pretty-ms では、少数点以下の桁数をコントロールするためのオプションが複数あります</u>。この実行は JavaScript Number.prototype.toFixed</u> に依存しますが、AssemblyScript の stdlib ではまだ問題があるようです</u>。この例の実装コードを書くこともできますが、なるべくシンプルにしたいので、今回はこの機能は含めません。では assembly/pretty-ms.ts を作成しましょう。

// This file is a port of:
// https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js
// The file is commented respective to how this is ported.

// Import our ported ported `parse-ms` module
// This ports the `require()` call:
// Ported From: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L2
import { parseMilliseconds } from "./parse-ms";

const SECOND_ROUNDING_EPSILON: f32 = 0.0000001;

// Define our options object that will be passed into our exported `prettyMilliseconds` function
// The options are from the documentation: https://github.com/sindresorhus/pretty-ms#options
// However, we removed the `DecimalDigits` options and `keepDecimalsOnWholeSeconds`, as Float.toFixed is in progress:
// https://github.com/AssemblyScript/assemblyscript/issues/1163
// In Typescript, `f64` and `i32` would have been `number` types, but we must be explicit with the type of number in AS
// Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L8
export class PrettyMillisecondsOptions {
  compact: boolean = false;
  unitCount: i32 = 0;
  verbose: boolean = false;
  separateMilliseconds: boolean = false;
  formatSubMilliseconds: boolean = false;
  colonNotation: boolean = false;
}

// This function takes in our word (which would be a string),
// and the count of that word (a float), to pluralize it.
// Also, the `i32` type in TypeScript would be `number`, but in AS, we must be explicit with the number type.
// Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L4
function pluralize(word: string, count: f64): string {

  // Since AssemblyScript is type checked, there is no need for ===
  // We can use the standard ==
  // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L4
  if (count == 1) {
    return word;
  }

  return word + "s";
}

// As of 2020, AssemblyScript closure support is still in progress and only supports pure nested functions
// Thus, we will pull out the nested function into its own function.
// We pass in the options and results that were previously accessible by the closure, and return the results
// We also typed all of the parameters, to their respective types they would have been in JS or TS.
// One notable parameter is `valueString`. In JavaScript, optional parameters will default to `undefined`.
// In AssemblyScript, we would want to initialize this parameter with a value of its type and check that value later.
// We could have done a `valueString: string | null = null` to also signify it's an optional high-level typed parameter in a more
// JS-like fashion, but we use an empty string as it is a simpler replacement to check for.
// Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L33
function add(
  options: PrettyMillisecondsOptions,
  result: Array<string>,
  value: f64,
  long: string,
  short: string,
  valueString: string = ""
): Array<string> {
  if (
    (result.length === 0 || !options.colonNotation) &&
    value === 0 &&
    !(options.colonNotation && short === "m")
  ) {
    return result;
  }

  // AssemblyScript doesn't have `undefined`, so we need to be
  // a bit more explicit with our typecheck here
  // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L38
  if (valueString == "") {
    valueString = value.toString();
  }

  // AssemblyScript would normally default types to i32, if the compiler can't figure out what
  // the type is from its initial assignment. So we need to define these types as strings,
  // since they are being used as strings
  // Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L39
  let prefix: string = "";
  let suffix: string = "";
  if (options.colonNotation) {
    prefix = result.length > 0 ? ":" : "";
    suffix = "";
    const wholeDigits = valueString.includes(".")
      ? valueString.split(".")[0].length
      : valueString.length;
    const minLength = result.length > 0 ? 2 : 1;
    valueString =
      "0".repeat(<i32>Math.max(0, minLength - wholeDigits)) + valueString;
  } else {
    prefix = "";

    // Since we removed the `DecimalDigits` options and `keepDecimalsOnWholeSeconds`, as Float.toFixed is in progress:
    // https://github.com/AssemblyScript/assemblyscript/issues/1163
    // Let's remove the trailing `.0` to clean things up by parsing our f64 into an i32
    valueString = I32.parseInt(valueString).toString();

    suffix = options.verbose ? " " + pluralize(long, value) : short;
  }

  result.push(prefix + valueString + suffix);
  return result;
}

// Export a function to parse our milliseconds into our human readable milliseconds string.
// Also, the `i32` type in TypeScript would be `number`, but in AS we must be explicit with the number type.
// Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L8
export function prettyMilliseconds(
  milliseconds: f64,
  options: PrettyMillisecondsOptions
): string {
  if (!Number.isFinite(milliseconds)) {
    throw new Error("Expected a finite number");
  }

  if (options.colonNotation) {
    options.compact = false;
    options.formatSubMilliseconds = false;
    options.separateMilliseconds = false;
    options.verbose = false;
  }

  // Since we aren't supporting DecimalDigits options in this port, we don't need to modify them
  // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L20

  // It is best to define most high-level types by their object and type
  // Therefore our Array is defined as `new Array<string>()` instead of `[]`
  // Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L25
  let result = new Array<string>();

  const parsed = parseMilliseconds(milliseconds);

  // As mentioned earlier, we pulled the add function into its own function.
  // Thus, we update our result as we add instead of using the closure.
  // We also updated the other `add()` calls below, but only commenting here for brevity.
  // Also, we don't need the Math.trunc() call since we are doing integer division by default, unlike JavaScript
  // Ported from: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L57
  result = add(options, result, Math.floor(parsed.days / 365), "year", "y");
  result = add(options, result, parsed.days % 365, "day", "d");
  result = add(options, result, parsed.hours, "hour", "h");
  result = add(options, result, parsed.minutes, "minute", "m");

  if (
    options.separateMilliseconds ||
    options.formatSubMilliseconds ||
    (!options.colonNotation && milliseconds < 1000)
  ) {
    result = add(options, result, parsed.seconds, "second", "s");
    if (options.formatSubMilliseconds) {
      result = add(options, result, parsed.milliseconds, "millisecond", "ms");
      result = add(options, result, parsed.microseconds, "microsecond", "µs");
      result = add(options, result, parsed.nanoseconds, "nanosecond", "ns");
    } else {
      const millisecondsAndBelow =
        parsed.milliseconds +
        parsed.microseconds / 1000 +
        parsed.nanoseconds / 1e6;

      // Since we aren't supporting DecimalDigits options in this port, we don't need `millisecondsDecimalDigits`
      // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L78

      const roundedMilliseconds =
        millisecondsAndBelow >= 1
          ? Math.round(millisecondsAndBelow)
          : Math.ceil(millisecondsAndBelow);

      // Since we aren't supporting DecimalDigits options in this port, we don't need `millisecondsDecimalDigits`
      // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L87
      const millisecondsString: string = roundedMilliseconds.toString();

      result = add(
        options,
        result,
        parseFloat(millisecondsString),
        "millisecond",
        "ms",
        millisecondsString
      );
    }
  } else {
    const seconds = (milliseconds / 1000) % 60;

    // Since we aren't supporting DecimalDigits options in this port, we don't need `secondsDecimalDigits`
    // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L78

    const secondsString = seconds.toString();
    result = add(
      options,
      result,
      parseFloat(secondsString),
      "second",
      "s",
      secondsString
    );
  }

  if (result.length === 0) {
    return "0" + (options.verbose ? " milliseconds" : "ms");
  }

  if (options.compact) {
    return result[0];
  }

  // We can replace the type check with a `> 0` check since we are using a typed language!
  // Referring to: https://github.com/sindresorhus/pretty-ms/blob/eda21362097d47ab309dca8cf07dc79b25fb0efa/index.js#L119
  if (options.unitCount > 0) {
    const separator = options.colonNotation ? "" : " ";
    return result.slice(0, <i32>Math.max(options.unitCount, 1)).join(separator);
  }

  return options.colonNotation ? result.join("") : result.join(" ");
}

できましたね!では次に、メインの Node.js アプリケーションロジックを assembly/pretty-execution-time.ts に移植しましょう。先ほど as-wasi の Date オブジェクトを使うことにしたので、ターミナルで npm install --save as-wasi を実行して as-wasi をプロジェクトにインストールします。それが終わったら、assembly/pretty-execution-time.ts を作成します。

import "wasi";
import { Date } from "as-wasi";
import { prettyMilliseconds } from "./pretty-ms";

export function getPrettyExecutionTime(): string {
  // Get our start time in milliseconds (Unix Timestamp)
  // In "as-wasi" this returns the milliseconds as a float
  // However, we will cast this to an integer for prettyMilliseconds
  const start = Date.now();

  // Do a random amount of busy work
  let iterations = 100000 + Math.floor(Math.random() * 10000000);
  for (let i = 0; i < iterations; i++) {
    iterations -= 1;
  }

  // Get our start time in milliseconds (Unix Timestamp)
  const end = Date.now();

  let responseString = "";
  responseString +=
    "Pretty Unix timestamp: " +
    prettyMilliseconds(start, { verbose: true }) +
    "\n";
  responseString +=
    "Busy work execution time: " +
    prettyMilliseconds(end - start, {
      verbose: true,
      formatSubMilliseconds: true,
    });

  return responseString;
}

最後に、Compute エントリーポイントの AssemblyScript ファイルにてエクスポートされた getPrettyExecutionTime 関数をコールします。そのために、assembly/index.ts を変更します。

import { Request, Response, Fastly } from "@fastly/as-compute";

// Import our pretty-execution time
import { getPrettyExecutionTime } from "./pretty-execution-time";

// Remove the unnecessary backend constants for our application
// Referring to: https://github.com/fastly/compute-starter-kit-assemblyscript-default/blob/78e536b046cff9e2a3e81945ef8b02ddc7bf2a75/assembly/index.ts#L3

// The entry point for your application.
//
// Use this function to define your main request handling logic. It could be
// used to route based on the request properties (such as method or path), send
// the request to a backend, make completely new requests, and/or generate
// synthetic responses.
function main(req: Request): Response {
  // Make any desired changes to the client request.
  req.headers().set("Host", "example.com");

  // We can filter requests that have unexpected methods.
  const VALID_METHODS = ["HEAD", "GET", "POST"];
  if (!VALID_METHODS.includes(req.method())) {
    return new Response(String.UTF8.encode("This method is not allowed"), {
      status: 405,
    });
  }

  let method = req.method();
  let urlParts = req.url().split("//").pop().split("/");
  let host = urlParts.shift();
  let path = "/" + urlParts.join("/");

  // If request is a `GET` to the `/` path, send a default response.
  if (method == "GET" && path == "/") {
    return new Response(String.UTF8.encode(getPrettyExecutionTime()), {
      status: 200,
    });
  }

  // Remove the unnecessary routes for our application
  // Referring to: https://github.com/fastly/compute-starter-kit-assemblyscript-default/blob/78e536b046cff9e2a3e81945ef8b02ddc7bf2a75/assembly/index.ts#L42

  // Catch all other requests and return a 404.
  return new Response(
    String.UTF8.encode("The page you requested could not be found"),
    {
      status: 404,
    }
  );
}

// Get the request from the client.
let req = Fastly.getClientRequest();

// Pass the request to the main request handler function.
let resp = main(req);

// Send the response back to the client.
Fastly.respondWith(resp);

アプリケーションの完成です!これで構築とデプロイが可能になりました。最終的な例は、こちら</u>をご覧ください。

最後に、いくつかポイントをまとめました。

  1. AssemblyScript と TypeScript の型には重複する部分があるので、TypeScript の parse-mspretty-ms の方がより移植しやすかったと思います。AssemblyScript の型の方が若干細かいので、ゼロから追加するよりは違いを調整して型を切り替える方が簡単です。

  2. 変数が宣言されると、AssemblyScript コンパイラは適切な型を判別しようとしますが、思いがけない型が決定される場合があります。移植後の動作がおかしい場合は、コンパイラに任せず、型を明示的に指定することで修正できることがあります。

  3. pretty-msparse-ms は、Express</u>Apollo</u> などの大きな JavaScript フレームワークより移植しやすいです。同じように人気が高いだけでなく、チュートリアル向けのシンプルな形式での移植も可能だったので、今回はこれらのパッケージを選びました。また大きなパッケージは、グローバル API のより多くの部分を使用します。例えば一般的な Node.js API は、ファイルシステムモジュールの</u> <u>fs</u> です。fsas-wasi における WASI 版は、<u>as-wasi</u> </u> <u>FileSystem</u> です。しかし、AssemblyScript や WASI、WebAssembly は比較的新しいテクノロジーなので、Node.js やブラウザにおけるすべてのグローバル JavaScript API と互換性があるわけではありません。

  4. 複数の AssemblyScript プロジェクトで使えるライブラリを移植する場合、ライセンス上可能であれば AssemblyScript コミュニティの参考になるので npm にアップロードしてみてください。AssemblyScript パッケージの公開は、通常 JavaScript パッケージをアップロードする際のプロセスとほとんど同じです</u>。唯一の違いは package.json の “main” キーの代わりに、ライブラリ内の AssemblyScript のエントリーファイルを示す “ascMain” を、AssemblyScript がライブラリの package.json 内で検索する</u>ことです。例えばライブラリのエントリーファイルが assembly/index.ts である場合、package.json に ”ascMain”: “assembly/index.ts” と追加します。

この新しくエキサイティングな言語の AssemblyScript を、皆さんがこれからどのように活用していくのか楽しみです。この記事で AssemblyScript が JavaScript や TypeScript の記述に非常に似ていること、そして JavaScript や TypeScript を Compute や WebAssembly の環境に取り入れるために AssemblyScript が最適のオプションのひとつであることがおわかりいただけたと思います。Compute に関する最新情報については、ぜひメーリングリストにご登録</u>ください。