summaryrefslogtreecommitdiff
path: root/cli/js/ops
diff options
context:
space:
mode:
authorBartek Iwańczuk <biwanczuk@gmail.com>2020-03-08 13:09:22 +0100
committerGitHub <noreply@github.com>2020-03-08 13:09:22 +0100
commit1b6f8318750d319d689f7eeef9e7e1f2e56b94a6 (patch)
treeb2e182b77cfbcd25ee893113de9f61509e16e787 /cli/js/ops
parentb9037c86ed8d1d55a59a1c1298fa12bbfcae6873 (diff)
reorg: move JS ops implementations to cli/js/ops/, part 1 (#4264)
Following JS ops were moved to separate files in cli/js/ops directory: - compiler - dispatch_json - dispatch_minimal - errors - fetch - fs_events - os - random - repl - resources - runtime_compiler - runtime - tty
Diffstat (limited to 'cli/js/ops')
-rw-r--r--cli/js/ops/compiler.ts55
-rw-r--r--cli/js/ops/dispatch_json.ts97
-rw-r--r--cli/js/ops/dispatch_minimal.ts115
-rw-r--r--cli/js/ops/errors.ts61
-rw-r--r--cli/js/ops/fetch.ts28
-rw-r--r--cli/js/ops/fs_events.ts39
-rw-r--r--cli/js/ops/get_random_values.ts30
-rw-r--r--cli/js/ops/os.ts227
-rw-r--r--cli/js/ops/repl.ts11
-rw-r--r--cli/js/ops/resources.ts23
-rw-r--r--cli/js/ops/runtime.ts67
-rw-r--r--cli/js/ops/runtime_compiler.ts23
-rw-r--r--cli/js/ops/tty.ts14
13 files changed, 790 insertions, 0 deletions
diff --git a/cli/js/ops/compiler.ts b/cli/js/ops/compiler.ts
new file mode 100644
index 000000000..b45ad42b2
--- /dev/null
+++ b/cli/js/ops/compiler.ts
@@ -0,0 +1,55 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { sendAsync, sendSync } from "./dispatch_json.ts";
+import { TextDecoder, TextEncoder } from "../web/text_encoding.ts";
+import { core } from "../core.ts";
+
+/** Ops to Rust to resolve modules' URLs. */
+export function resolveModules(
+ specifiers: string[],
+ referrer?: string
+): string[] {
+ return sendSync("op_resolve_modules", { specifiers, referrer });
+}
+
+/** Ops to Rust to fetch modules meta data. */
+export function fetchSourceFiles(
+ specifiers: string[],
+ referrer?: string
+): Promise<
+ Array<{
+ url: string;
+ filename: string;
+ mediaType: number;
+ sourceCode: string;
+ }>
+> {
+ return sendAsync("op_fetch_source_files", {
+ specifiers,
+ referrer
+ });
+}
+
+const encoder = new TextEncoder();
+const decoder = new TextDecoder();
+
+/** This op is also used during snapshotting */
+export function getAsset(name: string): string {
+ const opId = core.ops()["op_fetch_asset"];
+ // We really don't want to depend on JSON dispatch during snapshotting, so
+ // this op exchanges strings with Rust as raw byte arrays.
+ const sourceCodeBytes = core.dispatch(opId, encoder.encode(name));
+ return decoder.decode(sourceCodeBytes!);
+}
+
+export function cache(
+ extension: string,
+ moduleId: string,
+ contents: string
+): void {
+ sendSync("op_cache", {
+ extension,
+ moduleId,
+ contents
+ });
+}
diff --git a/cli/js/ops/dispatch_json.ts b/cli/js/ops/dispatch_json.ts
new file mode 100644
index 000000000..6fb9007df
--- /dev/null
+++ b/cli/js/ops/dispatch_json.ts
@@ -0,0 +1,97 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import * as util from "../util.ts";
+import { TextEncoder, TextDecoder } from "../web/text_encoding.ts";
+import { core } from "../core.ts";
+import { OPS_CACHE } from "../runtime.ts";
+import { ErrorKind, getErrorClass } from "../errors.ts";
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type Ok = any;
+
+interface JsonError {
+ kind: ErrorKind;
+ message: string;
+}
+
+interface JsonResponse {
+ ok?: Ok;
+ err?: JsonError;
+ promiseId?: number; // Only present in async messages.
+}
+
+const promiseTable = new Map<number, util.Resolvable<JsonResponse>>();
+let _nextPromiseId = 1;
+
+function nextPromiseId(): number {
+ return _nextPromiseId++;
+}
+
+function decode(ui8: Uint8Array): JsonResponse {
+ const s = new TextDecoder().decode(ui8);
+ return JSON.parse(s) as JsonResponse;
+}
+
+function encode(args: object): Uint8Array {
+ const s = JSON.stringify(args);
+ return new TextEncoder().encode(s);
+}
+
+function unwrapResponse(res: JsonResponse): Ok {
+ if (res.err != null) {
+ throw new (getErrorClass(res.err.kind))(res.err.message);
+ }
+ util.assert(res.ok != null);
+ return res.ok;
+}
+
+export function asyncMsgFromRust(resUi8: Uint8Array): void {
+ const res = decode(resUi8);
+ util.assert(res.promiseId != null);
+
+ const promise = promiseTable.get(res.promiseId!);
+ util.assert(promise != null);
+ promiseTable.delete(res.promiseId!);
+ promise.resolve(res);
+}
+
+export function sendSync(
+ opName: string,
+ args: object = {},
+ zeroCopy?: Uint8Array
+): Ok {
+ const opId = OPS_CACHE[opName];
+ util.log("sendSync", opName, opId);
+ const argsUi8 = encode(args);
+ const resUi8 = core.dispatch(opId, argsUi8, zeroCopy);
+ util.assert(resUi8 != null);
+
+ const res = decode(resUi8);
+ util.assert(res.promiseId == null);
+ return unwrapResponse(res);
+}
+
+export async function sendAsync(
+ opName: string,
+ args: object = {},
+ zeroCopy?: Uint8Array
+): Promise<Ok> {
+ const opId = OPS_CACHE[opName];
+ util.log("sendAsync", opName, opId);
+ const promiseId = nextPromiseId();
+ args = Object.assign(args, { promiseId });
+ const promise = util.createResolvable<Ok>();
+
+ const argsUi8 = encode(args);
+ const buf = core.dispatch(opId, argsUi8, zeroCopy);
+ if (buf) {
+ // Sync result.
+ const res = decode(buf);
+ promise.resolve(res);
+ } else {
+ // Async result.
+ promiseTable.set(promiseId, promise);
+ }
+
+ const res = await promise;
+ return unwrapResponse(res);
+}
diff --git a/cli/js/ops/dispatch_minimal.ts b/cli/js/ops/dispatch_minimal.ts
new file mode 100644
index 000000000..7aec4683c
--- /dev/null
+++ b/cli/js/ops/dispatch_minimal.ts
@@ -0,0 +1,115 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import * as util from "../util.ts";
+import { core } from "../core.ts";
+import { TextDecoder } from "../web/text_encoding.ts";
+import { ErrorKind, errors, getErrorClass } from "../errors.ts";
+
+const promiseTableMin = new Map<number, util.Resolvable<RecordMinimal>>();
+// Note it's important that promiseId starts at 1 instead of 0, because sync
+// messages are indicated with promiseId 0. If we ever add wrap around logic for
+// overflows, this should be taken into account.
+let _nextPromiseId = 1;
+
+const decoder = new TextDecoder();
+
+function nextPromiseId(): number {
+ return _nextPromiseId++;
+}
+
+export interface RecordMinimal {
+ promiseId: number;
+ arg: number;
+ result: number;
+ err?: {
+ kind: ErrorKind;
+ message: string;
+ };
+}
+
+export function recordFromBufMinimal(ui8: Uint8Array): RecordMinimal {
+ const header = ui8.subarray(0, 12);
+ const buf32 = new Int32Array(
+ header.buffer,
+ header.byteOffset,
+ header.byteLength / 4
+ );
+ const promiseId = buf32[0];
+ const arg = buf32[1];
+ const result = buf32[2];
+ let err;
+
+ if (arg < 0) {
+ const kind = result as ErrorKind;
+ const message = decoder.decode(ui8.subarray(12));
+ err = { kind, message };
+ } else if (ui8.length != 12) {
+ throw new errors.InvalidData("BadMessage");
+ }
+
+ return {
+ promiseId,
+ arg,
+ result,
+ err
+ };
+}
+
+function unwrapResponse(res: RecordMinimal): number {
+ if (res.err != null) {
+ throw new (getErrorClass(res.err.kind))(res.err.message);
+ }
+ return res.result;
+}
+
+const scratch32 = new Int32Array(3);
+const scratchBytes = new Uint8Array(
+ scratch32.buffer,
+ scratch32.byteOffset,
+ scratch32.byteLength
+);
+util.assert(scratchBytes.byteLength === scratch32.length * 4);
+
+export function asyncMsgFromRust(ui8: Uint8Array): void {
+ const record = recordFromBufMinimal(ui8);
+ const { promiseId } = record;
+ const promise = promiseTableMin.get(promiseId);
+ promiseTableMin.delete(promiseId);
+ util.assert(promise);
+ promise.resolve(record);
+}
+
+export async function sendAsyncMinimal(
+ opId: number,
+ arg: number,
+ zeroCopy: Uint8Array
+): Promise<number> {
+ const promiseId = nextPromiseId(); // AKA cmdId
+ scratch32[0] = promiseId;
+ scratch32[1] = arg;
+ scratch32[2] = 0; // result
+ const promise = util.createResolvable<RecordMinimal>();
+ const buf = core.dispatch(opId, scratchBytes, zeroCopy);
+ if (buf) {
+ const record = recordFromBufMinimal(buf);
+ // Sync result.
+ promise.resolve(record);
+ } else {
+ // Async result.
+ promiseTableMin.set(promiseId, promise);
+ }
+
+ const res = await promise;
+ return unwrapResponse(res);
+}
+
+export function sendSyncMinimal(
+ opId: number,
+ arg: number,
+ zeroCopy: Uint8Array
+): number {
+ scratch32[0] = 0; // promiseId 0 indicates sync
+ scratch32[1] = arg;
+ const res = core.dispatch(opId, scratchBytes, zeroCopy)!;
+ const resRecord = recordFromBufMinimal(res);
+ return unwrapResponse(resRecord);
+}
diff --git a/cli/js/ops/errors.ts b/cli/js/ops/errors.ts
new file mode 100644
index 000000000..f96e376d6
--- /dev/null
+++ b/cli/js/ops/errors.ts
@@ -0,0 +1,61 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { DiagnosticItem } from "../diagnostics.ts";
+import { sendSync } from "./dispatch_json.ts";
+
+/**
+ * Format an array of diagnostic items and return them as a single string.
+ * @param items An array of diagnostic items to format
+ */
+export function formatDiagnostics(items: DiagnosticItem[]): string {
+ return sendSync("op_format_diagnostic", { items });
+}
+
+export interface Location {
+ /** The full url for the module, e.g. `file://some/file.ts` or
+ * `https://some/file.ts`. */
+ filename: string;
+
+ /** The line number in the file. It is assumed to be 1-indexed. */
+ line: number;
+
+ /** The column number in the file. It is assumed to be 1-indexed. */
+ column: number;
+}
+
+/** Given a current location in a module, lookup the source location and
+ * return it.
+ *
+ * When Deno transpiles code, it keep source maps of the transpiled code. This
+ * function can be used to lookup the original location. This is automatically
+ * done when accessing the `.stack` of an error, or when an uncaught error is
+ * logged. This function can be used to perform the lookup for creating better
+ * error handling.
+ *
+ * **Note:** `line` and `column` are 1 indexed, which matches display
+ * expectations, but is not typical of most index numbers in Deno.
+ *
+ * An example:
+ *
+ * const orig = Deno.applySourceMap({
+ * location: "file://my/module.ts",
+ * line: 5,
+ * column: 15
+ * });
+ * console.log(`${orig.filename}:${orig.line}:${orig.column}`);
+ *
+ */
+export function applySourceMap(location: Location): Location {
+ const { filename, line, column } = location;
+ // On this side, line/column are 1 based, but in the source maps, they are
+ // 0 based, so we have to convert back and forth
+ const res = sendSync("op_apply_source_map", {
+ filename,
+ line: line - 1,
+ column: column - 1
+ });
+ return {
+ filename: res.filename,
+ line: res.line + 1,
+ column: res.column + 1
+ };
+}
diff --git a/cli/js/ops/fetch.ts b/cli/js/ops/fetch.ts
new file mode 100644
index 000000000..c5c0cb883
--- /dev/null
+++ b/cli/js/ops/fetch.ts
@@ -0,0 +1,28 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { sendAsync } from "./dispatch_json.ts";
+
+interface FetchRequest {
+ url: string;
+ method: string | null;
+ headers: Array<[string, string]>;
+}
+
+export interface FetchResponse {
+ bodyRid: number;
+ status: number;
+ statusText: string;
+ headers: Array<[string, string]>;
+}
+
+export async function fetch(
+ args: FetchRequest,
+ body: ArrayBufferView | undefined
+): Promise<FetchResponse> {
+ let zeroCopy = undefined;
+ if (body) {
+ zeroCopy = new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
+ }
+
+ return await sendAsync("op_fetch", args, zeroCopy);
+}
diff --git a/cli/js/ops/fs_events.ts b/cli/js/ops/fs_events.ts
new file mode 100644
index 000000000..09e82c515
--- /dev/null
+++ b/cli/js/ops/fs_events.ts
@@ -0,0 +1,39 @@
+// Copyright 2019 the Deno authors. All rights reserved. MIT license.
+import { sendSync, sendAsync } from "./dispatch_json.ts";
+import { close } from "./resources.ts";
+
+export interface FsEvent {
+ kind: "any" | "access" | "create" | "modify" | "remove";
+ paths: string[];
+}
+
+class FsEvents implements AsyncIterableIterator<FsEvent> {
+ readonly rid: number;
+
+ constructor(paths: string[], options: { recursive: boolean }) {
+ const { recursive } = options;
+ this.rid = sendSync("op_fs_events_open", { recursive, paths });
+ }
+
+ async next(): Promise<IteratorResult<FsEvent>> {
+ return await sendAsync("op_fs_events_poll", {
+ rid: this.rid
+ });
+ }
+
+ async return(value?: FsEvent): Promise<IteratorResult<FsEvent>> {
+ close(this.rid);
+ return { value, done: true };
+ }
+
+ [Symbol.asyncIterator](): AsyncIterableIterator<FsEvent> {
+ return this;
+ }
+}
+
+export function fsEvents(
+ paths: string | string[],
+ options = { recursive: true }
+): AsyncIterableIterator<FsEvent> {
+ return new FsEvents(Array.isArray(paths) ? paths : [paths], options);
+}
diff --git a/cli/js/ops/get_random_values.ts b/cli/js/ops/get_random_values.ts
new file mode 100644
index 000000000..0e384c259
--- /dev/null
+++ b/cli/js/ops/get_random_values.ts
@@ -0,0 +1,30 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { sendSync } from "./dispatch_json.ts";
+import { assert } from "../util.ts";
+
+/** Synchronously collects cryptographically secure random values. The
+ * underlying CSPRNG in use is Rust's `rand::rngs::ThreadRng`.
+ *
+ * const arr = new Uint8Array(32);
+ * crypto.getRandomValues(arr);
+ */
+export function getRandomValues<
+ T extends
+ | Int8Array
+ | Uint8Array
+ | Uint8ClampedArray
+ | Int16Array
+ | Uint16Array
+ | Int32Array
+ | Uint32Array
+>(typedArray: T): T {
+ assert(typedArray !== null, "Input must not be null");
+ assert(typedArray.length <= 65536, "Input must not be longer than 65536");
+ const ui8 = new Uint8Array(
+ typedArray.buffer,
+ typedArray.byteOffset,
+ typedArray.byteLength
+ );
+ sendSync("op_get_random_values", {}, ui8);
+ return typedArray;
+}
diff --git a/cli/js/ops/os.ts b/cli/js/ops/os.ts
new file mode 100644
index 000000000..2d27f7ef5
--- /dev/null
+++ b/cli/js/ops/os.ts
@@ -0,0 +1,227 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { sendSync } from "./dispatch_json.ts";
+import { errors } from "../errors.ts";
+import * as util from "../util.ts";
+
+/** Get the loadavg.
+ * Requires the `--allow-env` flag.
+ *
+ * console.log(Deno.loadavg());
+ */
+export function loadavg(): number[] {
+ return sendSync("op_loadavg");
+}
+
+/** Get the hostname.
+ * Requires the `--allow-env` flag.
+ *
+ * console.log(Deno.hostname());
+ */
+export function hostname(): string {
+ return sendSync("op_hostname");
+}
+
+/** Get OS release.
+ * Requires the `--allow-env` flag.
+ *
+ * console.log(Deno.osRelease());
+ */
+export function osRelease(): string {
+ return sendSync("op_os_release");
+}
+
+/** Exit the Deno process with optional exit code. */
+export function exit(code = 0): never {
+ sendSync("op_exit", { code });
+ return util.unreachable();
+}
+
+function setEnv(key: string, value: string): void {
+ sendSync("op_set_env", { key, value });
+}
+
+function getEnv(key: string): string | undefined {
+ return sendSync("op_get_env", { key })[0];
+}
+
+/** Returns a snapshot of the environment variables at invocation. Mutating a
+ * property in the object will set that variable in the environment for
+ * the process. The environment object will only accept `string`s
+ * as values.
+ *
+ * console.log(Deno.env("SHELL"));
+ * const myEnv = Deno.env();
+ * console.log(myEnv.SHELL);
+ * myEnv.TEST_VAR = "HELLO";
+ * const newEnv = Deno.env();
+ * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
+ */
+export function env(): { [index: string]: string };
+export function env(key: string): string | undefined;
+export function env(
+ key?: string
+): { [index: string]: string } | string | undefined {
+ if (key) {
+ return getEnv(key);
+ }
+ const env = sendSync("op_env");
+ return new Proxy(env, {
+ set(obj, prop: string, value: string): boolean {
+ setEnv(prop, value);
+ return Reflect.set(obj, prop, value);
+ }
+ });
+}
+
+type DirKind =
+ | "home"
+ | "cache"
+ | "config"
+ | "executable"
+ | "data"
+ | "data_local"
+ | "audio"
+ | "desktop"
+ | "document"
+ | "download"
+ | "font"
+ | "picture"
+ | "public"
+ | "template"
+ | "tmp"
+ | "video";
+
+/**
+ * Returns the user and platform specific directories.
+ * Requires the `--allow-env` flag.
+ * Returns null if there is no applicable directory or if any other error
+ * occurs.
+ *
+ * Argument values: "home", "cache", "config", "executable", "data",
+ * "data_local", "audio", "desktop", "document", "download", "font", "picture",
+ * "public", "template", "video"
+ *
+ * "cache"
+ * |Platform | Value | Example |
+ * | ------- | ----------------------------------- | ---------------------------- |
+ * | Linux | `$XDG_CACHE_HOME` or `$HOME`/.cache | /home/alice/.cache |
+ * | macOS | `$HOME`/Library/Caches | /Users/Alice/Library/Caches |
+ * | Windows | `{FOLDERID_LocalAppData}` | C:\Users\Alice\AppData\Local |
+ *
+ * "config"
+ * |Platform | Value | Example |
+ * | ------- | ------------------------------------- | -------------------------------- |
+ * | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config |
+ * | macOS | `$HOME`/Library/Preferences | /Users/Alice/Library/Preferences |
+ * | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
+ *
+ * "executable"
+ * |Platform | Value | Example |
+ * | ------- | --------------------------------------------------------------- | -----------------------|
+ * | Linux | `XDG_BIN_HOME` or `$XDG_DATA_HOME`/../bin or `$HOME`/.local/bin | /home/alice/.local/bin |
+ * | macOS | - | - |
+ * | Windows | - | - |
+ *
+ * "data"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------------------------- | ---------------------------------------- |
+ * | Linux | `$XDG_DATA_HOME` or `$HOME`/.local/share | /home/alice/.local/share |
+ * | macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support |
+ * | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
+ *
+ * "data_local"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------------------------- | ---------------------------------------- |
+ * | Linux | `$XDG_DATA_HOME` or `$HOME`/.local/share | /home/alice/.local/share |
+ * | macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support |
+ * | Windows | `{FOLDERID_LocalAppData}` | C:\Users\Alice\AppData\Local |
+ *
+ * "audio"
+ * |Platform | Value | Example |
+ * | ------- | ------------------ | -------------------- |
+ * | Linux | `XDG_MUSIC_DIR` | /home/alice/Music |
+ * | macOS | `$HOME`/Music | /Users/Alice/Music |
+ * | Windows | `{FOLDERID_Music}` | C:\Users\Alice\Music |
+ *
+ * "desktop"
+ * |Platform | Value | Example |
+ * | ------- | -------------------- | ---------------------- |
+ * | Linux | `XDG_DESKTOP_DIR` | /home/alice/Desktop |
+ * | macOS | `$HOME`/Desktop | /Users/Alice/Desktop |
+ * | Windows | `{FOLDERID_Desktop}` | C:\Users\Alice\Desktop |
+ *
+ * "document"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------- | ------------------------ |
+ * | Linux | `XDG_DOCUMENTS_DIR` | /home/alice/Documents |
+ * | macOS | `$HOME`/Documents | /Users/Alice/Documents |
+ * | Windows | `{FOLDERID_Documents}` | C:\Users\Alice\Documents |
+ *
+ * "download"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------- | ------------------------ |
+ * | Linux | `XDG_DOWNLOAD_DIR` | /home/alice/Downloads |
+ * | macOS | `$HOME`/Downloads | /Users/Alice/Downloads |
+ * | Windows | `{FOLDERID_Downloads}` | C:\Users\Alice\Downloads |
+ *
+ * "font"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------------------------------------- | ------------------------------ |
+ * | Linux | `$XDG_DATA_HOME`/fonts or `$HOME`/.local/share/fonts | /home/alice/.local/share/fonts |
+ * | macOS | `$HOME/Library/Fonts` | /Users/Alice/Library/Fonts |
+ * | Windows | – | – |
+ *
+ * "picture"
+ * |Platform | Value | Example |
+ * | ------- | --------------------- | ----------------------- |
+ * | Linux | `XDG_PICTURES_DIR` | /home/alice/Pictures |
+ * | macOS | `$HOME`/Pictures | /Users/Alice/Pictures |
+ * | Windows | `{FOLDERID_Pictures}` | C:\Users\Alice\Pictures |
+ *
+ * "public"
+ * |Platform | Value | Example |
+ * | ------- | --------------------- | ------------------- |
+ * | Linux | `XDG_PUBLICSHARE_DIR` | /home/alice/Public |
+ * | macOS | `$HOME`/Public | /Users/Alice/Public |
+ * | Windows | `{FOLDERID_Public}` | C:\Users\Public |
+ *
+ * "template"
+ * |Platform | Value | Example |
+ * | ------- | ---------------------- | ---------------------------------------------------------- |
+ * | Linux | `XDG_TEMPLATES_DIR` | /home/alice/Templates |
+ * | macOS | – | – |
+ * | Windows | `{FOLDERID_Templates}` | C:\Users\Alice\AppData\Roaming\Microsoft\Windows\Templates |
+ *
+ * "tmp"
+ *
+ * |Platform | Value | Example |
+ * | ------- | ---------------------- | ---------------------------------------------------------- |
+ * | Linux | `TMPDIR` | /tmp |
+ * | macOS | `TMPDIR` | /tmp |
+ * | Windows | `{TMP}` | C:\Users\Alice\AppData\Local\Temp |
+ *
+ * "video"
+ * |Platform | Value | Example |
+ * | ------- | ------------------- | --------------------- |
+ * | Linux | `XDG_VIDEOS_DIR` | /home/alice/Videos |
+ * | macOS | `$HOME`/Movies | /Users/Alice/Movies |
+ * | Windows | `{FOLDERID_Videos}` | C:\Users\Alice\Videos |
+ */
+export function dir(kind: DirKind): string | null {
+ try {
+ return sendSync("op_get_dir", { kind });
+ } catch (error) {
+ if (error instanceof errors.PermissionDenied) {
+ throw error;
+ }
+ return null;
+ }
+}
+
+/**
+ * Returns the path to the current deno executable.
+ * Requires the `--allow-env` flag.
+ */
+export function execPath(): string {
+ return sendSync("op_exec_path");
+}
diff --git a/cli/js/ops/repl.ts b/cli/js/ops/repl.ts
new file mode 100644
index 000000000..3f40f4456
--- /dev/null
+++ b/cli/js/ops/repl.ts
@@ -0,0 +1,11 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { sendSync, sendAsync } from "./dispatch_json.ts";
+
+export function startRepl(historyFile: string): number {
+ return sendSync("op_repl_start", { historyFile });
+}
+
+export async function readline(rid: number, prompt: string): Promise<string> {
+ return sendAsync("op_repl_readline", { rid, prompt });
+}
diff --git a/cli/js/ops/resources.ts b/cli/js/ops/resources.ts
new file mode 100644
index 000000000..b04810989
--- /dev/null
+++ b/cli/js/ops/resources.ts
@@ -0,0 +1,23 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { sendSync } from "./dispatch_json.ts";
+
+export interface ResourceMap {
+ [rid: number]: string;
+}
+
+/** Returns a map of open _file like_ resource ids along with their string
+ * representation.
+ */
+export function resources(): ResourceMap {
+ const res = sendSync("op_resources") as Array<[number, string]>;
+ const resources: ResourceMap = {};
+ for (const resourceTuple of res) {
+ resources[resourceTuple[0]] = resourceTuple[1];
+ }
+ return resources;
+}
+
+/** Close the given resource ID. */
+export function close(rid: number): void {
+ sendSync("op_close", { rid });
+}
diff --git a/cli/js/ops/runtime.ts b/cli/js/ops/runtime.ts
new file mode 100644
index 000000000..7538ce12f
--- /dev/null
+++ b/cli/js/ops/runtime.ts
@@ -0,0 +1,67 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { sendSync } from "./dispatch_json.ts";
+
+// TODO(bartlomieju): these two types are duplicated
+// in `cli/js/build.ts` - deduplicate
+export type OperatingSystem = "mac" | "win" | "linux";
+export type Arch = "x64" | "arm64";
+
+export interface Start {
+ cwd: string;
+ pid: number;
+ args: string[];
+ location: string; // Absolute URL.
+ repl: boolean;
+ debugFlag: boolean;
+ depsFlag: boolean;
+ typesFlag: boolean;
+ versionFlag: boolean;
+ denoVersion: string;
+ v8Version: string;
+ tsVersion: string;
+ noColor: boolean;
+ os: OperatingSystem;
+ arch: Arch;
+}
+
+export function start(): Start {
+ return sendSync("op_start");
+}
+
+export interface Metrics {
+ opsDispatched: number;
+ opsDispatchedSync: number;
+ opsDispatchedAsync: number;
+ opsDispatchedAsyncUnref: number;
+ opsCompleted: number;
+ opsCompletedSync: number;
+ opsCompletedAsync: number;
+ opsCompletedAsyncUnref: number;
+ bytesSentControl: number;
+ bytesSentData: number;
+ bytesReceived: number;
+}
+
+/** Receive metrics from the privileged side of Deno.
+ *
+ * > console.table(Deno.metrics())
+ * ┌─────────────────────────┬────────┐
+ * │ (index) │ Values │
+ * ├─────────────────────────┼────────┤
+ * │ opsDispatched │ 3 │
+ * │ opsDispatchedSync │ 2 │
+ * │ opsDispatchedAsync │ 1 │
+ * │ opsDispatchedAsyncUnref │ 0 │
+ * │ opsCompleted │ 3 │
+ * │ opsCompletedSync │ 2 │
+ * │ opsCompletedAsync │ 1 │
+ * │ opsCompletedAsyncUnref │ 0 │
+ * │ bytesSentControl │ 73 │
+ * │ bytesSentData │ 0 │
+ * │ bytesReceived │ 375 │
+ * └─────────────────────────┴────────┘
+ */
+export function metrics(): Metrics {
+ return sendSync("op_metrics");
+}
diff --git a/cli/js/ops/runtime_compiler.ts b/cli/js/ops/runtime_compiler.ts
new file mode 100644
index 000000000..035a4ef59
--- /dev/null
+++ b/cli/js/ops/runtime_compiler.ts
@@ -0,0 +1,23 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+import { sendAsync } from "./dispatch_json.ts";
+
+interface CompileRequest {
+ rootName: string;
+ sources?: Record<string, string>;
+ options?: string;
+ bundle: boolean;
+}
+
+export async function compile(request: CompileRequest): Promise<string> {
+ return sendAsync("op_compile", request);
+}
+
+interface TranspileRequest {
+ sources: Record<string, string>;
+ options?: string;
+}
+
+export async function transpile(request: TranspileRequest): Promise<string> {
+ return sendAsync("op_transpile", request);
+}
diff --git a/cli/js/ops/tty.ts b/cli/js/ops/tty.ts
new file mode 100644
index 000000000..2ad44d025
--- /dev/null
+++ b/cli/js/ops/tty.ts
@@ -0,0 +1,14 @@
+import { sendSync } from "./dispatch_json.ts";
+
+/** Check if a given resource is TTY. */
+export function isatty(rid: number): boolean {
+ return sendSync("op_isatty", { rid });
+}
+
+/** Set TTY to be under raw mode or not. */
+export function setRaw(rid: number, mode: boolean): void {
+ sendSync("op_set_raw", {
+ rid,
+ mode
+ });
+}