diff --git a/packages/preview2-shim/browser/clocks.js b/packages/preview2-shim/browser/clocks.js new file mode 100644 index 000000000..e62c29705 --- /dev/null +++ b/packages/preview2-shim/browser/clocks.js @@ -0,0 +1,47 @@ +export function wallClockNow(clock) { + if (clock === 1) { + const seconds = BigInt(Math.floor(Date.now() / 1000)); + const nanoseconds = (Date.now() % 1000) * 1000 * 1000; + return { seconds, nanoseconds }; + } + console.log("[clocks] UNKNOWN CLOCK"); +} + +export function monotonicClockResolution(clock) { + console.log(`[clocks] Monotonic clock resolution ${clock}`); +} + +export function wallClockResolution(clock) { + console.log(`[clocks] Wall clock resolution ${clock}`); +} + +let hrStart = hrtimeBigint(); + +export function monotonicClockNow(clock) { + if (clock === 0) { + return hrtimeBigint() - hrStart; + } + console.log("UNKNOWN CLOCK"); +} + +function hrtime(previousTimestamp) { + const baseNow = Math.floor((Date.now() - performance.now()) * 1e-3); + const clocktime = performance.now() * 1e-3; + let seconds = Math.floor(clocktime) + baseNow; + let nanoseconds = Math.floor((clocktime % 1) * 1e9); + + if (previousTimestamp) { + seconds = seconds - previousTimestamp[0]; + nanoseconds = nanoseconds - previousTimestamp[1]; + if (nanoseconds < 0) { + seconds--; + nanoseconds += 1e9; + } + } + return [seconds, nanoseconds]; +} + +function hrtimeBigint(time) { + const diff = hrtime(time); + return BigInt(diff[0] * 1e9 + diff[1]); +} diff --git a/packages/preview2-shim/browser/default-clocks.js b/packages/preview2-shim/browser/default-clocks.js new file mode 100644 index 000000000..3a5854a62 --- /dev/null +++ b/packages/preview2-shim/browser/default-clocks.js @@ -0,0 +1,7 @@ +export function defaultMonotonicClock() { + return 0; +} + +export function defaultWallClock() { + return 1; +} diff --git a/packages/preview2-shim/browser/exit.js b/packages/preview2-shim/browser/exit.js new file mode 100644 index 000000000..5509bdc65 --- /dev/null +++ b/packages/preview2-shim/browser/exit.js @@ -0,0 +1,21 @@ +class FailureExit extends Error { + code = 1; + constructor() { + super("failure-exit"); + } +} + +class SuccessfulExit extends Error { + code = 0; + constructor() { + super("successful-exit"); + } +} + +export function exit(status) { + console.log(`[exit] Exit: ${JSON.stringify(status)}`); + if (status.tag === "err") { + throw new FailureExit(); + } + throw new SuccessfulExit(); +} diff --git a/packages/preview2-shim/browser/filesystem.js b/packages/preview2-shim/browser/filesystem.js new file mode 100644 index 000000000..54daf07ef --- /dev/null +++ b/packages/preview2-shim/browser/filesystem.js @@ -0,0 +1,47 @@ +export function flags(fd) { + console.log(`[filesystem] FLAGS FOR ${fd}`); +} + +export function setFlags(fd, flags) { + console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); +} + +export function close(fd) { + console.log(`[filesystem] CLOSE: ${fd}`); +} + +export function removeDirectoryAt(fd, path) { + console.log(`[filesystem] RM DIR: ${fd} ${path}`); +} + +export function unlinkFileAt(fd, path) { + console.log(`[filesystem] UNLINK: ${fd} ${path}`); +} + +export function writeViaStream(fd, offset) { + console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); +} + +export function appendViaStream(fd, offset) { + console.log(`[filesystem] APPEND STREAM ${fd} ${offset}`); +} + +export function readViaStream(fd, offset) { + console.log(`[filesystem] READ STREAM ${fd} ${offset}`); +} + +export function openAt(fd, atFlags, path, offset) { + console.log(`[filesystem] OPEN AT ${fd}`); +} + +export function stat(fd) { + console.log(`[filesystem] STAT: ${fd}`); +} + +export function todoType(fd) { + console.log(`[filesystem] TODO TYPE: ${fd}`); +} + +export function closeDirEntryStream(s) { + console.log(`[filesystem] CLOSE DIR ENTRY STREAM`); +} diff --git a/packages/preview2-shim/browser/http.js b/packages/preview2-shim/browser/http.js new file mode 100644 index 000000000..c03653b55 --- /dev/null +++ b/packages/preview2-shim/browser/http.js @@ -0,0 +1,28 @@ +import { UnexpectedError } from "../http/error.js"; + +/** + * @param {import("../types/wasi-http").Request} req + * @returns {string} + */ +export function send(req) { + console.log(`[http] Send (browser) ${req.uri}`); + try { + const xhr = new XMLHttpRequest(); + xhr.open(req.method.toString(), req.uri, false); + xhr.responseType = "arraybuffer"; + for (let [name, value] of req.headers) { + xhr.setRequestHeader(name, value); + } + xhr.send(req.body.length > 0 ? req.body : null); + return { + status: xhr.status, + headers: xhr + .getAllResponseHeaders() + .trim() + .split(/[\r\n]+/), + body: xhr.response.byteLength > 0 ? xhr.response : undefined, + }; + } catch (err) { + throw new UnexpectedError(err.message); + } +} diff --git a/packages/preview2-shim/browser/index.js b/packages/preview2-shim/browser/index.js new file mode 100644 index 000000000..18a399e35 --- /dev/null +++ b/packages/preview2-shim/browser/index.js @@ -0,0 +1,21 @@ +import * as clocks from "./clocks.js"; +import * as defaultClocks from "./default-clocks.js"; +import * as exit from "./exit.js"; +import * as filesystem from "./filesystem.js"; +import * as io from "./io.js"; +import * as logging from "./logging.js"; +import * as poll from "./poll.js"; +import * as random from "./random.js"; +import * as stderr from "./stderr.js"; + +export const importObject = { + "wasi-clocks": clocks, + "wasi-default-clocks": defaultClocks, + "wasi-exit": exit, + "wasi-filesystem": filesystem, + "wasi-io": io, + "wasi-logging": logging, + "wasi-poll": poll, + "wasi-random": random, + "wasi-stderr": stderr, +}; diff --git a/packages/preview2-shim/browser/io.js b/packages/preview2-shim/browser/io.js new file mode 100644 index 000000000..50aea15e3 --- /dev/null +++ b/packages/preview2-shim/browser/io.js @@ -0,0 +1,34 @@ +export function dropInputStream(f) { + console.log(`[io] Drop input stream ${f}`); +} + +export function dropOutputStream(f) { + console.log(`[io] Drop output stream ${f}`); +} + +export function read(src, len) { + console.log(`[io] Read ${src}`); +} + +export function write(dst, buf) { + switch (dst) { + case 0: + throw new Error(`TODO: write stdin`); + case 1: + const decoder = new TextDecoder(); + console.log(decoder.decode(buf)); + return BigInt(buf.byteLength); + case 2: + throw new Error(`TODO: write stdout`); + default: + throw new Error(`TODO: write ${dst}`); + } +} + +export function skip(src, len) { + console.log(`[io] Skip ${src}`); +} + +export function write_repeated(dst, byte, len) { + console.log(`[io] Write repeated ${dst}`); +} diff --git a/packages/preview2-shim/browser/logging.js b/packages/preview2-shim/browser/logging.js new file mode 100644 index 000000000..2d3f0890a --- /dev/null +++ b/packages/preview2-shim/browser/logging.js @@ -0,0 +1,12 @@ +const levels = ["trace", "debug", "info", "warn", "error"]; + +let logLevel = levels.indexOf("warn"); + +export function setLevel(level) { + logLevel = levels.indexOf(level); +} + +export function log(level, context, msg) { + if (logLevel > levels.indexOf(level)) return; + console[level](`(${context}) ${msg}\n`); +} diff --git a/packages/preview2-shim/browser/poll.js b/packages/preview2-shim/browser/poll.js new file mode 100644 index 000000000..316afdd71 --- /dev/null +++ b/packages/preview2-shim/browser/poll.js @@ -0,0 +1,3 @@ +export function pollOneoff(f) { + console.log(`[poll] Poll oneoff ${f}`); +} diff --git a/packages/preview2-shim/browser/random.js b/packages/preview2-shim/browser/random.js new file mode 100644 index 000000000..775ec5322 --- /dev/null +++ b/packages/preview2-shim/browser/random.js @@ -0,0 +1,19 @@ +const MAX_BYTES = 65536; + +export function getRandomBytes(len) { + const bytes = new Uint8Array(len); + + if (len > MAX_BYTES) { + // this is the max bytes crypto.getRandomValues + // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues + for (var generated = 0; generated < len; generated += MAX_BYTES) { + // buffer.slice automatically checks if the end is past the end of + // the buffer so we don't have to here + crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES)); + } + } else { + crypto.getRandomValues(bytes); + } + + return bytes; +} diff --git a/packages/preview2-shim/browser/stderr.js b/packages/preview2-shim/browser/stderr.js new file mode 100644 index 000000000..d9784ebaa --- /dev/null +++ b/packages/preview2-shim/browser/stderr.js @@ -0,0 +1,3 @@ +export function print(message) { + console.error(`${message}\n`); +} diff --git a/packages/preview2-shim/http/error.js b/packages/preview2-shim/http/error.js new file mode 100644 index 000000000..d896b3651 --- /dev/null +++ b/packages/preview2-shim/http/error.js @@ -0,0 +1,11 @@ +export class UnexpectedError extends Error { + /** @type { import("../types/wasi-http").HttpErrorUnexpectedError } */ + payload; + constructor(message = "unexpected-error") { + super(message); + this.payload = { + tag: "unexpected-error", + val: message, + }; + } +} diff --git a/packages/preview2-shim/http/make-request.js b/packages/preview2-shim/http/make-request.js new file mode 100644 index 000000000..0b3dba3ca --- /dev/null +++ b/packages/preview2-shim/http/make-request.js @@ -0,0 +1,29 @@ +import { runAsWorker } from "synckit"; + +/** + * @param {import("../types/wasi-http").Request} req + * @returns {Promise} + */ +async function makeRequest(req) { + try { + let headers = new Headers(req.headers); + const resp = await fetch(req.uri, { + method: req.method.toString(), + headers, + body: req.body.length > 0 ? req.body : undefined, + }); + let arrayBuffer = await resp.arrayBuffer(); + return JSON.stringify({ + status: resp.status, + headers: resp.headers, + body: + arrayBuffer.byteLength > 0 + ? Buffer.from(arrayBuffer).toString("base64") + : undefined, + }); + } catch (err) { + return err.message; + } +} + +runAsWorker(makeRequest); diff --git a/packages/preview2-shim/index.d.ts b/packages/preview2-shim/index.d.ts new file mode 100644 index 000000000..48b3aa9f4 --- /dev/null +++ b/packages/preview2-shim/index.d.ts @@ -0,0 +1,9 @@ +export * as clocks from "./types/wasi-clocks"; +export * as defaultClocks from "./types/wasi-default-clocks"; +export * as exit from "./types/wasi-exit"; +export * as filesystem from "./types/wasi-filesystem"; +export * as io from "./types/wasi-io"; +export * as logging from "./types/wasi-logging"; +export * as poll from "./types/wasi-poll"; +export * as random from "./types/wasi-random"; +export * as stderr from "./types/wasi-stderr"; diff --git a/packages/preview2-shim/index.js b/packages/preview2-shim/index.js new file mode 100644 index 000000000..a8af064ba --- /dev/null +++ b/packages/preview2-shim/index.js @@ -0,0 +1 @@ +export * from "./nodejs/index.js"; diff --git a/packages/preview2-shim/nodejs/clocks.js b/packages/preview2-shim/nodejs/clocks.js new file mode 100644 index 000000000..2e81a44a5 --- /dev/null +++ b/packages/preview2-shim/nodejs/clocks.js @@ -0,0 +1,27 @@ +import { hrtime } from "node:process"; + +export function wallClockNow(clock) { + if (clock === 1) { + const seconds = BigInt(Math.floor(Date.now() / 1000)); + const nanoseconds = (Date.now() % 1000) * 1000 * 1000; + return { seconds, nanoseconds }; + } + console.log("[clocks] UNKNOWN CLOCK"); +} + +export function monotonicClockResolution(clock) { + console.log(`[clocks] Monotonic clock resolution ${clock}`); +} + +export function wallClockResolution(clock) { + console.log(`[clocks] Wall clock resolution ${clock}`); +} + +let hrStart = hrtime.bigint(); + +export function monotonicClockNow(clock) { + if (clock === 0) { + return hrtime.bigint() - hrStart; + } + console.log("[clocks] UNKNOWN CLOCK"); +} diff --git a/packages/preview2-shim/nodejs/default-clocks.js b/packages/preview2-shim/nodejs/default-clocks.js new file mode 100644 index 000000000..3a5854a62 --- /dev/null +++ b/packages/preview2-shim/nodejs/default-clocks.js @@ -0,0 +1,7 @@ +export function defaultMonotonicClock() { + return 0; +} + +export function defaultWallClock() { + return 1; +} diff --git a/packages/preview2-shim/nodejs/exit.js b/packages/preview2-shim/nodejs/exit.js new file mode 100644 index 000000000..2fea22774 --- /dev/null +++ b/packages/preview2-shim/nodejs/exit.js @@ -0,0 +1,4 @@ +export function exit(status) { + console.log(`[exit] Exit: ${JSON.stringify(status)}`); + process.exit(status.tag === 'err' ? 1 : 0); +} diff --git a/packages/preview2-shim/nodejs/filesystem.js b/packages/preview2-shim/nodejs/filesystem.js new file mode 100644 index 000000000..b2c41dfa1 --- /dev/null +++ b/packages/preview2-shim/nodejs/filesystem.js @@ -0,0 +1,58 @@ +// export interface DescriptorStat { +// dev: Device, +// ino: Inode, +// type: DescriptorType, +// nlink: Linkcount, +// size: Filesize, +// atim: Timestamp, +// mtim: Timestamp, +// ctim: Timestamp, +// } + +export function flags(fd) { + console.log(`[filesystem] FLAGS FOR ${fd}`); +} + +export function setFlags(fd, flags) { + console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); +} + +export function close(fd) { + console.log(`[filesystem] CLOSE: ${fd}`); +} + +export function removeDirectoryAt(fd, path) { + console.log(`[filesystem] RM DIR: ${fd} ${path}`); +} + +export function unlinkFileAt(fd, path) { + console.log(`[filesystem] UNLINK: ${fd} ${path}`); +} + +export function writeViaStream(fd, offset) { + console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); +} + +export function appendViaStream(fd, offset) { + console.log(`[filesystem] APPEND STREAM ${fd} ${offset}`); +} + +export function readViaStream(fd, offset) { + console.log(`[filesystem] READ STREAM ${fd} ${offset}`); +} + +export function openAt(fd, atFlags, path, offset) { + console.log(`[filesystem] OPEN AT ${fd}`); +} + +export function stat(fd) { + console.log(`[filesystem] STAT: ${fd}`); +} + +export function todoType(fd) { + console.log(`[filesystem] TODO TYPE: ${fd}`); +} + +export function closeDirEntryStream(s) { + console.log(`[filesystem] CLOSE DIR ENTRY STREAM`); +} diff --git a/packages/preview2-shim/nodejs/http.js b/packages/preview2-shim/nodejs/http.js new file mode 100644 index 000000000..e9591a8ec --- /dev/null +++ b/packages/preview2-shim/nodejs/http.js @@ -0,0 +1,19 @@ +import { createSyncFn } from "synckit"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { UnexpectedError } from "../http/error.js"; + +export function send(req) { + console.log(`[http] Send (nodejs) ${req.uri}`); + const dirname = path.dirname(fileURLToPath(import.meta.url)); + const syncFn = createSyncFn(path.resolve(dirname, "../http/make-request.js")); + let rawResponse = syncFn(req); + let response = JSON.parse(rawResponse); + if (response.status) { + return { + ...response, + body: response.body ? Buffer.from(response.body, "base64") : undefined, + }; + } + throw new UnexpectedError(response); +} diff --git a/packages/preview2-shim/nodejs/index.js b/packages/preview2-shim/nodejs/index.js new file mode 100644 index 000000000..18a399e35 --- /dev/null +++ b/packages/preview2-shim/nodejs/index.js @@ -0,0 +1,21 @@ +import * as clocks from "./clocks.js"; +import * as defaultClocks from "./default-clocks.js"; +import * as exit from "./exit.js"; +import * as filesystem from "./filesystem.js"; +import * as io from "./io.js"; +import * as logging from "./logging.js"; +import * as poll from "./poll.js"; +import * as random from "./random.js"; +import * as stderr from "./stderr.js"; + +export const importObject = { + "wasi-clocks": clocks, + "wasi-default-clocks": defaultClocks, + "wasi-exit": exit, + "wasi-filesystem": filesystem, + "wasi-io": io, + "wasi-logging": logging, + "wasi-poll": poll, + "wasi-random": random, + "wasi-stderr": stderr, +}; diff --git a/packages/preview2-shim/nodejs/io.js b/packages/preview2-shim/nodejs/io.js new file mode 100644 index 000000000..178adc1ef --- /dev/null +++ b/packages/preview2-shim/nodejs/io.js @@ -0,0 +1,33 @@ +export function dropInputStream(f) { + console.log(`[io] Drop input stream ${f}`); +} + +export function dropOutputStream(f) { + console.log(`[io] Drop output stream ${f}`); +} + +export function read(src, len) { + console.log(`[io] Read ${src}`); +} + +export function write(dst, buf) { + switch (dst) { + case 0: + throw new Error(`TODO: write stdin`); + case 1: + process.stdout.write(buf); + return BigInt(buf.byteLength); + case 2: + throw new Error(`TODO: write stdout`); + default: + throw new Error(`TODO: write ${dst}`); + } +} + +export function skip(src, len) { + console.log(`[io] Skip ${src}`); +} + +export function write_repeated(dst, byte, len) { + console.log(`[io] Write repeated ${dst}`); +} diff --git a/packages/preview2-shim/nodejs/logging.js b/packages/preview2-shim/nodejs/logging.js new file mode 100644 index 000000000..b19b831f8 --- /dev/null +++ b/packages/preview2-shim/nodejs/logging.js @@ -0,0 +1,12 @@ +const levels = ["trace", "debug", "info", "warn", "error"]; + +let logLevel = levels.indexOf("warn"); + +export function setLevel(level) { + logLevel = levels.indexOf(level); +} + +export function log(level, context, msg) { + if (logLevel > levels.indexOf(level)) return; + process.stdout.write(`${level}: (${context}) ${msg}\n`); +} diff --git a/packages/preview2-shim/nodejs/poll.js b/packages/preview2-shim/nodejs/poll.js new file mode 100644 index 000000000..316afdd71 --- /dev/null +++ b/packages/preview2-shim/nodejs/poll.js @@ -0,0 +1,3 @@ +export function pollOneoff(f) { + console.log(`[poll] Poll oneoff ${f}`); +} diff --git a/packages/preview2-shim/nodejs/random.js b/packages/preview2-shim/nodejs/random.js new file mode 100644 index 000000000..8f0a03f13 --- /dev/null +++ b/packages/preview2-shim/nodejs/random.js @@ -0,0 +1,5 @@ +import { randomBytes } from "node:crypto"; + +export function getRandomBytes(len) { + return randomBytes(len); +} diff --git a/packages/preview2-shim/nodejs/stderr.js b/packages/preview2-shim/nodejs/stderr.js new file mode 100644 index 000000000..7a3eb082c --- /dev/null +++ b/packages/preview2-shim/nodejs/stderr.js @@ -0,0 +1,3 @@ +export function print(message) { + process.stderr.write(`${message}\n`); +} diff --git a/packages/preview2-shim/package.json b/packages/preview2-shim/package.json new file mode 100644 index 000000000..ca43e21e5 --- /dev/null +++ b/packages/preview2-shim/package.json @@ -0,0 +1,23 @@ +{ + "name": "@bytecodealliance/preview2-shim", + "version": "0.0.1", + "description": "WASI Preview2 shim in JavaScript for the Browser and Node.js", + "author": "Guy Bedford, Eduardo Rodrigues<16357187+eduardomourar@users.noreply.github.com>", + "type": "module", + "exports": { + "node": "./nodejs/index.js", + "default": "./browser/index.js" + }, + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "browser/", + "http/", + "nodejs/", + "types/" + ], + "dependencies": { + "synckit": "^0.8.5" + } +} diff --git a/packages/preview2-shim/types/wasi-clocks.d.ts b/packages/preview2-shim/types/wasi-clocks.d.ts new file mode 100644 index 000000000..68aadaec5 --- /dev/null +++ b/packages/preview2-shim/types/wasi-clocks.d.ts @@ -0,0 +1,11 @@ +export type MonotonicClock = number; +export type Instant = bigint; +export type WallClock = number; +export interface Datetime { + seconds: bigint, + nanoseconds: number, +} +export namespace WasiClocks { + export function monotonicClockNow(clock: MonotonicClock): Instant; + export function wallClockNow(clock: WallClock): Datetime; +} diff --git a/packages/preview2-shim/types/wasi-default-clocks.d.ts b/packages/preview2-shim/types/wasi-default-clocks.d.ts new file mode 100644 index 000000000..17d83411e --- /dev/null +++ b/packages/preview2-shim/types/wasi-default-clocks.d.ts @@ -0,0 +1,6 @@ +export type MonotonicClock = MonotonicClock; +export type WallClock = WallClock; +export namespace WasiDefaultClocks { + export function defaultMonotonicClock(): MonotonicClock; + export function defaultWallClock(): WallClock; +} diff --git a/packages/preview2-shim/types/wasi-exit.d.ts b/packages/preview2-shim/types/wasi-exit.d.ts new file mode 100644 index 000000000..7c9a74ba7 --- /dev/null +++ b/packages/preview2-shim/types/wasi-exit.d.ts @@ -0,0 +1,3 @@ +export namespace WasiExit { + export function exit(status: { tag: 'err' | 'ok' }): void; +} diff --git a/packages/preview2-shim/types/wasi-filesystem.d.ts b/packages/preview2-shim/types/wasi-filesystem.d.ts new file mode 100644 index 000000000..681ec11e7 --- /dev/null +++ b/packages/preview2-shim/types/wasi-filesystem.d.ts @@ -0,0 +1,150 @@ +export type Descriptor = number; +export type Filesize = bigint; +export type InputStream = InputStream; +/** + * # Variants + * + * ## `"access"` + * + * ## `"again"` + * + * ## `"already"` + * + * ## `"badf"` + * + * ## `"busy"` + * + * ## `"deadlk"` + * + * ## `"dquot"` + * + * ## `"exist"` + * + * ## `"fbig"` + * + * ## `"ilseq"` + * + * ## `"inprogress"` + * + * ## `"intr"` + * + * ## `"inval"` + * + * ## `"io"` + * + * ## `"isdir"` + * + * ## `"loop"` + * + * ## `"mlink"` + * + * ## `"msgsize"` + * + * ## `"nametoolong"` + * + * ## `"nodev"` + * + * ## `"noent"` + * + * ## `"nolck"` + * + * ## `"nomem"` + * + * ## `"nospc"` + * + * ## `"nosys"` + * + * ## `"notdir"` + * + * ## `"notempty"` + * + * ## `"notrecoverable"` + * + * ## `"notsup"` + * + * ## `"notty"` + * + * ## `"nxio"` + * + * ## `"overflow"` + * + * ## `"perm"` + * + * ## `"pipe"` + * + * ## `"rofs"` + * + * ## `"spipe"` + * + * ## `"txtbsy"` + * + * ## `"xdev"` + */ +export type Errno = 'access' | 'again' | 'already' | 'badf' | 'busy' | 'deadlk' | 'dquot' | 'exist' | 'fbig' | 'ilseq' | 'inprogress' | 'intr' | 'inval' | 'io' | 'isdir' | 'loop' | 'mlink' | 'msgsize' | 'nametoolong' | 'nodev' | 'noent' | 'nolck' | 'nomem' | 'nospc' | 'nosys' | 'notdir' | 'notempty' | 'notrecoverable' | 'notsup' | 'notty' | 'nxio' | 'overflow' | 'perm' | 'pipe' | 'rofs' | 'spipe' | 'txtbsy' | 'xdev'; +export type OutputStream = OutputStream; +export type DirEntryStream = number; +export type Device = bigint; +export type Inode = bigint; +/** + * # Variants + * + * ## `"unknown"` + * + * ## `"block-device"` + * + * ## `"character-device"` + * + * ## `"directory"` + * + * ## `"fifo"` + * + * ## `"symbolic-link"` + * + * ## `"regular-file"` + * + * ## `"socket"` + */ +export type DescriptorType = 'unknown' | 'block-device' | 'character-device' | 'directory' | 'fifo' | 'symbolic-link' | 'regular-file' | 'socket'; +export type Linkcount = bigint; +export type Datetime = Datetime; +export interface DescriptorStat { + dev: Device, + ino: Inode, + type: DescriptorType, + nlink: Linkcount, + size: Filesize, + atim: Datetime, + mtim: Datetime, + ctim: Datetime, +} +export interface AtFlags { + symlinkFollow?: boolean, +} +export interface OFlags { + create?: boolean, + directory?: boolean, + excl?: boolean, + trunc?: boolean, +} +export interface DescriptorFlags { + read?: boolean, + write?: boolean, + dsync?: boolean, + nonblock?: boolean, + rsync?: boolean, + sync?: boolean, +} +export interface Mode { + readable?: boolean, + writeable?: boolean, + executable?: boolean, +} +export namespace WasiFilesystem { + export function readViaStream(fd: Descriptor, offset: Filesize): InputStream; + export function writeViaStream(fd: Descriptor, offset: Filesize): OutputStream; + export function appendViaStream(fd: Descriptor): OutputStream; + export function closeDirEntryStream(s: DirEntryStream): void; + export function stat(fd: Descriptor): DescriptorStat; + export function openAt(fd: Descriptor, atFlags: AtFlags, path: string, oFlags: OFlags, flags: DescriptorFlags, mode: Mode): Descriptor; + export function close(fd: Descriptor): void; +} diff --git a/packages/preview2-shim/types/wasi-http.d.ts b/packages/preview2-shim/types/wasi-http.d.ts new file mode 100644 index 000000000..80416d7dd --- /dev/null +++ b/packages/preview2-shim/types/wasi-http.d.ts @@ -0,0 +1,55 @@ +/** + * # Variants + * + * ## `"get"` + * + * ## `"post"` + * + * ## `"put"` + * + * ## `"delete"` + * + * ## `"patch"` + * + * ## `"head"` + * + * ## `"options"` + */ +export type Method = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options'; +export type Uri = string; +export type Headers = [string, string][]; +export type Params = [string, string][]; +export type Body = Uint8Array; +export interface Request { + method: Method, + uri: Uri, + headers: Headers, + params: Params, + body?: Body, +} +export type HttpStatus = number; +export interface Response { + status: HttpStatus, + headers?: Headers, + body?: Body, +} +export type HttpError = HttpErrorInvalidUrl | HttpErrorTimeoutError | HttpErrorProtocolError | HttpErrorUnexpectedError; +export interface HttpErrorInvalidUrl { + tag: 'invalid-url', + val: string, +} +export interface HttpErrorTimeoutError { + tag: 'timeout-error', + val: string, +} +export interface HttpErrorProtocolError { + tag: 'protocol-error', + val: string, +} +export interface HttpErrorUnexpectedError { + tag: 'unexpected-error', + val: string, +} +export namespace WasiHttp { + export function send(req: Request): Response; +} diff --git a/packages/preview2-shim/types/wasi-io.d.ts b/packages/preview2-shim/types/wasi-io.d.ts new file mode 100644 index 000000000..5cd70ae10 --- /dev/null +++ b/packages/preview2-shim/types/wasi-io.d.ts @@ -0,0 +1,10 @@ +export type InputStream = number; +export interface StreamError { +} +export type OutputStream = number; +export namespace WasiIo { + export function read(src: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; + export function write(dst: OutputStream, buf: Uint8Array): bigint; + export function dropInputStream(f: InputStream): void; + export function dropOutputStream(f: OutputStream): void; +} diff --git a/packages/preview2-shim/types/wasi-poll.d.ts b/packages/preview2-shim/types/wasi-poll.d.ts new file mode 100644 index 000000000..f349f1a35 --- /dev/null +++ b/packages/preview2-shim/types/wasi-poll.d.ts @@ -0,0 +1,4 @@ +export type Pollable = number; +export namespace WasiPoll { + export function pollOneoff(input: Pollable[]): Uint8Array; +} diff --git a/packages/preview2-shim/types/wasi-random.d.ts b/packages/preview2-shim/types/wasi-random.d.ts new file mode 100644 index 000000000..e9a642225 --- /dev/null +++ b/packages/preview2-shim/types/wasi-random.d.ts @@ -0,0 +1,3 @@ +export namespace WasiRandom { + export function getRandomBytes(len: number): Uint8Array | ArrayBuffer; +} diff --git a/packages/preview2-shim/types/wasi-stderr.d.ts b/packages/preview2-shim/types/wasi-stderr.d.ts new file mode 100644 index 000000000..a959b8ebd --- /dev/null +++ b/packages/preview2-shim/types/wasi-stderr.d.ts @@ -0,0 +1,3 @@ +export namespace WasiStderr { + export function print(message: string): void; +}