Skip to content

Commit

Permalink
feat: add the fetch and more APIs to the JavaScript kit. Create the s…
Browse files Browse the repository at this point in the history
…him using esbuild
  • Loading branch information
Angelmmiguel committed Jul 4, 2023
1 parent 1f85f38 commit 68f91f0
Show file tree
Hide file tree
Showing 18 changed files with 670 additions and 164 deletions.
72 changes: 67 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions examples/js-fetch/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Builds a reply to the given request
*/
const reply = async (request) => {
if (request.method != "GET") {
// Don't allow other methods.
// Here you can see how to return a custom status
return new Response("Method not allowed", {
status: 405
});
}

// Body response
let body;

try {
let res = await fetch('https://jsonplaceholder.typicode.com/posts/');
let json = await res.json();

// Build a new response.
// Add some basic sanitization
body = `<!DOCTYPE html>
<head>
<title>Wasm Workers Server</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
<style>
body { max-width: 1000px; }
main { margin: 5rem 0; }
h1, p { text-align: center; }
h1 { margin-bottom: 2rem; }
pre { font-size: .9rem; }
pre > code { padding: 2rem; }
p { margin-top: 2rem; }
</style>
</head>
<body>
<main>
<h1>Hello from Wasm Workers Server 👋</h1>
<p>Available articles:</p>
<ul>
${json.map(({ title }) => `<li>${title.replace("<", "&lt;").replace(">", "&gt;")}</li>`).join("")}
</ul>
<p>
This page was generated by a JavaScript file running in WebAssembly.
</p>
</main>
</body>`;
} catch (e) {
body = `There was an error with the request: ${e}`;
}

let response = new Response(body);

// Add a new header
response.headers.set("x-generated-by", "wasm-workers-server");

return response;
}

// Subscribe to the Fetch event
addEventListener("fetch", event => {
return event.respondWith(reply(event.request));
});
6 changes: 5 additions & 1 deletion kits/javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
!*.wasm
node_modules
dist
package-lock.json

!*.wasm
4 changes: 3 additions & 1 deletion kits/javascript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ edition = "2021"

[dependencies]
anyhow = "1.0"
javy = { version = "1.0.0", features = ["json"] }
javy = { version = "1.0.0", features = ["json"] }
# Use an old version until we add support for components.
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "cb871cfa1ee460b51eb1d144b175b9aab9c50aba" }
39 changes: 39 additions & 0 deletions kits/javascript/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{io::ErrorKind, process::Command};

// Build the client admin panel.
fn main() {
// First check if NPM is available in the system
match Command::new("npm").spawn() {
Ok(_) => {
Command::new("npm")
.current_dir("shims")
.arg("install")
.status()
.expect("failed to execute process");

Command::new("npm")
.current_dir("shims")
.args(["run", "build"])
.status()
.expect("failed to execute process");
}
Err(e) => {
if let ErrorKind::NotFound = e.kind() {
eprintln!("`npm` was not found in your system. Please, install NodeJS / NPM to build the admin panel.");
eprintln!("See: https://nodejs.dev/en/download/");
} else {
eprintln!(
"There was an error when building the admin panel with NodeJS / NPM: {e}"
);
}
}
}

// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=shims/*.js");
println!("cargo:rerun-if-changed=shims/package.json");
println!("cargo:rerun-if-changed=shims/types/*.js");
}
61 changes: 61 additions & 0 deletions kits/javascript/shims/bindings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

import { TextEncoder } from "@sinonjs/text-encoding";
import { Response } from "./types/response";

(function () {
const __wws_send_http_request = globalThis.__wws_send_http_request;
const __wws_console_log = globalThis.__wws_console_log;

globalThis.fetch = (uri, opts) => {
let optsWithDefault = {
method: "GET",
headers: {},
body: null,
...opts
};

if (optsWithDefault.body !== null && typeof optsWithDefault.body !== "string") {
try {
optsWithDefault.body = new TextEncoder().encode(optsWithDefault.body);
} catch (e) {
return Promise.reject(`There was an error encoding the body: ${e}. Use a String or encode it using TextEncoder.`)
}
}

let result = __wws_send_http_request(uri, optsWithDefault);

if (result.error === true) {
return Promise.reject(new Error(`[${result.type}] ${result.message}`));
} else {
let response = new Response(result.body, {
headers: result.headers,
status: result.status,
})

return Promise.resolve(response);
}
}

globalThis.console = {
error(msg) {
this.log(msg);
},
log(msg) {
__wws_console_log(msg);
},
info(msg) {
this.log(msg);
},
debug(msg) {
this.log(msg);
},
warn(msg) {
this.log(msg);
}
}

Reflect.deleteProperty(globalThis, "__wws_send_http_request");
Reflect.deleteProperty(globalThis, "__wws_console_log");
})();
69 changes: 69 additions & 0 deletions kits/javascript/shims/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

// Main entrypoint for the project.

// Import the different external polyfills
import URLSearchParams from "@ungap/url-search-params";
import { TextEncoder, TextDecoder } from "@sinonjs/text-encoding";

// Import all the project types
import "./bindings";
import { Request, Response, Headers, Cache } from "./types";

// Define the globals
globalThis.URLSearchParams = URLSearchParams;
globalThis.TextEncoder = TextEncoder;
globalThis.TextDecoder = TextDecoder;

// Main logic
let handlerFunction;

let addEventListener = (_eventName, handler) => {
// Store the callback globally
handlerFunction = handler;
};

const requestToHandler = input => {
const request = new Request(input);
const event = {
request,
response: {},
respondWith(res) {
this.response = res;
}
};

Cache.init(input.kv);

try {
handlerFunction(event);

// Always convert event.response to a Promise
Promise.resolve(
event.response
).then(res => {
// Set the result in the global value
result = {
data: res.body,
headers: res.headers.headers,
status: res.status,
kv: Cache.state
};
})
.catch((err) => {
error = `Couldn't process the response from the handler:\n${err}`;
});
} catch (err) {
error = `There was an error running the handler:\n${err}`;
}
};

// This is the entrypoint for the project.
entrypoint = requestToHandler;

// Set the result
result = {};

// Save errors
error = null
15 changes: 15 additions & 0 deletions kits/javascript/shims/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "shims",
"version": "1.0.0",
"private": "true",
"main": "index.js",
"scripts": {
"build": "esbuild --bundle ./index.js --tree-shaking=false --outdir=dist --platform=node"
},
"dependencies": {
"@sinonjs/text-encoding": "^0.7.2",
"@ungap/url-search-params": "^0.2.2",
"esbuild": "0.18.11",
"http-status": "^1.6.2"
}
}
Loading

0 comments on commit 68f91f0

Please sign in to comment.