😅
Heresy
What is it?
Heresy
noun, /ˈher.ə.si/
(the act of having) an opinion or belief that is the opposite of
or against what is the official or popular opinion,
or an action that shows that you have no respect for the official opinion.
Heresy is a pure Go runtime that lets you:
- Embed the runtime and run JavaScript as middleware for
http.Server
in either Express.js style, or Web WorkerFetchEvent
style;- The handler script can be reloaded on-the-fly!
- Run the runtime as a reverse proxy to some backend services, with the power of JavaScript as scripting language to intercept requests;
- Or spin up the runtime as a standalone server to run JavaScript application, with the power of Go.
What is it not?
- It is not a secure/isolated runtime to run untrusted user code;
- It is not a sandbox similar to
v8::Isolate
.
Examples
Express.js style
function httpHandler({ req, res, next }) {
if (req.path === "/") {
next()
} else {
res.status(403).send({error: 'access denied'})
}
}
registerExpressHandler(httpHandler)
FetchEvent
style
async function eventHandler(event) {
if (event.request.method === "POST") {
event.respondWith(new Response(event.request.body, {
headers: event.request.headers
}))
}
// to the next handler in http.Server
}
registerEventHandler(eventHandler)
With network access
async function httpHandler({ res, fetch }) {
const resp = await fetch("https://example.com/")
res.send(await resp.text())
}
registerExpressHandler(httpHandler, {
fetch: true
})
// ... similarly in FetchEvent
// async function eventHandler(event) {
// const { fetch } = event
// const resp = await fetch("https://example.com/")
// event.respondWith(resp)
// }
// registerEventHandler(eventHandler, {
// fetch: true
// })
Supported ECMAScript Features
The JavaScript runtime is provided by goja. Currently it supports most features up to ES2018, with the notable exceptions of:
- async iterator (
async function* foo()
andfor await...of
); SharedArrayBuffer
;- ES2015 modules (
import foo from 'bar'
, please use a bundler that outputs UMD or CJS).
The recommended transpile target is ES2017. However, if you run into problems, ES6 can be used as a fallback.
Runtime Features Matrix
Supported Features via Polyfill |
---|
URLSearchParams |
TextEncoder /TextDecoder (UTF-8 Only) |
Web Streams API (ReadableStream , etc), backed by io.Reader /io.Writer |
Fetch API (Headers , Request , Response ) |
Component | Status | req/request | resp/respondWith | next |
---|---|---|---|---|
Express.js | WIP | Partial implementations (see request_context_request.go ) |
Partial implementations (see request_context_response.go ) |
Works |
FetchEvent | Implemented* | Works | Works | Works |
Fetch API | Implemented |
*: Even though ECMAScript is single-threaded in nature, heresy runtime manages data access and IOs asynchronously. Therefore, once your event handler returns, it should not call any methods from FetchEvent
.
The following usage will result in a race and crash the runtime:
function eventHandler(evt) {
// ...
evt.respondWith(/* ... */)
setTimeout(() => {
evt.fetch(/* ... */)
}, 100)
// evt.fetch will be called after your handler returns!
}
Use .waitUntil
instead:
function eventHandler(evt) {
// ...
evt.respondWith(/* ... */)
evt.waitUntil((async () => {
// e.g. send request metrics
await evt.fetch(/* ... */)
await evt.fetch(/* ... */)
})())
}
The first rule still applies if you use .waitUntil
incorrectly. The following usage will also crash the runtime:
function eventHandler(evt) {
// ...
evt.respondWith(/* ... */)
setTimeout(() => {
evt.waitUntil((async () => {
await evt.fetch(/* ... */)
})())
}, 100)
// evt.waitUntil will be called after your handler returns!
}
Add license scan report and status
Your FOSSA integration was successful! Attached in this PR is a badge and license report to track scan status in your README.
Below are docs for integrating FOSSA license checks into your CI:
performance focus refactoring
ext/common/x: use custom object pool implementation to keep runtime objects long-lived, as VM objects allocations are serialized
ext/common/shared: lazy initialization of header properties ext/fetch: lazy initialization of response properties express: lazy initialization of all properties/methods event: lazy initialization of all properties/methods
ext/stream: create new ReadableStream instead of reusing as ReadableStream is not reusable in JavaScript
ext/promise: use async/await for resolver to improve VM allocations behavior under high concurrent requests
js: use BYOBReader when draining ReadableStream
cmd/example: move examples into separate folders
transpile: remove go-typescript due to GPL-3 incompatibility
README: updated to be more explicit about intended usage and supported ECMAScript features