Skip to content
Merged
47 changes: 47 additions & 0 deletions packages/preview2-shim/browser/clocks.js
Original file line number Diff line number Diff line change
@@ -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]);
}
7 changes: 7 additions & 0 deletions packages/preview2-shim/browser/default-clocks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function defaultMonotonicClock() {
return 0;
}

export function defaultWallClock() {
return 1;
}
21 changes: 21 additions & 0 deletions packages/preview2-shim/browser/exit.js
Original file line number Diff line number Diff line change
@@ -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();
}
47 changes: 47 additions & 0 deletions packages/preview2-shim/browser/filesystem.js
Original file line number Diff line number Diff line change
@@ -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`);
}
28 changes: 28 additions & 0 deletions packages/preview2-shim/browser/http.js
Original file line number Diff line number Diff line change
@@ -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);
}
}
21 changes: 21 additions & 0 deletions packages/preview2-shim/browser/index.js
Original file line number Diff line number Diff line change
@@ -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,
};
34 changes: 34 additions & 0 deletions packages/preview2-shim/browser/io.js
Original file line number Diff line number Diff line change
@@ -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}`);
}
12 changes: 12 additions & 0 deletions packages/preview2-shim/browser/logging.js
Original file line number Diff line number Diff line change
@@ -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`);
}
3 changes: 3 additions & 0 deletions packages/preview2-shim/browser/poll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function pollOneoff(f) {
console.log(`[poll] Poll oneoff ${f}`);
}
19 changes: 19 additions & 0 deletions packages/preview2-shim/browser/random.js
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions packages/preview2-shim/browser/stderr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function print(message) {
console.error(`${message}\n`);
}
11 changes: 11 additions & 0 deletions packages/preview2-shim/http/error.js
Original file line number Diff line number Diff line change
@@ -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,
};
}
}
29 changes: 29 additions & 0 deletions packages/preview2-shim/http/make-request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { runAsWorker } from "synckit";

/**
* @param {import("../types/wasi-http").Request} req
* @returns {Promise<string>}
*/
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);
9 changes: 9 additions & 0 deletions packages/preview2-shim/index.d.ts
Original file line number Diff line number Diff line change
@@ -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";
1 change: 1 addition & 0 deletions packages/preview2-shim/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./nodejs/index.js";
27 changes: 27 additions & 0 deletions packages/preview2-shim/nodejs/clocks.js
Original file line number Diff line number Diff line change
@@ -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");
}
7 changes: 7 additions & 0 deletions packages/preview2-shim/nodejs/default-clocks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function defaultMonotonicClock() {
return 0;
}

export function defaultWallClock() {
return 1;
}
4 changes: 4 additions & 0 deletions packages/preview2-shim/nodejs/exit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function exit(status) {
console.log(`[exit] Exit: ${JSON.stringify(status)}`);
process.exit(status.tag === 'err' ? 1 : 0);
}
Loading