mirror of
https://github.com/nlepage/go-wasm-http-server.git
synced 2026-01-12 10:09:12 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb62588b31 | ||
|
|
9ae9f3c443 | ||
|
|
9ff6ec615a | ||
|
|
ac1cfbfe8a | ||
|
|
f0a7bf8f2b | ||
|
|
d7055fb5ea | ||
|
|
b9921d7d43 | ||
|
|
2f3e39bf97 | ||
|
|
065e91545b | ||
|
|
9be33ecb4d | ||
|
|
6ddad6298b | ||
|
|
1f38d08145 | ||
|
|
5dc06a3dbb | ||
|
|
38c1814f66 | ||
|
|
268c971467 | ||
|
|
57a311369e | ||
|
|
5f2b342f3a | ||
|
|
8e787fbf29 | ||
|
|
ce6765e72a | ||
|
|
d12a255cff | ||
|
|
a16a847b26 | ||
|
|
3cf36c41e2 | ||
|
|
30a6ef67f9 | ||
|
|
669f82020d | ||
|
|
c94dcd965d | ||
|
|
2d786bdb14 | ||
|
|
c561826125 | ||
|
|
897626b7d1 | ||
|
|
c93d379f20 | ||
|
|
98257b470a | ||
|
|
b2bd8679fd | ||
|
|
770d49a106 | ||
|
|
e8555180f7 | ||
|
|
8abad8cb77 | ||
|
|
74cbaf89b5 |
37
.all-contributorsrc
Normal file
37
.all-contributorsrc
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"projectName": "go-wasm-http-server",
|
||||||
|
"projectOwner": "nlepage",
|
||||||
|
"repoType": "github",
|
||||||
|
"repoHost": "https://github.com",
|
||||||
|
"files": [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"imageSize": 100,
|
||||||
|
"commit": false,
|
||||||
|
"commitConvention": "gitmoji",
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"login": "jphastings",
|
||||||
|
"name": "JP Hastings-Edrei",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/42999?v=4",
|
||||||
|
"profile": "https://byjp.me/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"doc",
|
||||||
|
"example"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "EliCDavis",
|
||||||
|
"name": "Eli Davis",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/9094977?v=4",
|
||||||
|
"profile": "https://recolude.com/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contributorsPerLine": 7,
|
||||||
|
"linkToUsage": false
|
||||||
|
}
|
||||||
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
2
LICENSE
2
LICENSE
@@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2021 Nicolas Lepage
|
Copyright 2025 Nicolas Lepage
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
82
README.md
82
README.md
@@ -6,9 +6,6 @@
|
|||||||
<a href="https://github.com/nlepage/go-wasm-http-server/blob/master/LICENSE" target="_blank">
|
<a href="https://github.com/nlepage/go-wasm-http-server/blob/master/LICENSE" target="_blank">
|
||||||
<img alt="License: Apache 2.0" src="https://img.shields.io/badge/License-Apache--2.0-yellow.svg" />
|
<img alt="License: Apache 2.0" src="https://img.shields.io/badge/License-Apache--2.0-yellow.svg" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://twitter.com/njblepage" target="_blank">
|
|
||||||
<img alt="Twitter: njblepage" src="https://img.shields.io/twitter/follow/njblepage.svg?style=social" />
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> Embed your Go HTTP handlers in a ServiceWorker (using [WebAssembly](https://mdn.io/WebAssembly/)) and emulate an HTTP server!
|
> Embed your Go HTTP handlers in a ServiceWorker (using [WebAssembly](https://mdn.io/WebAssembly/)) and emulate an HTTP server!
|
||||||
@@ -18,13 +15,18 @@
|
|||||||
- [Hello example](https://nlepage.github.io/go-wasm-http-server/hello) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello))
|
- [Hello example](https://nlepage.github.io/go-wasm-http-server/hello) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello))
|
||||||
- [Hello example with state](https://nlepage.github.io/go-wasm-http-server/hello-state) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state))
|
- [Hello example with state](https://nlepage.github.io/go-wasm-http-server/hello-state) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state))
|
||||||
- [Hello example with state and keepalive](https://nlepage.github.io/go-wasm-http-server/hello-state-keepalive) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state-keepalive))
|
- [Hello example with state and keepalive](https://nlepage.github.io/go-wasm-http-server/hello-state-keepalive) ([sources](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state-keepalive))
|
||||||
|
- [Hello example with Server Sent Events](https://nlepage.github.io/go-wasm-http-server/hello-sse/) ([sources](https://nlepage.github.io/go-wasm-http-server/hello-sse/))
|
||||||
- [😺 Catption generator example](https://nlepage.github.io/catption/wasm) ([sources](https://github.com/nlepage/catption/tree/wasm))
|
- [😺 Catption generator example](https://nlepage.github.io/catption/wasm) ([sources](https://github.com/nlepage/catption/tree/wasm))
|
||||||
- [Random password generator web server](https://nlepage.github.io/random-password-please/) ([sources](https://github.com/nlepage/random-password-please) forked from [jbarham/random-password-please](https://github.com/jbarham/random-password-please))
|
- [Random password generator web server](https://nlepage.github.io/random-password-please/) ([sources](https://github.com/nlepage/random-password-please) forked from [jbarham/random-password-please](https://github.com/jbarham/random-password-please))
|
||||||
|
- [Server fallbacks, and compiling with TinyGo](https://nlepage.github.io/go-wasm-http-server/tinygo/) (runs locally; see [sources & readme](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/tinygo#readme) for how to run this example)
|
||||||
|
|
||||||
## How?
|
## How?
|
||||||
|
|
||||||
Talk given at the Go devroom of FOSDEM 2021 explaining how `go-wasm-http-server` works:
|
Below is a talk given at the Go devroom of FOSDEM 2021 explaining how `go-wasm-http-server` works.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> `go-wasm-http-server` has suffered major changes since this talk, be aware that it is not accurate anymore on several aspects.
|
||||||
|
> Please refer to the documentation below for up to date usage of `go-wasm-http-server`.
|
||||||
|
|
||||||
[](https://youtu.be/O2RB_8ircdE)
|
[](https://youtu.be/O2RB_8ircdE)
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ The slides are available [here](https://nlepage.github.io/go-wasm-http-talk/).
|
|||||||
`go-wasm-http-server` requires you to build your Go application to WebAssembly, so you need to make sure your code is compatible:
|
`go-wasm-http-server` requires you to build your Go application to WebAssembly, so you need to make sure your code is compatible:
|
||||||
- no C bindings
|
- no C bindings
|
||||||
- no System dependencies such as file system or network (database server for example)
|
- no System dependencies such as file system or network (database server for example)
|
||||||
|
- For smaller WASM blobs, your code may also benefit from being compatible with, and compiled by, [TinyGo](https://tinygo.org/docs/reference/lang-support/stdlib/). See the TinyGo specific details below.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -48,7 +51,7 @@ In your Go code, replace [`http.ListenAndServe()`](https://pkg.go.dev/net/http#L
|
|||||||
|
|
||||||
📄 `server.go`
|
📄 `server.go`
|
||||||
```go
|
```go
|
||||||
// +build !js,!wasm
|
//go:build !js && !wasm
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -67,7 +70,7 @@ becomes:
|
|||||||
|
|
||||||
📄 `server_js_wasm.go`
|
📄 `server_js_wasm.go`
|
||||||
```go
|
```go
|
||||||
// +build js,wasm
|
//go:build js && wasm
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -87,17 +90,37 @@ You may want to use build tags as shown above (or file name suffixes) in order t
|
|||||||
Then build your WebAssembly binary:
|
Then build your WebAssembly binary:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
# To compile with Go
|
||||||
GOOS=js GOARCH=wasm go build -o server.wasm .
|
GOOS=js GOARCH=wasm go build -o server.wasm .
|
||||||
|
|
||||||
|
# To compile with TinyGo, if your code is compatible
|
||||||
|
GOOS=js GOARCH=wasm tinygo build -o server.wasm .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2: Create ServiceWorker file
|
### Step 2: Create ServiceWorker file
|
||||||
|
|
||||||
|
First, check the version of Go/TinyGo you compiled your wasm with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go version
|
||||||
|
go version go1.23.4 darwin/arm64
|
||||||
|
# ^------^
|
||||||
|
|
||||||
|
$ tinygo version
|
||||||
|
tinygo version 0.35.0 darwin/arm64 (using go version go1.23.4 and LLVM version 18.1.2)
|
||||||
|
# ^----^
|
||||||
|
```
|
||||||
|
|
||||||
Create a ServiceWorker file with the following code:
|
Create a ServiceWorker file with the following code:
|
||||||
|
|
||||||
📄 `sw.js`
|
📄 `sw.js`
|
||||||
```js
|
```js
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.18.4/misc/wasm/wasm_exec.js')
|
// Note the 'go.1.23.4' below, that matches the version you just found:
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.0.0/sw.js')
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.4/misc/wasm/wasm_exec.js')
|
||||||
|
// If you compiled with TinyGo then, similarly, use:
|
||||||
|
importScripts('https://cdn.jsdelivr.net/gh/tinygo-org/tinygo@0.35.0/targets/wasm_exec.js')
|
||||||
|
|
||||||
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.2.1/sw.js')
|
||||||
|
|
||||||
registerWasmHTTPListener('path/to/server.wasm')
|
registerWasmHTTPListener('path/to/server.wasm')
|
||||||
```
|
```
|
||||||
@@ -163,15 +186,44 @@ URL string of the WebAssembly module, example: `"path/to/my-module.wasm"`.
|
|||||||
An optional object containing:
|
An optional object containing:
|
||||||
|
|
||||||
- `base` (`string`): Base path of the server, relative to the ServiceWorker's scope.
|
- `base` (`string`): Base path of the server, relative to the ServiceWorker's scope.
|
||||||
|
- `cacheName` (`string`): Name of the [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to store the WebAssembly binary.
|
||||||
- `args` (`string[]`): Arguments for the WebAssembly module.
|
- `args` (`string[]`): Arguments for the WebAssembly module.
|
||||||
|
- `passthrough` (`(request: Request): boolean`): Optional callback to allow passing the request through to network.
|
||||||
|
|
||||||
## Author
|
## <abbr title="Frequently Asked Questions">FAQ</abbr> ❓
|
||||||
|
|
||||||
👤 **Nicolas Lepage**
|
### Are WebSockets supported?
|
||||||
|
|
||||||
* Website: https://nicolas.lepage.dev/
|
No, WebSockets aren’t and won’t be supported, because Service Workers cannot intercept websocket connections.
|
||||||
* Twitter: [@njblepage](https://twitter.com/njblepage)
|
|
||||||
* Github: [@nlepage](https://github.com/nlepage)
|
However [Server Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events), which is an alternative to WebSockets, are supported, you can find the code for an example [here](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-sse) and the demo [here](https://nlepage.github.io/go-wasm-http-server/hello-sse/).
|
||||||
|
|
||||||
|
### Is it compatible with TinyGo?
|
||||||
|
|
||||||
|
Yes, an example and some specific information is available [here](https://github.com/nlepage/go-wasm-http-server/tree/master/docs/tinygo).
|
||||||
|
|
||||||
|
## Contributors ✨
|
||||||
|
|
||||||
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://byjp.me/"><img src="https://avatars.githubusercontent.com/u/42999?v=4?s=100" width="100px;" alt="JP Hastings-Edrei"/><br /><sub><b>JP Hastings-Edrei</b></sub></a><br /><a href="https://github.com/nlepage/go-wasm-http-server/commits?author=jphastings" title="Code">💻</a> <a href="https://github.com/nlepage/go-wasm-http-server/commits?author=jphastings" title="Documentation">📖</a> <a href="#example-jphastings" title="Examples">💡</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://recolude.com/"><img src="https://avatars.githubusercontent.com/u/9094977?v=4?s=100" width="100px;" alt="Eli Davis"/><br /><sub><b>Eli Davis</b></sub></a><br /><a href="https://github.com/nlepage/go-wasm-http-server/commits?author=EliCDavis" title="Code">💻</a> <a href="https://github.com/nlepage/go-wasm-http-server/issues?q=author%3AEliCDavis" title="Bug reports">🐛</a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
@@ -183,7 +235,7 @@ Give a ⭐️ if this project helped you!
|
|||||||
|
|
||||||
## 📝 License
|
## 📝 License
|
||||||
|
|
||||||
Copyright © 2021 [Nicolas Lepage](https://github.com/nlepage).<br />
|
Copyright © 2025 [Nicolas Lepage](https://github.com/nlepage).<br />
|
||||||
This project is [Apache 2.0](https://github.com/nlepage/go-wasm-http-server/blob/master/LICENSE) licensed.
|
This project is [Apache 2.0](https://github.com/nlepage/go-wasm-http-server/blob/master/LICENSE) licensed.
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|||||||
27
docs/hello-multiple/api.go
Normal file
27
docs/hello-multiple/api.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
wasmhttp "github.com/nlepage/go-wasm-http-server/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var binaryName = ""
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) {
|
||||||
|
res.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(res).Encode(map[string]string{
|
||||||
|
"message": fmt.Sprintf("Hello from %s at path %s", binaryName, os.Getenv("WASM_HTTP_PATH")),
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
wasmhttp.Serve(nil)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
BIN
docs/hello-multiple/api1.wasm
Executable file
BIN
docs/hello-multiple/api1.wasm
Executable file
Binary file not shown.
BIN
docs/hello-multiple/api2.wasm
Executable file
BIN
docs/hello-multiple/api2.wasm
Executable file
Binary file not shown.
3
docs/hello-multiple/build.sh
Executable file
3
docs/hello-multiple/build.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
GOOS=js GOARCH=wasm go build -o api1.wasm --ldflags="-X 'main.binaryName=api1.wasm'" .
|
||||||
|
GOOS=js GOARCH=wasm go build -o api2.wasm --ldflags="-X 'main.binaryName=api2.wasm'" .
|
||||||
29
docs/hello-multiple/index.html
Normal file
29
docs/hello-multiple/index.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>go-wasm-http-server hello demo</title>
|
||||||
|
<script>
|
||||||
|
navigator.serviceWorker.register('sw.js');
|
||||||
|
|
||||||
|
async function hello() {
|
||||||
|
const api = document.querySelector('#api').value;
|
||||||
|
|
||||||
|
const res = await fetch(`${api}/hello`);
|
||||||
|
|
||||||
|
const { message } = await res.json();
|
||||||
|
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<label for="api">API: </label
|
||||||
|
><select id="api">
|
||||||
|
<option value="api1/1">api1.wasm first instance</option>
|
||||||
|
<option value="api1/2">api1.wasm second instance</option>
|
||||||
|
<option value="api2/1">api2.wasm first instance</option>
|
||||||
|
<option value="api2/2">api2.wasm second instance</option>
|
||||||
|
</select>
|
||||||
|
<button onclick="hello()">Hello</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
15
docs/hello-multiple/sw.js
Normal file
15
docs/hello-multiple/sw.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.25.1/lib/wasm/wasm_exec.js');
|
||||||
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@master/sw.js');
|
||||||
|
|
||||||
|
addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(caches.open('examples').then((cache) => cache.addAll(['api1.wasm', 'api2.wasm'])));
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
registerWasmHTTPListener('api1.wasm', { base: 'api1/1/' });
|
||||||
|
registerWasmHTTPListener('api1.wasm', { base: 'api1/2/' });
|
||||||
|
registerWasmHTTPListener('api2.wasm', { base: 'api2/1/' });
|
||||||
|
registerWasmHTTPListener('api2.wasm', { base: 'api2/2/' });
|
||||||
Binary file not shown.
@@ -1,32 +1,56 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>go-wasm-http-server hello demo</title>
|
<title>go-wasm-http-server hello sse demo</title>
|
||||||
<script>
|
<script>
|
||||||
navigator.serviceWorker.register('sw.js')
|
navigator.serviceWorker.register('sw.js').then((registration) => {
|
||||||
.then(registration => {
|
const serviceWorker = registration.installing ?? registration.waiting ?? registration.active;
|
||||||
const serviceWorker = registration.installing ?? registration.waiting ?? registration.active
|
if (serviceWorker.state === 'activated') {
|
||||||
if (serviceWorker.state === 'activated') {
|
document.querySelector('#open-button').disabled = false;
|
||||||
startEventSource()
|
} else {
|
||||||
} else {
|
serviceWorker.addEventListener('statechange', (e) => {
|
||||||
serviceWorker.addEventListener('statechange', e => {
|
if (e.target.state === 'activated') {
|
||||||
if (e.target.state === 'activated') startEventSource()
|
document.querySelector('#open-button').disabled = false;
|
||||||
})
|
}
|
||||||
}
|
});
|
||||||
})
|
}
|
||||||
|
});
|
||||||
function startEventSource() {
|
|
||||||
const es = new EventSource('/api/events')
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
es.addEventListener('ping', (e) => {
|
let es;
|
||||||
const p = document.createElement('p')
|
|
||||||
p.textContent = `ping: data=${e.data}`
|
document.querySelector('#open-button').addEventListener('click', () => {
|
||||||
document.body.append(p)
|
if (es && es.readyState === es.OPEN) return;
|
||||||
})
|
es = new EventSource('api/events');
|
||||||
|
es.addEventListener('ping', (e) => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = `ping: data=${e.data}`;
|
||||||
|
document.querySelector('ul').append(li);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#close-button').addEventListener('click', () => {
|
||||||
|
if (!es) return;
|
||||||
|
es.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector('#clear-button').addEventListener('click', () => {
|
||||||
|
document.querySelector('ul').innerHTML = '';
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('unload', () => {
|
window.addEventListener('unload', () => {
|
||||||
es.close()
|
if (!es) return;
|
||||||
})
|
es.close();
|
||||||
}
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body>
|
||||||
|
<p>
|
||||||
|
<button id="open-button" disabled>Open event source</button>
|
||||||
|
<button id="close-button">Close event source</button>
|
||||||
|
<button id="clear-button">Clear events</button>
|
||||||
|
</p>
|
||||||
|
<ul></ul>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.1/misc/wasm/wasm_exec.js')
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.25.1/lib/wasm/wasm_exec.js');
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.0.0/sw.js')
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@master/sw.js');
|
||||||
|
|
||||||
|
const wasm = 'api.wasm';
|
||||||
|
|
||||||
addEventListener('install', (event) => {
|
addEventListener('install', (event) => {
|
||||||
event.waitUntil(skipWaiting())
|
event.waitUntil(caches.open('examples').then((cache) => cache.add(wasm)));
|
||||||
})
|
});
|
||||||
|
|
||||||
addEventListener('activate', event => {
|
addEventListener('activate', (event) => {
|
||||||
event.waitUntil(clients.claim())
|
event.waitUntil(clients.claim());
|
||||||
})
|
});
|
||||||
|
|
||||||
registerWasmHTTPListener('api.wasm', { base: 'api' })
|
registerWasmHTTPListener(wasm, { base: 'api' });
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>go-wasm-http-server hello with state demo</title>
|
<title>go-wasm-http-server hello with state and keepalive demo</title>
|
||||||
<script>
|
<script>
|
||||||
navigator.serviceWorker.register('sw.js').then(registration => {
|
navigator.serviceWorker.register('sw.js').then((registration) => {
|
||||||
const sw = registration.active ?? registration.installing ?? registration.waiting
|
const sw = registration.active ?? registration.installing ?? registration.waiting;
|
||||||
setInterval(() => sw.postMessage(null), 15000)
|
setInterval(() => sw.postMessage(null), 15000);
|
||||||
})
|
});
|
||||||
|
|
||||||
async function hello() {
|
async function hello() {
|
||||||
const name = document.querySelector("#name").value
|
const name = document.querySelector('#name').value;
|
||||||
|
|
||||||
const res = await fetch('api/hello', {
|
const res = await fetch('api/hello', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ name })
|
body: JSON.stringify({ name }),
|
||||||
})
|
});
|
||||||
|
|
||||||
const { message } = await res.json()
|
const { message } = await res.json();
|
||||||
|
|
||||||
alert(message)
|
alert(message);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<label for="name">Name: </label><input id="name" value="World">
|
<label for="name">Name: </label><input id="name" value="World" />
|
||||||
<button onclick="hello()">Hello</button>
|
<button onclick="hello()">Hello</button>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.1/misc/wasm/wasm_exec.js')
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.25.1/lib/wasm/wasm_exec.js');
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.0.0/sw.js')
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@master/sw.js');
|
||||||
|
|
||||||
addEventListener('install', event => {
|
const wasm = '../hello-state/api.wasm';
|
||||||
event.waitUntil(skipWaiting())
|
|
||||||
})
|
|
||||||
|
|
||||||
addEventListener('activate', event => {
|
|
||||||
event.waitUntil(clients.claim())
|
|
||||||
})
|
|
||||||
|
|
||||||
addEventListener('message', () => {})
|
addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(caches.open('examples').then((cache) => cache.add(wasm)));
|
||||||
|
});
|
||||||
|
|
||||||
registerWasmHTTPListener('../hello-state/api.wasm', { base: 'api' })
|
addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener('message', () => {});
|
||||||
|
|
||||||
|
registerWasmHTTPListener(wasm, { base: 'api' });
|
||||||
|
|||||||
Binary file not shown.
@@ -3,27 +3,27 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>go-wasm-http-server hello with state demo</title>
|
<title>go-wasm-http-server hello with state demo</title>
|
||||||
<script>
|
<script>
|
||||||
navigator.serviceWorker.register('sw.js')
|
navigator.serviceWorker.register('sw.js');
|
||||||
|
|
||||||
async function hello() {
|
async function hello() {
|
||||||
const name = document.querySelector("#name").value
|
const name = document.querySelector('#name').value;
|
||||||
|
|
||||||
const res = await fetch('api/hello', {
|
const res = await fetch('api/hello', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ name })
|
body: JSON.stringify({ name }),
|
||||||
})
|
});
|
||||||
|
|
||||||
const { message } = await res.json()
|
const { message } = await res.json();
|
||||||
|
|
||||||
alert(message)
|
alert(message);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<label for="name">Name: </label><input id="name" value="World">
|
<label for="name">Name: </label><input id="name" value="World" />
|
||||||
<button onclick="hello()">Hello</button>
|
<button onclick="hello()">Hello</button>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.1/misc/wasm/wasm_exec.js')
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.25.1/lib/wasm/wasm_exec.js');
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.0.0/sw.js')
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@master/sw.js');
|
||||||
|
|
||||||
|
const wasm = 'api.wasm';
|
||||||
|
|
||||||
addEventListener('install', (event) => {
|
addEventListener('install', (event) => {
|
||||||
event.waitUntil(skipWaiting())
|
event.waitUntil(caches.open('examples').then((cache) => cache.add(wasm)));
|
||||||
})
|
});
|
||||||
|
|
||||||
addEventListener('activate', event => {
|
|
||||||
event.waitUntil(clients.claim())
|
|
||||||
})
|
|
||||||
|
|
||||||
registerWasmHTTPListener('api.wasm', { base: 'api' })
|
addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
registerWasmHTTPListener(wasm, { base: 'api' });
|
||||||
|
|||||||
Binary file not shown.
@@ -3,27 +3,27 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>go-wasm-http-server hello demo</title>
|
<title>go-wasm-http-server hello demo</title>
|
||||||
<script>
|
<script>
|
||||||
navigator.serviceWorker.register('sw.js')
|
navigator.serviceWorker.register('sw.js');
|
||||||
|
|
||||||
async function hello() {
|
async function hello() {
|
||||||
const name = document.querySelector("#name").value
|
const name = document.querySelector('#name').value;
|
||||||
|
|
||||||
const res = await fetch('api/hello', {
|
const res = await fetch('api/hello', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ name })
|
body: JSON.stringify({ name }),
|
||||||
})
|
});
|
||||||
|
|
||||||
const { message } = await res.json()
|
const { message } = await res.json();
|
||||||
|
|
||||||
alert(message)
|
alert(message);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<label for="name">Name: </label><input id="name" value="World">
|
<label for="name">Name: </label><input id="name" value="World" />
|
||||||
<button onclick="hello()">Hello</button>
|
<button onclick="hello()">Hello</button>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.23.1/misc/wasm/wasm_exec.js')
|
importScripts('https://cdn.jsdelivr.net/gh/golang/go@go1.25.1/lib/wasm/wasm_exec.js');
|
||||||
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.0.0/sw.js')
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@master/sw.js');
|
||||||
|
|
||||||
|
const wasm = 'api.wasm';
|
||||||
|
|
||||||
addEventListener('install', (event) => {
|
addEventListener('install', (event) => {
|
||||||
event.waitUntil(skipWaiting())
|
event.waitUntil(caches.open('examples').then((cache) => cache.add(wasm)));
|
||||||
})
|
});
|
||||||
|
|
||||||
addEventListener('activate', event => {
|
addEventListener('activate', (event) => {
|
||||||
event.waitUntil(clients.claim())
|
event.waitUntil(clients.claim());
|
||||||
})
|
});
|
||||||
|
|
||||||
registerWasmHTTPListener('api.wasm', { base: 'api' })
|
registerWasmHTTPListener(wasm, { base: 'api' });
|
||||||
|
|||||||
45
docs/index.html
Normal file
45
docs/index.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>go-wasm-http-server examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>go-wasm-http-server examples</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="hello/">Hello example</a> (<a
|
||||||
|
href="https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello"
|
||||||
|
>Sources</a
|
||||||
|
>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="hello-multiple/">Hello example with multiple wasm instances</a> (<a
|
||||||
|
href="https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-multiple"
|
||||||
|
>Sources</a
|
||||||
|
>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="hello-state/">Hello example with state</a> (<a
|
||||||
|
href="https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state"
|
||||||
|
>Sources</a
|
||||||
|
>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="hello-state-keepalive/">Hello example with state and keepalive</a> (<a
|
||||||
|
href="https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-state-keepalive"
|
||||||
|
>Sources</a
|
||||||
|
>)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="hello-sse/">Hello example with Server Sent Events</a> (<a
|
||||||
|
href="https://github.com/nlepage/go-wasm-http-server/tree/master/docs/hello-sse"
|
||||||
|
>Sources</a
|
||||||
|
>)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
See <a href="https://github.com/nlepage/go-wasm-http-server?tab=readme-ov-file#readme">README</a> for more
|
||||||
|
information.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
42
docs/tinygo/README.md
Normal file
42
docs/tinygo/README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Compiling with TinyGo
|
||||||
|
|
||||||
|
This example demonstrates that go-wasm-http-server can also be compiled with [TinyGo](https://www.tinygo.org), producing significantly smaller WASM blobs, though at the expense of [at least one known bug](https://github.com/tinygo-org/tinygo/issues/1140) and a [reduced standard library](https://tinygo.org/docs/reference/lang-support/stdlib/).
|
||||||
|
|
||||||
|
This example also demonstrates how the same code can be used for both server-side execution, and client-side execution in WASM (providing support for clients that cannot interpret WASM).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
You'll need a version of [TinyGo installed](https://tinygo.org/getting-started/install/). (eg. `brew install tinygo-org/tools/tinygo`)
|
||||||
|
|
||||||
|
You'll need to make sure the first line of `sw.js` here has the same tinygo version number as your TinyGo version (this was v0.35.0 at time of writing).
|
||||||
|
|
||||||
|
## Build & run
|
||||||
|
|
||||||
|
Compile the WASM blob with TinyGo (this has been done for you for this example):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GOOS=js GOARCH=wasm tinygo build -o api.wasm .
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the server (with Go, not TinyGo):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go run .
|
||||||
|
Server starting on http://127.0.0.1:<port>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important notes
|
||||||
|
|
||||||
|
You **must** use the TinyGo `wasm_exec.js`, specific to the version of TinyGo used to compile the WASM, in your `sw.js`. For example, if using the JSDelivr CDN:
|
||||||
|
|
||||||
|
```js
|
||||||
|
importScripts('https://cdn.jsdelivr.net/gh/tinygo-org/tinygo@0.35.0/targets/wasm_exec.js')
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `0.35.0` within the path matches the TinyGo version used:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ tinygo version
|
||||||
|
tinygo version 0.35.0 darwin/arm64 (using go version go1.23.4 and LLVM version 18.1.2)
|
||||||
|
# ^----^
|
||||||
|
```
|
||||||
BIN
docs/tinygo/api.wasm
Normal file
BIN
docs/tinygo/api.wasm
Normal file
Binary file not shown.
19
docs/tinygo/handlers.go
Normal file
19
docs/tinygo/handlers.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func goRuntimeHandler(res http.ResponseWriter, req *http.Request) {
|
||||||
|
res.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(res).Encode(map[string]string{
|
||||||
|
"os": runtime.GOOS,
|
||||||
|
"arch": runtime.GOARCH,
|
||||||
|
"compiler": runtime.Compiler,
|
||||||
|
"version": runtime.Version(),
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
44
docs/tinygo/index.html
Normal file
44
docs/tinygo/index.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>go-wasm-http-server tinygo demo</title>
|
||||||
|
<script>
|
||||||
|
const sw = navigator.serviceWorker
|
||||||
|
|
||||||
|
function attachServiceWorker() {
|
||||||
|
sw.register('sw.js')
|
||||||
|
.then(() => {
|
||||||
|
document.getElementById('wasm-status').innerText = "⚡️ Loaded - Will execute WASM locally"
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
document.getElementById('wasm-status').innerText = "🛑 Error loading service worker — Check console"
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeQuery() {
|
||||||
|
const res = await fetch('api/tiny', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.getElementById('output').innerText = await res.text()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>This example demonstrates that go-wasm-http-server can be compiled with <a href="https://www.tinygo.org">TinyGo</em>, producing significantly smaller WASM blobs, at the expense of <a href="https://github.com/tinygo-org/tinygo/issues/1140">at least one known bug</a>, and a <a href="https://tinygo.org/docs/reference/lang-support/stdlib/">reduced standard library</a>.</p>
|
||||||
|
<dl><dt>WASM HTTP Service Worker:</dt><dd id="wasm-status">☁️ Not loaded — will call server</dd></dl>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li><button onclick="makeQuery()">Call API</button></li>
|
||||||
|
<li><button onclick="attachServiceWorker()">Attach the service worker</button></li>
|
||||||
|
<li><span>Call the API again (Step 1)</span></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>Response:</h3>
|
||||||
|
<pre id="output"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
docs/tinygo/server.go
Normal file
34
docs/tinygo/server.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//go:build !wasm
|
||||||
|
// +build !wasm
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed *.html *.js *.wasm
|
||||||
|
var thisDir embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Serve all files in this directory statically
|
||||||
|
http.Handle("/", http.FileServer(http.FS(thisDir)))
|
||||||
|
|
||||||
|
// Note that this needs to be mounted at /api/tiny, rather than just /tiny (like in wasm.go)
|
||||||
|
// because the service worker mounts the WASM server at /api (at the end of sw.js)
|
||||||
|
http.HandleFunc("/api/tiny", goRuntimeHandler)
|
||||||
|
|
||||||
|
// Pick any available port. Note that ServiceWorkers _require_ localhost for non-SSL serving (so other LAN/WAN IPs will prevent the service worker from loading)
|
||||||
|
listener, err := net.Listen("tcp", ":0")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to claim a port to start server on: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share the port being used & start
|
||||||
|
fmt.Printf("Server starting on http://127.0.0.1:%d\n", listener.Addr().(*net.TCPAddr).Port)
|
||||||
|
panic(http.Serve(listener, nil))
|
||||||
|
}
|
||||||
14
docs/tinygo/sw.js
Normal file
14
docs/tinygo/sw.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
importScripts('https://cdn.jsdelivr.net/gh/tinygo-org/tinygo@0.35.0/targets/wasm_exec.js')
|
||||||
|
importScripts('https://cdn.jsdelivr.net/gh/nlepage/go-wasm-http-server@v2.2.1/sw.js')
|
||||||
|
|
||||||
|
const wasm = 'api.wasm'
|
||||||
|
|
||||||
|
addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(caches.open('examples').then((cache) => cache.add(wasm)))
|
||||||
|
})
|
||||||
|
|
||||||
|
addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(clients.claim())
|
||||||
|
})
|
||||||
|
|
||||||
|
registerWasmHTTPListener(wasm, { base: 'api' })
|
||||||
18
docs/tinygo/wasm.go
Normal file
18
docs/tinygo/wasm.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build wasm
|
||||||
|
// +build wasm
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
wasmhttp "github.com/nlepage/go-wasm-http-server/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/tiny", goRuntimeHandler)
|
||||||
|
|
||||||
|
wasmhttp.Serve(nil)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
wasmhttp "github.com/nlepage/go-wasm-http-server/v2/v2"
|
wasmhttp "github.com/nlepage/go-wasm-http-server/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Demonstrates a simple hello JSON service.
|
// Demonstrates a simple hello JSON service.
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -1,9 +1,9 @@
|
|||||||
module github.com/nlepage/go-wasm-http-server/v2
|
module github.com/nlepage/go-wasm-http-server/v2
|
||||||
|
|
||||||
go 1.18
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hack-pad/safejs v0.1.1
|
github.com/hack-pad/safejs v0.1.1
|
||||||
github.com/nlepage/go-js-promise v1.0.0
|
github.com/nlepage/go-js-promise v1.1.0
|
||||||
github.com/tmaxmax/go-sse v0.8.0
|
github.com/tmaxmax/go-sse v0.11.0
|
||||||
)
|
)
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -1,6 +1,6 @@
|
|||||||
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
||||||
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||||
github.com/nlepage/go-js-promise v1.0.0 h1:K7OmJ3+0BgWJ2LfXchg2sI6RDr7AW/KWR8182epFwGQ=
|
github.com/nlepage/go-js-promise v1.1.0 h1:BfvywsIMo4cpNOKyoReBWkxEW8f9HMwXqGc45wEKPRs=
|
||||||
github.com/nlepage/go-js-promise v1.0.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo=
|
github.com/nlepage/go-js-promise v1.1.0/go.mod h1:bdOP0wObXu34euibyK39K1hoBCtlgTKXGc56AGflaRo=
|
||||||
github.com/tmaxmax/go-sse v0.8.0 h1:pPpTgyyi1r7vG2o6icebnpGEh3ebcnBXqDWkb7aTofs=
|
github.com/tmaxmax/go-sse v0.11.0 h1:nogmJM6rJUoOLoAwEKeQe5XlVpt9l7N82SS1jI7lWFg=
|
||||||
github.com/tmaxmax/go-sse v0.8.0/go.mod h1:HLoxqxdH+7oSUItjtnpxjzJedfr/+Rrm/dNWBcTxJFM=
|
github.com/tmaxmax/go-sse v0.11.0/go.mod h1:u/2kZQR1tyngo1lKaNCj1mJmhXGZWS1Zs5yiSOD+Eg8=
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type Reader struct {
|
|||||||
off int
|
off int
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ io.Reader = (*Reader)(nil)
|
var _ io.ReadCloser = (*Reader)(nil)
|
||||||
|
|
||||||
func NewReader(r safejs.Value) *Reader {
|
func NewReader(r safejs.Value) *Reader {
|
||||||
return &Reader{
|
return &Reader{
|
||||||
@@ -83,3 +83,14 @@ func (r *Reader) Read(p []byte) (int, error) {
|
|||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Close() error {
|
||||||
|
p, err := r.value.Call("cancel")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = promise.Await(safejs.Unsafe(p))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,11 +12,14 @@ type Writer struct {
|
|||||||
Value safejs.Value
|
Value safejs.Value
|
||||||
controller safejs.Value
|
controller safejs.Value
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
cancelled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ io.WriteCloser = (*Writer)(nil)
|
var _ io.WriteCloser = (*Writer)(nil)
|
||||||
|
|
||||||
func NewWriter() (*Writer, error) {
|
func NewWriter() (*Writer, error) {
|
||||||
|
var rs *Writer
|
||||||
|
|
||||||
var start safejs.Func
|
var start safejs.Func
|
||||||
var controller safejs.Value
|
var controller safejs.Value
|
||||||
|
|
||||||
@@ -34,6 +37,7 @@ func NewWriter() (*Writer, error) {
|
|||||||
|
|
||||||
cancel, err = safejs.FuncOf(func(_ safejs.Value, _ []safejs.Value) any {
|
cancel, err = safejs.FuncOf(func(_ safejs.Value, _ []safejs.Value) any {
|
||||||
defer cancel.Release()
|
defer cancel.Release()
|
||||||
|
rs.cancelled = true
|
||||||
cancelCtx()
|
cancelCtx()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -54,14 +58,20 @@ func NewWriter() (*Writer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Writer{
|
rs = &Writer{
|
||||||
Value: value,
|
Value: value,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Writer) Write(b []byte) (int, error) {
|
func (rs *Writer) Write(b []byte) (int, error) {
|
||||||
|
if rs.cancelled {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
chunk, err := jstype.Uint8Array.New(len(b)) // FIXME reuse same Uint8Array?
|
chunk, err := jstype.Uint8Array.New(len(b)) // FIXME reuse same Uint8Array?
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -78,8 +88,12 @@ func (rs *Writer) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Writer) Close() error {
|
func (rs *Writer) Close() error {
|
||||||
rs.controller.Call("close")
|
if rs.cancelled {
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := rs.controller.Call("close")
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Writer) Context() context.Context {
|
func (rs *Writer) Context() context.Context {
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ func (v Value) IndexString(i int) (string, error) {
|
|||||||
return safejs.Value(sv).String()
|
return safejs.Value(sv).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Value) IsNull() bool {
|
||||||
|
return safejs.Value(v).IsNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) IsUndefined() bool {
|
||||||
|
return safejs.Value(v).IsUndefined()
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) New(args ...any) (Value, error) {
|
func (v Value) New(args ...any) (Value, error) {
|
||||||
args = toJSValue(args).([]any)
|
args = toJSValue(args).([]any)
|
||||||
r, err := safejs.Value(v).New(args...)
|
r, err := safejs.Value(v).New(args...)
|
||||||
|
|||||||
787
package-lock.json
generated
Normal file
787
package-lock.json
generated
Normal file
@@ -0,0 +1,787 @@
|
|||||||
|
{
|
||||||
|
"name": "go-wasm-http-server",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "go-wasm-http-server",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"all-contributors-cli": "^6.26.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/all-contributors-cli": {
|
||||||
|
"version": "6.26.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.26.1.tgz",
|
||||||
|
"integrity": "sha512-Ymgo3FJACRBEd1eE653FD1J/+uD0kqpUNYfr9zNC1Qby0LgbhDBzB3EF6uvkAbYpycStkk41J+0oo37Lc02yEw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.6",
|
||||||
|
"async": "^3.1.0",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"didyoumean": "^1.2.1",
|
||||||
|
"inquirer": "^7.3.3",
|
||||||
|
"json-fixer": "^1.6.8",
|
||||||
|
"lodash": "^4.11.2",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"pify": "^5.0.0",
|
||||||
|
"yargs": "^15.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"all-contributors": "dist/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"prettier": "^2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-escapes": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": "^0.21.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chardet": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/cli-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"restore-cursor": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-width": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/didyoumean": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/external-editor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chardet": "^0.7.0",
|
||||||
|
"iconv-lite": "^0.4.24",
|
||||||
|
"tmp": "^0.0.33"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/figures": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inquirer": {
|
||||||
|
"version": "7.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
|
||||||
|
"integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-escapes": "^4.2.1",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"cli-cursor": "^3.1.0",
|
||||||
|
"cli-width": "^3.0.0",
|
||||||
|
"external-editor": "^3.0.3",
|
||||||
|
"figures": "^3.0.0",
|
||||||
|
"lodash": "^4.17.19",
|
||||||
|
"mute-stream": "0.0.8",
|
||||||
|
"run-async": "^2.4.0",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"through": "^2.3.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/json-fixer": {
|
||||||
|
"version": "1.6.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-fixer/-/json-fixer-1.6.15.tgz",
|
||||||
|
"integrity": "sha512-TuDuZ5KrgyjoCIppdPXBMqiGfota55+odM+j2cQ5rt/XKyKmqGB3Whz1F8SN8+60yYGy/Nu5lbRZ+rx8kBIvBw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.18.9",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"pegjs": "^0.10.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/mimic-fn": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mute-stream": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/onetime": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mimic-fn": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pegjs": {
|
||||||
|
"version": "0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz",
|
||||||
|
"integrity": "sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"pegjs": "bin/pegjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pify": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||||
|
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/restore-cursor": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"onetime": "^5.1.0",
|
||||||
|
"signal-exit": "^3.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/run-async": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "6.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
|
||||||
|
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">=2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/through": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tmp": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"os-tmpdir": "~1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
|
"node_modules/type-fest": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR CC0-1.0)",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "15.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||||
|
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||||
|
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,5 +5,8 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "https://github.com/nlepage/go-wasm-http-server",
|
"repository": "https://github.com/nlepage/go-wasm-http-server",
|
||||||
"author": "Nicolas Lepage <19571875+nlepage@users.noreply.github.com>",
|
"author": "Nicolas Lepage <19571875+nlepage@users.noreply.github.com>",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"all-contributors-cli": "^6.26.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
request.go
88
request.go
@@ -1,43 +1,89 @@
|
|||||||
package wasmhttp
|
package wasmhttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/url"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
|
promise "github.com/nlepage/go-js-promise"
|
||||||
"github.com/nlepage/go-wasm-http-server/v2/internal/readablestream"
|
"github.com/nlepage/go-wasm-http-server/v2/internal/readablestream"
|
||||||
"github.com/nlepage/go-wasm-http-server/v2/internal/safejs"
|
"github.com/nlepage/go-wasm-http-server/v2/internal/safejs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
navigator = safejs.Safe(js.Global().Get("navigator"))
|
||||||
|
)
|
||||||
|
|
||||||
// Request builds and returns the equivalent http.Request
|
// Request builds and returns the equivalent http.Request
|
||||||
func Request(uvalue js.Value) (*http.Request, error) {
|
func Request(uvalue js.Value) (*http.Request, error) {
|
||||||
value := safejs.Safe(uvalue)
|
value := safejs.Safe(uvalue)
|
||||||
|
|
||||||
body, err := value.Get("body")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := body.Call("getReader")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
method, err := value.GetString("method")
|
method, err := value.GetString("method")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := value.GetString("url")
|
rawURL, err := value.GetString("url")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u, err := url.Parse(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req := httptest.NewRequest(
|
body, err := value.Get("body")
|
||||||
method,
|
if err != nil {
|
||||||
url,
|
return nil, err
|
||||||
readablestream.NewReader(r),
|
}
|
||||||
)
|
|
||||||
|
var bodyReader io.ReadCloser
|
||||||
|
|
||||||
|
if !body.IsNull() {
|
||||||
|
// WORKAROUND: Firefox does not have request.body ReadableStream
|
||||||
|
if body.IsUndefined() {
|
||||||
|
blobp, err := value.Call("blob")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, err := promise.Await(safejs.Unsafe(blobp))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err = safejs.Safe(blob).Call("stream")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := body.Call("getReader")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyReader = readablestream.NewReader(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &http.Request{
|
||||||
|
Method: method,
|
||||||
|
URL: u,
|
||||||
|
Host: u.Host,
|
||||||
|
Body: bodyReader,
|
||||||
|
Header: make(http.Header),
|
||||||
|
Proto: "HTTP/1.1",
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
RequestURI: rawURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
referer, err := value.GetString("referrer")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Referer", referer)
|
||||||
|
|
||||||
headers, err := value.Get("headers")
|
headers, err := value.Get("headers")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -81,5 +127,13 @@ func Request(uvalue js.Value) (*http.Request, error) {
|
|||||||
req.Header.Set(key, value)
|
req.Header.Set(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.UserAgent() == "" {
|
||||||
|
userAgent, err := navigator.GetString("userAgent")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func (r *response) WriteHeader(code int) {
|
|||||||
checkWriteHeaderCode(code)
|
checkWriteHeaderCode(code)
|
||||||
|
|
||||||
init, err := safejs.ValueOf(map[string]any{
|
init, err := safejs.ValueOf(map[string]any{
|
||||||
"code": code,
|
"status": code,
|
||||||
"headers": r.headerValue(),
|
"headers": r.headerValue(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,6 +123,9 @@ func (r *response) Flush() {
|
|||||||
|
|
||||||
// Close implements [io.Closer]
|
// Close implements [io.Closer]
|
||||||
func (r *response) Close() error {
|
func (r *response) Close() error {
|
||||||
|
if !r.wroteHeader {
|
||||||
|
r.WriteHeader(200)
|
||||||
|
}
|
||||||
if err := r.body.Flush(); err != nil {
|
if err := r.body.Flush(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,7 @@ func (r *response) Context() context.Context {
|
|||||||
func (r *response) WriteError(str string) {
|
func (r *response) WriteError(str string) {
|
||||||
slog.Error(str)
|
slog.Error(str)
|
||||||
if !r.wroteHeader {
|
if !r.wroteHeader {
|
||||||
|
r.Header().Set("Content-Type", "text/plain")
|
||||||
r.WriteHeader(500)
|
r.WriteHeader(500)
|
||||||
_, _ = r.WriteString(str)
|
_, _ = r.WriteString(str)
|
||||||
}
|
}
|
||||||
|
|||||||
19
serve.go
19
serve.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
@@ -21,10 +22,8 @@ func Serve(handler http.Handler) (func(), error) {
|
|||||||
h = http.DefaultServeMux
|
h = http.DefaultServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix, err := wasmhttp.GetString("path")
|
path := os.Getenv("WASM_HTTP_PATH")
|
||||||
if err != nil {
|
prefix := path
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for strings.HasSuffix(prefix, "/") {
|
for strings.HasSuffix(prefix, "/") {
|
||||||
prefix = strings.TrimSuffix(prefix, "/")
|
prefix = strings.TrimSuffix(prefix, "/")
|
||||||
@@ -57,19 +56,13 @@ func Serve(handler http.Handler) (func(), error) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
var errStr string
|
res.WriteError(fmt.Sprintf("%+v", r))
|
||||||
if err, ok := r.(error); ok {
|
|
||||||
errStr = err.Error()
|
|
||||||
} else {
|
|
||||||
errStr = fmt.Sprintf("%s", r)
|
|
||||||
}
|
|
||||||
res.WriteError(errStr)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
req, err := Request(safejs.Unsafe(args[0]))
|
req, err := Request(safejs.Unsafe(args[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.WriteError(err.Error())
|
res.WriteError(fmt.Sprintf("%+v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +77,7 @@ func Serve(handler http.Handler) (func(), error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = wasmhttp.Call("setHandler", handlerValue); err != nil {
|
if _, err = wasmhttp.Call("setHandler", path, handlerValue); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
sw.js
110
sw.js
@@ -1,34 +1,94 @@
|
|||||||
function registerWasmHTTPListener(wasm, { base, args = [] } = {}) {
|
class WasmHTTP {
|
||||||
let path = new URL(registration.scope).pathname
|
#listeners = new Map();
|
||||||
if (base && base !== '') path = `${trimEnd(path, '/')}/${trimStart(base, '/')}`
|
|
||||||
|
|
||||||
const handlerPromise = new Promise(setHandler => {
|
setHandler(path, handler) {
|
||||||
self.wasmhttp = {
|
const listener = this.#listeners.get(path);
|
||||||
path,
|
if (!listener) {
|
||||||
setHandler,
|
throw new Error(`no listener for path "${path}"`);
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const go = new Go()
|
listener.setHandler(handler);
|
||||||
go.argv = [wasm, ...args]
|
}
|
||||||
WebAssembly.instantiateStreaming(fetch(wasm), go.importObject).then(({ instance }) => go.run(instance))
|
|
||||||
|
|
||||||
addEventListener('fetch', e => {
|
handle(e) {
|
||||||
const { pathname } = new URL(e.request.url)
|
const { pathname } = new URL(e.request.url);
|
||||||
if (!pathname.startsWith(path)) return
|
|
||||||
|
|
||||||
e.respondWith(handlerPromise.then(handler => handler(e.request)))
|
for (const [path, listener] of this.#listeners) {
|
||||||
})
|
if (pathname.startsWith(path)) {
|
||||||
|
listener.handle(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addListener({ wasm, base = '', cacheName, passthrough, args = [], env = {} }) {
|
||||||
|
const path = new URL(trimStart(base, '/'), registration.scope).pathname;
|
||||||
|
|
||||||
|
if (this.#listeners.has(path)) {
|
||||||
|
throw new Error(`a listener is already registered for path "${path}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#listeners.set(path, new WasmHTTPListener({ wasm, path, cacheName, passthrough, args, env }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WasmHTTPListener {
|
||||||
|
#handlerPromise;
|
||||||
|
#resolveHandler;
|
||||||
|
#passthrough;
|
||||||
|
|
||||||
|
constructor({ wasm, path, cacheName, passthrough, args, env }) {
|
||||||
|
this.#handlerPromise = new Promise((resolve) => {
|
||||||
|
this.#resolveHandler = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#passthrough = passthrough;
|
||||||
|
|
||||||
|
this.#run({ wasm, path, cacheName, passthrough, args, env });
|
||||||
|
}
|
||||||
|
|
||||||
|
async #run({ wasm, path, cacheName, args, env }) {
|
||||||
|
try {
|
||||||
|
const go = new Go();
|
||||||
|
go.argv = [wasm, ...args];
|
||||||
|
go.env = { ...env, WASM_HTTP_PATH: path };
|
||||||
|
|
||||||
|
const cache = cacheName ? await caches.open(cacheName) : caches;
|
||||||
|
const source = (await cache.match(wasm)) ?? (await fetch(wasm));
|
||||||
|
|
||||||
|
const { instance } = await WebAssembly.instantiateStreaming(source, go.importObject);
|
||||||
|
|
||||||
|
await go.run(instance);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`error while running ${wasm} for path "${path}"`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setHandler(handler) {
|
||||||
|
this.#resolveHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle(e) {
|
||||||
|
if (this.#passthrough?.(e.request)) return;
|
||||||
|
|
||||||
|
// FIXME return 500 if run has thrown
|
||||||
|
|
||||||
|
e.respondWith(this.#handlerPromise.then((handler) => handler(e.request)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.wasmhttp = new WasmHTTP();
|
||||||
|
|
||||||
|
addEventListener('fetch', (e) => {
|
||||||
|
self.wasmhttp.handle(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
function registerWasmHTTPListener(wasm, { base, cacheName, passthrough, args, env } = {}) {
|
||||||
|
self.wasmhttp.addListener({ wasm, base, cacheName, passthrough, args, env });
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimStart(s, c) {
|
function trimStart(s, c) {
|
||||||
let r = s
|
let r = s;
|
||||||
while (r.startsWith(c)) r = r.slice(c.length)
|
while (r.startsWith(c)) r = r.slice(c.length);
|
||||||
return r
|
return r;
|
||||||
}
|
|
||||||
|
|
||||||
function trimEnd(s, c) {
|
|
||||||
let r = s
|
|
||||||
while (r.endsWith(c)) r = r.slice(0, -c.length)
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user