├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── demo ├── index.html ├── labor.js ├── main.go ├── package.json ├── sw.js ├── wasm_exec.js └── yarn.lock ├── download.go ├── go.mod ├── promise.go ├── request.go ├── response.go └── serve.go /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - run: curl https://purge.jsdelivr.net/gh/yisar/labor/sw.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.html 8 | *.htm 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | **/node_modules/ 18 | demo/out.wasm 19 | 20 | # Other 21 | .DS_Store 22 | /.idea 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Labor 2 | 3 | Implementation of [Web Container](https://github.com/stackblitz/webcontainer-core) 4 | 5 | ### Run 6 | 7 | ```shell 8 | $ cd demo 9 | $ yarn build 10 | $ yarn dev 11 | ``` 12 | 13 | ### Detail 14 | 15 | ```js 16 | code => js runtime => rust/go wasm API 17 | ``` 18 | 19 | 举个例子 20 | 21 | ```js 22 | const res = await readFile('a.js') 23 | ``` 24 | 这一句代码中的 `readFile` 最终是要执行到 rust/go 中的 API 的,但到底怎么将 go 的 API 注入进去的呢? 25 | 26 | 此时就需要 go 注入到全局一个 API 27 | 28 | ```go 29 | js.Global().Set("readFile", js.FuncOf(func(this js.Value, args []js.Value) interface{} { 30 | readFile(args[0].String()) // 相当于 eval 31 | return nil 32 | })) 33 | ``` 34 | 我们在 js 侧,只需要直接调用,不需要发接口 35 | 36 | ```js 37 | new Function('readFile', `const res = await readFile('a.js')`)(readFile) 38 | ``` 39 | 大功告成,就是如此简单,在 wasm 端实现 API,然后在 js 侧注入到字符串里即可 40 | 41 | 但还没完,那么问题来了,文件系统呢?http呢? 42 | 43 | 这就得看 go 对 WASI 的支持程度了,亲测 `net/http` 包大部分支持良好,但不能启端口,`osutil` 包内存 API 是支持的 44 | 45 | 46 | demo 只是打个比方,这个项目我会用 go 写,因为 docker 也是用 go 写的 47 | 48 | 值得一提的是,为了获得更好的性能和大小,我们接下来会使用类似 [tinygo](https://github.com/tinygo-org/tinygo) 的编译器 49 | 50 | 但是 trip worker 我还是会用 rust 实现,理由和 deno 一样,所以本质上,同一套标准库,我得写两遍了,呜呜呜 51 | 52 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | demo 6 | 26 | 27 | 28 | 29 | 30 | 31 |

32 | 
33 | 
34 | 


--------------------------------------------------------------------------------
/demo/labor.js:
--------------------------------------------------------------------------------
 1 | importScripts('./wasm_exec.js')
 2 | 
 3 | self.labor = {
 4 |   http: {},
 5 |   fs: {},
 6 | }
 7 | 
 8 | function registerLaborListener(wasm, { base, args = [] } = {}) {
 9 |   let path = new URL(registration.scope).pathname
10 |   if (base && base !== '') path = `${trimEnd(path, '/')}/${trimStart(base, '/')}`
11 | 
12 |   const handlerPromise = new Promise(setHandler => {
13 |     self.labor.path = path
14 |     self.labor.setHandler = setHandler
15 |   })
16 | 
17 |   addEventListener('fetch', e => {
18 |     const { pathname } = new URL(e.request.url)
19 |     if (!pathname.startsWith(path)) return
20 |     e.respondWith(handlerPromise.then(handler => handler(e.request)))
21 |   })
22 | 
23 |   return new Promise((resolve) => {
24 |     const go = new Go()
25 |     go.argv = [wasm, ...args]
26 |     WebAssembly.instantiateStreaming(fetch(wasm), go.importObject).then(({ instance }) => {
27 |       go.run(instance)
28 |       resolve(self.labor)
29 |     })
30 |   })
31 | }
32 | 
33 | function excute(script){
34 |   new Function('labor', `with(labor){${script}}`)(self.labor)
35 | }
36 | 
37 | function trimStart(s, c) {
38 |   let r = s
39 |   while (r.startsWith(c)) r = r.slice(c.length)
40 |   return r
41 | }
42 | 
43 | function trimEnd(s, c) {
44 |   let r = s
45 |   while (r.endsWith(c)) r = r.slice(0, -c.length)
46 |   return r
47 | }
48 | 
49 | self.labor.require = (path) => {
50 |   if (path in self.labor) {
51 |     return self.labor[path]
52 |   }
53 | }


--------------------------------------------------------------------------------
/demo/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"fmt"
 6 | 	"net/http"
 7 | 	"syscall/js"
 8 | 
 9 | 	labor "github.com/yisar/labor"
10 | )
11 | 
12 | func main() {
13 | 
14 | 	js.Global().Get("labor").Get("http").Set("get", js.FuncOf(func (this js.Value, args []js.Value) interface{} {
15 | 		path := args[0].String()
16 | 		httpGet(path)
17 | 		return nil
18 | 	}))
19 | 
20 | 	js.Global().Get("labor").Get("http").Set("serve", js.FuncOf(func (this js.Value, args []js.Value) interface{} {
21 | 		labor.Serve(nil)
22 | 		return nil
23 | 	}))
24 | 
25 | 	js.Global().Get("labor").Get("http").Set("download", js.FuncOf(func (this js.Value, args []js.Value) interface{} {
26 | 		body := labor.Download(args[0].String())
27 | 		return body
28 | 	}))
29 | 
30 | 	select {}
31 | }
32 | 
33 | func httpGet(path string) interface{}{
34 | 	http.HandleFunc(path, func(res http.ResponseWriter, req *http.Request) {
35 | 		params := make(map[string]string)
36 | 		if err := json.NewDecoder(req.Body).Decode(¶ms); err != nil {
37 | 			panic(err)
38 | 		}
39 | 
40 | 		res.Header().Add("Content-Type", "application/json")
41 | 		if err := json.NewEncoder(res).Encode(map[string]string{
42 | 			"message": fmt.Sprintf("Hello %s!", params["name"]),
43 | 		}); err != nil {
44 | 			panic(err)
45 | 		}
46 | 	})
47 | 	return nil
48 | }


--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "demo",
 3 |   "version": "1.0.0",
 4 |   "description": "",
 5 |   "main": "sw.js",
 6 |   "scripts": {
 7 |     "build": "cross-env GOOS=js GOARCH=wasm go build -o out.wasm main.go",
 8 |     "dev": "serve ."
 9 |   },
10 |   "keywords": [],
11 |   "author": "",
12 |   "license": "ISC",
13 |   "devDependencies": {
14 |     "cross-env": "^7.0.3",
15 |     "serve": "^11.3.2"
16 |   }
17 | }
18 | 


--------------------------------------------------------------------------------
/demo/sw.js:
--------------------------------------------------------------------------------
 1 | importScripts('./labor.js')
 2 | 
 3 | addEventListener('install', (event) => {
 4 |   event.waitUntil(skipWaiting())
 5 | })
 6 | 
 7 | addEventListener('activate', event => {
 8 |   event.waitUntil(clients.claim())
 9 | })
10 | 
11 | registerLaborListener('out.wasm', { base: 'api' }).then((labor) => {
12 |   excute(`
13 |     const http = require('http')
14 |     http.get('/hello')
15 |     http.serve()
16 |   `)
17 | 
18 |   // const buffer = self.labor.http.download('https://berial.vercel.app')
19 |   // console.log(buffer)
20 | })
21 | 


--------------------------------------------------------------------------------
/demo/wasm_exec.js:
--------------------------------------------------------------------------------
  1 | // Copyright 2018 The Go Authors. All rights reserved.
  2 | // Use of this source code is governed by a BSD-style
  3 | // license that can be found in the LICENSE file.
  4 | 
  5 | (() => {
  6 | 	// Map multiple JavaScript environments to a single common API,
  7 | 	// preferring web standards over Node.js API.
  8 | 	//
  9 | 	// Environments considered:
 10 | 	// - Browsers
 11 | 	// - Node.js
 12 | 	// - Electron
 13 | 	// - Parcel
 14 | 	// - Webpack
 15 | 
 16 | 	if (typeof global !== "undefined") {
 17 | 		// global already exists
 18 | 	} else if (typeof window !== "undefined") {
 19 | 		window.global = window;
 20 | 	} else if (typeof self !== "undefined") {
 21 | 		self.global = self;
 22 | 	} else {
 23 | 		throw new Error("cannot export Go (neither global, window nor self is defined)");
 24 | 	}
 25 | 
 26 | 	if (!global.require && typeof require !== "undefined") {
 27 | 		global.require = require;
 28 | 	}
 29 | 
 30 | 	if (!global.fs && global.require) {
 31 | 		const fs = require("fs");
 32 | 		if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
 33 | 			global.fs = fs;
 34 | 		}
 35 | 	}
 36 | 
 37 | 	const enosys = () => {
 38 | 		const err = new Error("not implemented");
 39 | 		err.code = "ENOSYS";
 40 | 		return err;
 41 | 	};
 42 | 
 43 | 	if (!global.fs) {
 44 | 		let outputBuf = "";
 45 | 		global.fs = {
 46 | 			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
 47 | 			writeSync(fd, buf) {
 48 | 				outputBuf += decoder.decode(buf);
 49 | 				const nl = outputBuf.lastIndexOf("\n");
 50 | 				if (nl != -1) {
 51 | 					console.log(outputBuf.substr(0, nl));
 52 | 					outputBuf = outputBuf.substr(nl + 1);
 53 | 				}
 54 | 				return buf.length;
 55 | 			},
 56 | 			write(fd, buf, offset, length, position, callback) {
 57 | 				if (offset !== 0 || length !== buf.length || position !== null) {
 58 | 					callback(enosys());
 59 | 					return;
 60 | 				}
 61 | 				const n = this.writeSync(fd, buf);
 62 | 				callback(null, n);
 63 | 			},
 64 | 			chmod(path, mode, callback) { callback(enosys()); },
 65 | 			chown(path, uid, gid, callback) { callback(enosys()); },
 66 | 			close(fd, callback) { callback(enosys()); },
 67 | 			fchmod(fd, mode, callback) { callback(enosys()); },
 68 | 			fchown(fd, uid, gid, callback) { callback(enosys()); },
 69 | 			fstat(fd, callback) { callback(enosys()); },
 70 | 			fsync(fd, callback) { callback(null); },
 71 | 			ftruncate(fd, length, callback) { callback(enosys()); },
 72 | 			lchown(path, uid, gid, callback) { callback(enosys()); },
 73 | 			link(path, link, callback) { callback(enosys()); },
 74 | 			lstat(path, callback) { callback(enosys()); },
 75 | 			mkdir(path, perm, callback) { callback(enosys()); },
 76 | 			open(path, flags, mode, callback) { callback(enosys()); },
 77 | 			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
 78 | 			readdir(path, callback) { callback(enosys()); },
 79 | 			readlink(path, callback) { callback(enosys()); },
 80 | 			rename(from, to, callback) { callback(enosys()); },
 81 | 			rmdir(path, callback) { callback(enosys()); },
 82 | 			stat(path, callback) { callback(enosys()); },
 83 | 			symlink(path, link, callback) { callback(enosys()); },
 84 | 			truncate(path, length, callback) { callback(enosys()); },
 85 | 			unlink(path, callback) { callback(enosys()); },
 86 | 			utimes(path, atime, mtime, callback) { callback(enosys()); },
 87 | 		};
 88 | 	}
 89 | 
 90 | 	if (!global.process) {
 91 | 		global.process = {
 92 | 			getuid() { return -1; },
 93 | 			getgid() { return -1; },
 94 | 			geteuid() { return -1; },
 95 | 			getegid() { return -1; },
 96 | 			getgroups() { throw enosys(); },
 97 | 			pid: -1,
 98 | 			ppid: -1,
 99 | 			umask() { throw enosys(); },
100 | 			cwd() { throw enosys(); },
101 | 			chdir() { throw enosys(); },
102 | 		}
103 | 	}
104 | 
105 | 	if (!global.crypto && global.require) {
106 | 		const nodeCrypto = require("crypto");
107 | 		global.crypto = {
108 | 			getRandomValues(b) {
109 | 				nodeCrypto.randomFillSync(b);
110 | 			},
111 | 		};
112 | 	}
113 | 	if (!global.crypto) {
114 | 		throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
115 | 	}
116 | 
117 | 	if (!global.performance) {
118 | 		global.performance = {
119 | 			now() {
120 | 				const [sec, nsec] = process.hrtime();
121 | 				return sec * 1000 + nsec / 1000000;
122 | 			},
123 | 		};
124 | 	}
125 | 
126 | 	if (!global.TextEncoder && global.require) {
127 | 		global.TextEncoder = require("util").TextEncoder;
128 | 	}
129 | 	if (!global.TextEncoder) {
130 | 		throw new Error("global.TextEncoder is not available, polyfill required");
131 | 	}
132 | 
133 | 	if (!global.TextDecoder && global.require) {
134 | 		global.TextDecoder = require("util").TextDecoder;
135 | 	}
136 | 	if (!global.TextDecoder) {
137 | 		throw new Error("global.TextDecoder is not available, polyfill required");
138 | 	}
139 | 
140 | 	// End of polyfills for common API.
141 | 
142 | 	const encoder = new TextEncoder("utf-8");
143 | 	const decoder = new TextDecoder("utf-8");
144 | 
145 | 	global.Go = class {
146 | 		constructor() {
147 | 			this.argv = ["js"];
148 | 			this.env = {};
149 | 			this.exit = (code) => {
150 | 				if (code !== 0) {
151 | 					console.warn("exit code:", code);
152 | 				}
153 | 			};
154 | 			this._exitPromise = new Promise((resolve) => {
155 | 				this._resolveExitPromise = resolve;
156 | 			});
157 | 			this._pendingEvent = null;
158 | 			this._scheduledTimeouts = new Map();
159 | 			this._nextCallbackTimeoutID = 1;
160 | 
161 | 			const setInt64 = (addr, v) => {
162 | 				this.mem.setUint32(addr + 0, v, true);
163 | 				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
164 | 			}
165 | 
166 | 			const getInt64 = (addr) => {
167 | 				const low = this.mem.getUint32(addr + 0, true);
168 | 				const high = this.mem.getInt32(addr + 4, true);
169 | 				return low + high * 4294967296;
170 | 			}
171 | 
172 | 			const loadValue = (addr) => {
173 | 				const f = this.mem.getFloat64(addr, true);
174 | 				if (f === 0) {
175 | 					return undefined;
176 | 				}
177 | 				if (!isNaN(f)) {
178 | 					return f;
179 | 				}
180 | 
181 | 				const id = this.mem.getUint32(addr, true);
182 | 				return this._values[id];
183 | 			}
184 | 
185 | 			const storeValue = (addr, v) => {
186 | 				const nanHead = 0x7FF80000;
187 | 
188 | 				if (typeof v === "number" && v !== 0) {
189 | 					if (isNaN(v)) {
190 | 						this.mem.setUint32(addr + 4, nanHead, true);
191 | 						this.mem.setUint32(addr, 0, true);
192 | 						return;
193 | 					}
194 | 					this.mem.setFloat64(addr, v, true);
195 | 					return;
196 | 				}
197 | 
198 | 				if (v === undefined) {
199 | 					this.mem.setFloat64(addr, 0, true);
200 | 					return;
201 | 				}
202 | 
203 | 				let id = this._ids.get(v);
204 | 				if (id === undefined) {
205 | 					id = this._idPool.pop();
206 | 					if (id === undefined) {
207 | 						id = this._values.length;
208 | 					}
209 | 					this._values[id] = v;
210 | 					this._goRefCounts[id] = 0;
211 | 					this._ids.set(v, id);
212 | 				}
213 | 				this._goRefCounts[id]++;
214 | 				let typeFlag = 0;
215 | 				switch (typeof v) {
216 | 					case "object":
217 | 						if (v !== null) {
218 | 							typeFlag = 1;
219 | 						}
220 | 						break;
221 | 					case "string":
222 | 						typeFlag = 2;
223 | 						break;
224 | 					case "symbol":
225 | 						typeFlag = 3;
226 | 						break;
227 | 					case "function":
228 | 						typeFlag = 4;
229 | 						break;
230 | 				}
231 | 				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
232 | 				this.mem.setUint32(addr, id, true);
233 | 			}
234 | 
235 | 			const loadSlice = (addr) => {
236 | 				const array = getInt64(addr + 0);
237 | 				const len = getInt64(addr + 8);
238 | 				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
239 | 			}
240 | 
241 | 			const loadSliceOfValues = (addr) => {
242 | 				const array = getInt64(addr + 0);
243 | 				const len = getInt64(addr + 8);
244 | 				const a = new Array(len);
245 | 				for (let i = 0; i < len; i++) {
246 | 					a[i] = loadValue(array + i * 8);
247 | 				}
248 | 				return a;
249 | 			}
250 | 
251 | 			const loadString = (addr) => {
252 | 				const saddr = getInt64(addr + 0);
253 | 				const len = getInt64(addr + 8);
254 | 				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
255 | 			}
256 | 
257 | 			const timeOrigin = Date.now() - performance.now();
258 | 			this.importObject = {
259 | 				go: {
260 | 					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
261 | 					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
262 | 					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
263 | 					// This changes the SP, thus we have to update the SP used by the imported function.
264 | 
265 | 					// func wasmExit(code int32)
266 | 					"runtime.wasmExit": (sp) => {
267 | 						sp >>>= 0;
268 | 						const code = this.mem.getInt32(sp + 8, true);
269 | 						this.exited = true;
270 | 						delete this._inst;
271 | 						delete this._values;
272 | 						delete this._goRefCounts;
273 | 						delete this._ids;
274 | 						delete this._idPool;
275 | 						this.exit(code);
276 | 					},
277 | 
278 | 					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
279 | 					"runtime.wasmWrite": (sp) => {
280 | 						sp >>>= 0;
281 | 						const fd = getInt64(sp + 8);
282 | 						const p = getInt64(sp + 16);
283 | 						const n = this.mem.getInt32(sp + 24, true);
284 | 						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
285 | 					},
286 | 
287 | 					// func resetMemoryDataView()
288 | 					"runtime.resetMemoryDataView": (sp) => {
289 | 						sp >>>= 0;
290 | 						this.mem = new DataView(this._inst.exports.mem.buffer);
291 | 					},
292 | 
293 | 					// func nanotime1() int64
294 | 					"runtime.nanotime1": (sp) => {
295 | 						sp >>>= 0;
296 | 						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
297 | 					},
298 | 
299 | 					// func walltime1() (sec int64, nsec int32)
300 | 					"runtime.walltime1": (sp) => {
301 | 						sp >>>= 0;
302 | 						const msec = (new Date).getTime();
303 | 						setInt64(sp + 8, msec / 1000);
304 | 						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
305 | 					},
306 | 
307 | 					// func scheduleTimeoutEvent(delay int64) int32
308 | 					"runtime.scheduleTimeoutEvent": (sp) => {
309 | 						sp >>>= 0;
310 | 						const id = this._nextCallbackTimeoutID;
311 | 						this._nextCallbackTimeoutID++;
312 | 						this._scheduledTimeouts.set(id, setTimeout(
313 | 							() => {
314 | 								this._resume();
315 | 								while (this._scheduledTimeouts.has(id)) {
316 | 									// for some reason Go failed to register the timeout event, log and try again
317 | 									// (temporary workaround for https://github.com/golang/go/issues/28975)
318 | 									console.warn("scheduleTimeoutEvent: missed timeout event");
319 | 									this._resume();
320 | 								}
321 | 							},
322 | 							getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
323 | 						));
324 | 						this.mem.setInt32(sp + 16, id, true);
325 | 					},
326 | 
327 | 					// func clearTimeoutEvent(id int32)
328 | 					"runtime.clearTimeoutEvent": (sp) => {
329 | 						sp >>>= 0;
330 | 						const id = this.mem.getInt32(sp + 8, true);
331 | 						clearTimeout(this._scheduledTimeouts.get(id));
332 | 						this._scheduledTimeouts.delete(id);
333 | 					},
334 | 
335 | 					// func getRandomData(r []byte)
336 | 					"runtime.getRandomData": (sp) => {
337 | 						sp >>>= 0;
338 | 						crypto.getRandomValues(loadSlice(sp + 8));
339 | 					},
340 | 
341 | 					// func finalizeRef(v ref)
342 | 					"syscall/js.finalizeRef": (sp) => {
343 | 						sp >>>= 0;
344 | 						const id = this.mem.getUint32(sp + 8, true);
345 | 						this._goRefCounts[id]--;
346 | 						if (this._goRefCounts[id] === 0) {
347 | 							const v = this._values[id];
348 | 							this._values[id] = null;
349 | 							this._ids.delete(v);
350 | 							this._idPool.push(id);
351 | 						}
352 | 					},
353 | 
354 | 					// func stringVal(value string) ref
355 | 					"syscall/js.stringVal": (sp) => {
356 | 						sp >>>= 0;
357 | 						storeValue(sp + 24, loadString(sp + 8));
358 | 					},
359 | 
360 | 					// func valueGet(v ref, p string) ref
361 | 					"syscall/js.valueGet": (sp) => {
362 | 						sp >>>= 0;
363 | 						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
364 | 						sp = this._inst.exports.getsp() >>> 0; // see comment above
365 | 						storeValue(sp + 32, result);
366 | 					},
367 | 
368 | 					// func valueSet(v ref, p string, x ref)
369 | 					"syscall/js.valueSet": (sp) => {
370 | 						sp >>>= 0;
371 | 						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
372 | 					},
373 | 
374 | 					// func valueDelete(v ref, p string)
375 | 					"syscall/js.valueDelete": (sp) => {
376 | 						sp >>>= 0;
377 | 						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
378 | 					},
379 | 
380 | 					// func valueIndex(v ref, i int) ref
381 | 					"syscall/js.valueIndex": (sp) => {
382 | 						sp >>>= 0;
383 | 						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
384 | 					},
385 | 
386 | 					// valueSetIndex(v ref, i int, x ref)
387 | 					"syscall/js.valueSetIndex": (sp) => {
388 | 						sp >>>= 0;
389 | 						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
390 | 					},
391 | 
392 | 					// func valueCall(v ref, m string, args []ref) (ref, bool)
393 | 					"syscall/js.valueCall": (sp) => {
394 | 						sp >>>= 0;
395 | 						try {
396 | 							const v = loadValue(sp + 8);
397 | 							const m = Reflect.get(v, loadString(sp + 16));
398 | 							const args = loadSliceOfValues(sp + 32);
399 | 							const result = Reflect.apply(m, v, args);
400 | 							sp = this._inst.exports.getsp() >>> 0; // see comment above
401 | 							storeValue(sp + 56, result);
402 | 							this.mem.setUint8(sp + 64, 1);
403 | 						} catch (err) {
404 | 							storeValue(sp + 56, err);
405 | 							this.mem.setUint8(sp + 64, 0);
406 | 						}
407 | 					},
408 | 
409 | 					// func valueInvoke(v ref, args []ref) (ref, bool)
410 | 					"syscall/js.valueInvoke": (sp) => {
411 | 						sp >>>= 0;
412 | 						try {
413 | 							const v = loadValue(sp + 8);
414 | 							const args = loadSliceOfValues(sp + 16);
415 | 							const result = Reflect.apply(v, undefined, args);
416 | 							sp = this._inst.exports.getsp() >>> 0; // see comment above
417 | 							storeValue(sp + 40, result);
418 | 							this.mem.setUint8(sp + 48, 1);
419 | 						} catch (err) {
420 | 							storeValue(sp + 40, err);
421 | 							this.mem.setUint8(sp + 48, 0);
422 | 						}
423 | 					},
424 | 
425 | 					// func valueNew(v ref, args []ref) (ref, bool)
426 | 					"syscall/js.valueNew": (sp) => {
427 | 						sp >>>= 0;
428 | 						try {
429 | 							const v = loadValue(sp + 8);
430 | 							const args = loadSliceOfValues(sp + 16);
431 | 							const result = Reflect.construct(v, args);
432 | 							sp = this._inst.exports.getsp() >>> 0; // see comment above
433 | 							storeValue(sp + 40, result);
434 | 							this.mem.setUint8(sp + 48, 1);
435 | 						} catch (err) {
436 | 							storeValue(sp + 40, err);
437 | 							this.mem.setUint8(sp + 48, 0);
438 | 						}
439 | 					},
440 | 
441 | 					// func valueLength(v ref) int
442 | 					"syscall/js.valueLength": (sp) => {
443 | 						sp >>>= 0;
444 | 						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
445 | 					},
446 | 
447 | 					// valuePrepareString(v ref) (ref, int)
448 | 					"syscall/js.valuePrepareString": (sp) => {
449 | 						sp >>>= 0;
450 | 						const str = encoder.encode(String(loadValue(sp + 8)));
451 | 						storeValue(sp + 16, str);
452 | 						setInt64(sp + 24, str.length);
453 | 					},
454 | 
455 | 					// valueLoadString(v ref, b []byte)
456 | 					"syscall/js.valueLoadString": (sp) => {
457 | 						sp >>>= 0;
458 | 						const str = loadValue(sp + 8);
459 | 						loadSlice(sp + 16).set(str);
460 | 					},
461 | 
462 | 					// func valueInstanceOf(v ref, t ref) bool
463 | 					"syscall/js.valueInstanceOf": (sp) => {
464 | 						sp >>>= 0;
465 | 						this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
466 | 					},
467 | 
468 | 					// func copyBytesToGo(dst []byte, src ref) (int, bool)
469 | 					"syscall/js.copyBytesToGo": (sp) => {
470 | 						sp >>>= 0;
471 | 						const dst = loadSlice(sp + 8);
472 | 						const src = loadValue(sp + 32);
473 | 						if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
474 | 							this.mem.setUint8(sp + 48, 0);
475 | 							return;
476 | 						}
477 | 						const toCopy = src.subarray(0, dst.length);
478 | 						dst.set(toCopy);
479 | 						setInt64(sp + 40, toCopy.length);
480 | 						this.mem.setUint8(sp + 48, 1);
481 | 					},
482 | 
483 | 					// func copyBytesToJS(dst ref, src []byte) (int, bool)
484 | 					"syscall/js.copyBytesToJS": (sp) => {
485 | 						sp >>>= 0;
486 | 						const dst = loadValue(sp + 8);
487 | 						const src = loadSlice(sp + 16);
488 | 						if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
489 | 							this.mem.setUint8(sp + 48, 0);
490 | 							return;
491 | 						}
492 | 						const toCopy = src.subarray(0, dst.length);
493 | 						dst.set(toCopy);
494 | 						setInt64(sp + 40, toCopy.length);
495 | 						this.mem.setUint8(sp + 48, 1);
496 | 					},
497 | 
498 | 					"debug": (value) => {
499 | 						console.log(value);
500 | 					},
501 | 				}
502 | 			};
503 | 		}
504 | 
505 | 		async run(instance) {
506 | 			if (!(instance instanceof WebAssembly.Instance)) {
507 | 				throw new Error("Go.run: WebAssembly.Instance expected");
508 | 			}
509 | 			this._inst = instance;
510 | 			this.mem = new DataView(this._inst.exports.mem.buffer);
511 | 			this._values = [ // JS values that Go currently has references to, indexed by reference id
512 | 				NaN,
513 | 				0,
514 | 				null,
515 | 				true,
516 | 				false,
517 | 				global,
518 | 				this,
519 | 			];
520 | 			this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
521 | 			this._ids = new Map([ // mapping from JS values to reference ids
522 | 				[0, 1],
523 | 				[null, 2],
524 | 				[true, 3],
525 | 				[false, 4],
526 | 				[global, 5],
527 | 				[this, 6],
528 | 			]);
529 | 			this._idPool = [];   // unused ids that have been garbage collected
530 | 			this.exited = false; // whether the Go program has exited
531 | 
532 | 			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
533 | 			let offset = 4096;
534 | 
535 | 			const strPtr = (str) => {
536 | 				const ptr = offset;
537 | 				const bytes = encoder.encode(str + "\0");
538 | 				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
539 | 				offset += bytes.length;
540 | 				if (offset % 8 !== 0) {
541 | 					offset += 8 - (offset % 8);
542 | 				}
543 | 				return ptr;
544 | 			};
545 | 
546 | 			const argc = this.argv.length;
547 | 
548 | 			const argvPtrs = [];
549 | 			this.argv.forEach((arg) => {
550 | 				argvPtrs.push(strPtr(arg));
551 | 			});
552 | 			argvPtrs.push(0);
553 | 
554 | 			const keys = Object.keys(this.env).sort();
555 | 			keys.forEach((key) => {
556 | 				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
557 | 			});
558 | 			argvPtrs.push(0);
559 | 
560 | 			const argv = offset;
561 | 			argvPtrs.forEach((ptr) => {
562 | 				this.mem.setUint32(offset, ptr, true);
563 | 				this.mem.setUint32(offset + 4, 0, true);
564 | 				offset += 8;
565 | 			});
566 | 
567 | 			this._inst.exports.run(argc, argv);
568 | 			if (this.exited) {
569 | 				this._resolveExitPromise();
570 | 			}
571 | 			await this._exitPromise;
572 | 		}
573 | 
574 | 		_resume() {
575 | 			if (this.exited) {
576 | 				throw new Error("Go program has already exited");
577 | 			}
578 | 			this._inst.exports.resume();
579 | 			if (this.exited) {
580 | 				this._resolveExitPromise();
581 | 			}
582 | 		}
583 | 
584 | 		_makeFuncWrapper(id) {
585 | 			const go = this;
586 | 			return function () {
587 | 				const event = { id: id, this: this, args: arguments };
588 | 				go._pendingEvent = event;
589 | 				go._resume();
590 | 				return event.result;
591 | 			};
592 | 		}
593 | 	}
594 | 
595 | 	if (
596 | 		typeof module !== "undefined" &&
597 | 		global.require &&
598 | 		global.require.main === module &&
599 | 		global.process &&
600 | 		global.process.versions &&
601 | 		!global.process.versions.electron
602 | 	) {
603 | 		if (process.argv.length < 3) {
604 | 			console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
605 | 			process.exit(1);
606 | 		}
607 | 
608 | 		const go = new Go();
609 | 		go.argv = process.argv.slice(2);
610 | 		go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
611 | 		go.exit = process.exit;
612 | 		WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
613 | 			process.on("exit", (code) => { // Node.js exits if no event handler is pending
614 | 				if (code === 0 && !go.exited) {
615 | 					// deadlock, make Go print error and stack traces
616 | 					go._pendingEvent = { id: 0 };
617 | 					go._resume();
618 | 				}
619 | 			});
620 | 			return go.run(result.instance);
621 | 		}).catch((err) => {
622 | 			console.error(err);
623 | 			process.exit(1);
624 | 		});
625 | 	}
626 | })();
627 | 


--------------------------------------------------------------------------------
/demo/yarn.lock:
--------------------------------------------------------------------------------
  1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
  2 | # yarn lockfile v1
  3 | 
  4 | 
  5 | "@zeit/schemas@2.6.0":
  6 |   version "2.6.0"
  7 |   resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
  8 |   integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==
  9 | 
 10 | accepts@~1.3.5:
 11 |   version "1.3.7"
 12 |   resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
 13 |   integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
 14 |   dependencies:
 15 |     mime-types "~2.1.24"
 16 |     negotiator "0.6.2"
 17 | 
 18 | ajv@6.5.3:
 19 |   version "6.5.3"
 20 |   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9"
 21 |   integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==
 22 |   dependencies:
 23 |     fast-deep-equal "^2.0.1"
 24 |     fast-json-stable-stringify "^2.0.0"
 25 |     json-schema-traverse "^0.4.1"
 26 |     uri-js "^4.2.2"
 27 | 
 28 | ansi-align@^2.0.0:
 29 |   version "2.0.0"
 30 |   resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
 31 |   integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
 32 |   dependencies:
 33 |     string-width "^2.0.0"
 34 | 
 35 | ansi-regex@^3.0.0:
 36 |   version "3.0.0"
 37 |   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
 38 |   integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
 39 | 
 40 | ansi-styles@^3.2.1:
 41 |   version "3.2.1"
 42 |   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
 43 |   integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
 44 |   dependencies:
 45 |     color-convert "^1.9.0"
 46 | 
 47 | arch@^2.1.0:
 48 |   version "2.2.0"
 49 |   resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
 50 |   integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
 51 | 
 52 | arg@2.0.0:
 53 |   version "2.0.0"
 54 |   resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545"
 55 |   integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==
 56 | 
 57 | balanced-match@^1.0.0:
 58 |   version "1.0.2"
 59 |   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
 60 |   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 61 | 
 62 | boxen@1.3.0:
 63 |   version "1.3.0"
 64 |   resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
 65 |   integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
 66 |   dependencies:
 67 |     ansi-align "^2.0.0"
 68 |     camelcase "^4.0.0"
 69 |     chalk "^2.0.1"
 70 |     cli-boxes "^1.0.0"
 71 |     string-width "^2.0.0"
 72 |     term-size "^1.2.0"
 73 |     widest-line "^2.0.0"
 74 | 
 75 | brace-expansion@^1.1.7:
 76 |   version "1.1.11"
 77 |   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
 78 |   integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
 79 |   dependencies:
 80 |     balanced-match "^1.0.0"
 81 |     concat-map "0.0.1"
 82 | 
 83 | bytes@3.0.0:
 84 |   version "3.0.0"
 85 |   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
 86 |   integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
 87 | 
 88 | camelcase@^4.0.0:
 89 |   version "4.1.0"
 90 |   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
 91 |   integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
 92 | 
 93 | chalk@2.4.1:
 94 |   version "2.4.1"
 95 |   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
 96 |   integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
 97 |   dependencies:
 98 |     ansi-styles "^3.2.1"
 99 |     escape-string-regexp "^1.0.5"
100 |     supports-color "^5.3.0"
101 | 
102 | chalk@^2.0.1:
103 |   version "2.4.2"
104 |   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
105 |   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
106 |   dependencies:
107 |     ansi-styles "^3.2.1"
108 |     escape-string-regexp "^1.0.5"
109 |     supports-color "^5.3.0"
110 | 
111 | cli-boxes@^1.0.0:
112 |   version "1.0.0"
113 |   resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
114 |   integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
115 | 
116 | clipboardy@1.2.3:
117 |   version "1.2.3"
118 |   resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef"
119 |   integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA==
120 |   dependencies:
121 |     arch "^2.1.0"
122 |     execa "^0.8.0"
123 | 
124 | color-convert@^1.9.0:
125 |   version "1.9.3"
126 |   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
127 |   integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
128 |   dependencies:
129 |     color-name "1.1.3"
130 | 
131 | color-name@1.1.3:
132 |   version "1.1.3"
133 |   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
134 |   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
135 | 
136 | compressible@~2.0.14:
137 |   version "2.0.18"
138 |   resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
139 |   integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
140 |   dependencies:
141 |     mime-db ">= 1.43.0 < 2"
142 | 
143 | compression@1.7.3:
144 |   version "1.7.3"
145 |   resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db"
146 |   integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==
147 |   dependencies:
148 |     accepts "~1.3.5"
149 |     bytes "3.0.0"
150 |     compressible "~2.0.14"
151 |     debug "2.6.9"
152 |     on-headers "~1.0.1"
153 |     safe-buffer "5.1.2"
154 |     vary "~1.1.2"
155 | 
156 | concat-map@0.0.1:
157 |   version "0.0.1"
158 |   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
159 |   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
160 | 
161 | content-disposition@0.5.2:
162 |   version "0.5.2"
163 |   resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
164 |   integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
165 | 
166 | cross-env@^7.0.3:
167 |   version "7.0.3"
168 |   resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
169 |   integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
170 |   dependencies:
171 |     cross-spawn "^7.0.1"
172 | 
173 | cross-spawn@^5.0.1:
174 |   version "5.1.0"
175 |   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
176 |   integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
177 |   dependencies:
178 |     lru-cache "^4.0.1"
179 |     shebang-command "^1.2.0"
180 |     which "^1.2.9"
181 | 
182 | cross-spawn@^7.0.1:
183 |   version "7.0.3"
184 |   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
185 |   integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
186 |   dependencies:
187 |     path-key "^3.1.0"
188 |     shebang-command "^2.0.0"
189 |     which "^2.0.1"
190 | 
191 | debug@2.6.9:
192 |   version "2.6.9"
193 |   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
194 |   integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
195 |   dependencies:
196 |     ms "2.0.0"
197 | 
198 | deep-extend@^0.6.0:
199 |   version "0.6.0"
200 |   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
201 |   integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
202 | 
203 | escape-string-regexp@^1.0.5:
204 |   version "1.0.5"
205 |   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
206 |   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
207 | 
208 | execa@^0.7.0:
209 |   version "0.7.0"
210 |   resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
211 |   integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
212 |   dependencies:
213 |     cross-spawn "^5.0.1"
214 |     get-stream "^3.0.0"
215 |     is-stream "^1.1.0"
216 |     npm-run-path "^2.0.0"
217 |     p-finally "^1.0.0"
218 |     signal-exit "^3.0.0"
219 |     strip-eof "^1.0.0"
220 | 
221 | execa@^0.8.0:
222 |   version "0.8.0"
223 |   resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
224 |   integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=
225 |   dependencies:
226 |     cross-spawn "^5.0.1"
227 |     get-stream "^3.0.0"
228 |     is-stream "^1.1.0"
229 |     npm-run-path "^2.0.0"
230 |     p-finally "^1.0.0"
231 |     signal-exit "^3.0.0"
232 |     strip-eof "^1.0.0"
233 | 
234 | fast-deep-equal@^2.0.1:
235 |   version "2.0.1"
236 |   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
237 |   integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
238 | 
239 | fast-json-stable-stringify@^2.0.0:
240 |   version "2.1.0"
241 |   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
242 |   integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
243 | 
244 | fast-url-parser@1.1.3:
245 |   version "1.1.3"
246 |   resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
247 |   integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=
248 |   dependencies:
249 |     punycode "^1.3.2"
250 | 
251 | get-stream@^3.0.0:
252 |   version "3.0.0"
253 |   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
254 |   integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
255 | 
256 | has-flag@^3.0.0:
257 |   version "3.0.0"
258 |   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
259 |   integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
260 | 
261 | ini@~1.3.0:
262 |   version "1.3.8"
263 |   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
264 |   integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
265 | 
266 | is-fullwidth-code-point@^2.0.0:
267 |   version "2.0.0"
268 |   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
269 |   integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
270 | 
271 | is-stream@^1.1.0:
272 |   version "1.1.0"
273 |   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
274 |   integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
275 | 
276 | isexe@^2.0.0:
277 |   version "2.0.0"
278 |   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
279 |   integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
280 | 
281 | json-schema-traverse@^0.4.1:
282 |   version "0.4.1"
283 |   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
284 |   integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
285 | 
286 | lru-cache@^4.0.1:
287 |   version "4.1.5"
288 |   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
289 |   integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
290 |   dependencies:
291 |     pseudomap "^1.0.2"
292 |     yallist "^2.1.2"
293 | 
294 | mime-db@1.47.0, "mime-db@>= 1.43.0 < 2":
295 |   version "1.47.0"
296 |   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
297 |   integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
298 | 
299 | mime-db@~1.33.0:
300 |   version "1.33.0"
301 |   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
302 |   integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
303 | 
304 | mime-types@2.1.18:
305 |   version "2.1.18"
306 |   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
307 |   integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
308 |   dependencies:
309 |     mime-db "~1.33.0"
310 | 
311 | mime-types@~2.1.24:
312 |   version "2.1.30"
313 |   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
314 |   integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
315 |   dependencies:
316 |     mime-db "1.47.0"
317 | 
318 | minimatch@3.0.4:
319 |   version "3.0.4"
320 |   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
321 |   integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
322 |   dependencies:
323 |     brace-expansion "^1.1.7"
324 | 
325 | minimist@^1.2.0:
326 |   version "1.2.5"
327 |   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
328 |   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
329 | 
330 | ms@2.0.0:
331 |   version "2.0.0"
332 |   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
333 |   integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
334 | 
335 | negotiator@0.6.2:
336 |   version "0.6.2"
337 |   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
338 |   integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
339 | 
340 | npm-run-path@^2.0.0:
341 |   version "2.0.2"
342 |   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
343 |   integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
344 |   dependencies:
345 |     path-key "^2.0.0"
346 | 
347 | on-headers@~1.0.1:
348 |   version "1.0.2"
349 |   resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
350 |   integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
351 | 
352 | p-finally@^1.0.0:
353 |   version "1.0.0"
354 |   resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
355 |   integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
356 | 
357 | path-is-inside@1.0.2:
358 |   version "1.0.2"
359 |   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
360 |   integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
361 | 
362 | path-key@^2.0.0:
363 |   version "2.0.1"
364 |   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
365 |   integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
366 | 
367 | path-key@^3.1.0:
368 |   version "3.1.1"
369 |   resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
370 |   integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
371 | 
372 | path-to-regexp@2.2.1:
373 |   version "2.2.1"
374 |   resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
375 |   integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
376 | 
377 | pseudomap@^1.0.2:
378 |   version "1.0.2"
379 |   resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
380 |   integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
381 | 
382 | punycode@^1.3.2:
383 |   version "1.4.1"
384 |   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
385 |   integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
386 | 
387 | punycode@^2.1.0:
388 |   version "2.1.1"
389 |   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
390 |   integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
391 | 
392 | range-parser@1.2.0:
393 |   version "1.2.0"
394 |   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
395 |   integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
396 | 
397 | rc@^1.0.1, rc@^1.1.6:
398 |   version "1.2.8"
399 |   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
400 |   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
401 |   dependencies:
402 |     deep-extend "^0.6.0"
403 |     ini "~1.3.0"
404 |     minimist "^1.2.0"
405 |     strip-json-comments "~2.0.1"
406 | 
407 | registry-auth-token@3.3.2:
408 |   version "3.3.2"
409 |   resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
410 |   integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
411 |   dependencies:
412 |     rc "^1.1.6"
413 |     safe-buffer "^5.0.1"
414 | 
415 | registry-url@3.1.0:
416 |   version "3.1.0"
417 |   resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
418 |   integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
419 |   dependencies:
420 |     rc "^1.0.1"
421 | 
422 | safe-buffer@5.1.2:
423 |   version "5.1.2"
424 |   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
425 |   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
426 | 
427 | safe-buffer@^5.0.1:
428 |   version "5.2.1"
429 |   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
430 |   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
431 | 
432 | serve-handler@6.1.3:
433 |   version "6.1.3"
434 |   resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8"
435 |   integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==
436 |   dependencies:
437 |     bytes "3.0.0"
438 |     content-disposition "0.5.2"
439 |     fast-url-parser "1.1.3"
440 |     mime-types "2.1.18"
441 |     minimatch "3.0.4"
442 |     path-is-inside "1.0.2"
443 |     path-to-regexp "2.2.1"
444 |     range-parser "1.2.0"
445 | 
446 | serve@^11.3.2:
447 |   version "11.3.2"
448 |   resolved "https://registry.yarnpkg.com/serve/-/serve-11.3.2.tgz#b905e980616feecd170e51c8f979a7b2374098f5"
449 |   integrity sha512-yKWQfI3xbj/f7X1lTBg91fXBP0FqjJ4TEi+ilES5yzH0iKJpN5LjNb1YzIfQg9Rqn4ECUS2SOf2+Kmepogoa5w==
450 |   dependencies:
451 |     "@zeit/schemas" "2.6.0"
452 |     ajv "6.5.3"
453 |     arg "2.0.0"
454 |     boxen "1.3.0"
455 |     chalk "2.4.1"
456 |     clipboardy "1.2.3"
457 |     compression "1.7.3"
458 |     serve-handler "6.1.3"
459 |     update-check "1.5.2"
460 | 
461 | shebang-command@^1.2.0:
462 |   version "1.2.0"
463 |   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
464 |   integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
465 |   dependencies:
466 |     shebang-regex "^1.0.0"
467 | 
468 | shebang-command@^2.0.0:
469 |   version "2.0.0"
470 |   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
471 |   integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
472 |   dependencies:
473 |     shebang-regex "^3.0.0"
474 | 
475 | shebang-regex@^1.0.0:
476 |   version "1.0.0"
477 |   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
478 |   integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
479 | 
480 | shebang-regex@^3.0.0:
481 |   version "3.0.0"
482 |   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
483 |   integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
484 | 
485 | signal-exit@^3.0.0:
486 |   version "3.0.3"
487 |   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
488 |   integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
489 | 
490 | string-width@^2.0.0, string-width@^2.1.1:
491 |   version "2.1.1"
492 |   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
493 |   integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
494 |   dependencies:
495 |     is-fullwidth-code-point "^2.0.0"
496 |     strip-ansi "^4.0.0"
497 | 
498 | strip-ansi@^4.0.0:
499 |   version "4.0.0"
500 |   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
501 |   integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
502 |   dependencies:
503 |     ansi-regex "^3.0.0"
504 | 
505 | strip-eof@^1.0.0:
506 |   version "1.0.0"
507 |   resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
508 |   integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
509 | 
510 | strip-json-comments@~2.0.1:
511 |   version "2.0.1"
512 |   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
513 |   integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
514 | 
515 | supports-color@^5.3.0:
516 |   version "5.5.0"
517 |   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
518 |   integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
519 |   dependencies:
520 |     has-flag "^3.0.0"
521 | 
522 | term-size@^1.2.0:
523 |   version "1.2.0"
524 |   resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
525 |   integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
526 |   dependencies:
527 |     execa "^0.7.0"
528 | 
529 | update-check@1.5.2:
530 |   version "1.5.2"
531 |   resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28"
532 |   integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==
533 |   dependencies:
534 |     registry-auth-token "3.3.2"
535 |     registry-url "3.1.0"
536 | 
537 | uri-js@^4.2.2:
538 |   version "4.4.1"
539 |   resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
540 |   integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
541 |   dependencies:
542 |     punycode "^2.1.0"
543 | 
544 | vary@~1.1.2:
545 |   version "1.1.2"
546 |   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
547 |   integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
548 | 
549 | which@^1.2.9:
550 |   version "1.3.1"
551 |   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
552 |   integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
553 |   dependencies:
554 |     isexe "^2.0.0"
555 | 
556 | which@^2.0.1:
557 |   version "2.0.2"
558 |   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
559 |   integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
560 |   dependencies:
561 |     isexe "^2.0.0"
562 | 
563 | widest-line@^2.0.0:
564 |   version "2.0.1"
565 |   resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"
566 |   integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
567 |   dependencies:
568 |     string-width "^2.1.1"
569 | 
570 | yallist@^2.1.2:
571 |   version "2.1.2"
572 |   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
573 |   integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
574 | 


--------------------------------------------------------------------------------
/download.go:
--------------------------------------------------------------------------------
 1 | package labor
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"io/ioutil"
 6 | 	"net/http"
 7 | 	"syscall/js"
 8 | )
 9 | 
10 | func Download(url string) interface{} {
11 |   res, _ := http.Get(url)
12 | 	b, _ := ioutil.ReadAll(res.Body)
13 | 	fmt.Println(res)
14 | 	body := js.Global().Get("Uint8Array").New(len(b))
15 | 	js.CopyBytesToJS(body, b)
16 | 	return body
17 | }


--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/yisar/labor
2 | 
3 | go 1.16
4 | 


--------------------------------------------------------------------------------
/promise.go:
--------------------------------------------------------------------------------
 1 | package labor
 2 | 
 3 | import (
 4 | 	"syscall/js"
 5 | )
 6 | 
 7 | func NewPromise() (p js.Value, resolve func(interface{}), reject func(interface{})) {
 8 | 	var cbFunc js.Func
 9 | 	cbFunc = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
10 | 		cbFunc.Release()
11 | 
12 | 		resolve = func(value interface{}) {
13 | 			args[0].Invoke(value)
14 | 		}
15 | 
16 | 		reject = func(value interface{}) {
17 | 			args[1].Invoke(value)
18 | 		}
19 | 
20 | 		return js.Undefined()
21 | 	})
22 | 
23 | 	p = js.Global().Get("Promise").New(cbFunc)
24 | 
25 | 	return
26 | }
27 | 
28 | func Await(p js.Value) (js.Value, error) {
29 | 	resCh := make(chan js.Value)
30 | 	var then js.Func
31 | 	then = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
32 | 		resCh <- args[0]
33 | 		return nil
34 | 	})
35 | 	defer then.Release()
36 | 
37 | 	errCh := make(chan error)
38 | 	var catch js.Func
39 | 	catch = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
40 | 		errCh <- js.Error{args[0]}
41 | 		return nil
42 | 	})
43 | 	defer catch.Release()
44 | 
45 | 	p.Call("then", then).Call("catch", catch)
46 | 
47 | 	select {
48 | 	case res := <-resCh:
49 | 		return res, nil
50 | 	case err := <-errCh:
51 | 		return js.Undefined(), err
52 | 	}
53 | }
54 | 


--------------------------------------------------------------------------------
/request.go:
--------------------------------------------------------------------------------
 1 | package labor
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"net/http"
 6 | 	"net/http/httptest"
 7 | 	"syscall/js"
 8 | )
 9 | 
10 | func Request(r js.Value) *http.Request {
11 | 	jsBody := js.Global().Get("Uint8Array").New(Await(r.Call("arrayBuffer")))
12 | 	body := make([]byte, jsBody.Get("length").Int())
13 | 	js.CopyBytesToGo(body, jsBody)
14 | 
15 | 	req := httptest.NewRequest(
16 | 		r.Get("method").String(),
17 | 		r.Get("url").String(),
18 | 		bytes.NewBuffer(body),
19 | 	)
20 | 
21 | 	headersIt := r.Get("headers").Call("entries")
22 | 	for {
23 | 		e := headersIt.Call("next")
24 | 		if e.Get("done").Bool() {
25 | 			break
26 | 		}
27 | 		v := e.Get("value")
28 | 		req.Header.Set(v.Index(0).String(), v.Index(1).String())
29 | 	}
30 | 
31 | 	return req
32 | }
33 | 


--------------------------------------------------------------------------------
/response.go:
--------------------------------------------------------------------------------
 1 | package labor
 2 | 
 3 | import (
 4 | 	"io/ioutil"
 5 | 	"net/http/httptest"
 6 | 	"syscall/js"
 7 | )
 8 | 
 9 | type ResponseRecorder struct {
10 | 	*httptest.ResponseRecorder
11 | }
12 | 
13 | func NewResponseRecorder() ResponseRecorder {
14 | 	return ResponseRecorder{httptest.NewRecorder()}
15 | }
16 | 
17 | var _ js.Wrapper = ResponseRecorder{}
18 | 
19 | func (rr ResponseRecorder) JSValue() js.Value {
20 | 	var res = rr.Result()
21 | 
22 | 	var body js.Value = js.Undefined()
23 | 	if res.ContentLength != 0 {
24 | 		var b, err = ioutil.ReadAll(res.Body)
25 | 		if err != nil {
26 | 			panic(err)
27 | 		}
28 | 		body = js.Global().Get("Uint8Array").New(len(b))
29 | 		js.CopyBytesToJS(body, b)
30 | 	}
31 | 
32 | 	var init = make(map[string]interface{}, 2)
33 | 
34 | 	if res.StatusCode != 0 {
35 | 		init["status"] = res.StatusCode
36 | 	}
37 | 
38 | 	if len(res.Header) != 0 {
39 | 		var headers = make(map[string]interface{}, len(res.Header))
40 | 		for k := range res.Header {
41 | 			headers[k] = res.Header.Get(k)
42 | 		}
43 | 		init["headers"] = headers
44 | 	}
45 | 
46 | 	return js.Global().Get("Response").New(body, init)
47 | }
48 | 


--------------------------------------------------------------------------------
/serve.go:
--------------------------------------------------------------------------------
 1 | package labor
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"net/http"
 6 | 	"strings"
 7 | 	"syscall/js"
 8 | )
 9 | 
10 | func Serve(handler http.Handler) func() {
11 | 	var h = handler
12 | 	if h == nil {
13 | 		h = http.DefaultServeMux
14 | 	}
15 | 
16 | 	var prefix = js.Global().Get("labor").Get("path").String()
17 | 	for strings.HasSuffix(prefix, "/") {
18 | 		prefix = strings.TrimSuffix(prefix, "/")
19 | 	}
20 | 
21 | 	if prefix != "" {
22 | 		var mux = http.NewServeMux()
23 | 		mux.Handle(prefix+"/", http.StripPrefix(prefix, h))
24 | 		h = mux
25 | 	}
26 | 
27 | 	var cb = js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
28 | 		var resPromise, resolve, reject = NewPromise()
29 | 
30 | 		go func() {
31 | 			defer func() {
32 | 				if r := recover(); r != nil {
33 | 					if err, ok := r.(error); ok {
34 | 						reject(fmt.Sprintf("labor: panic: %+v\n", err))
35 | 					} else {
36 | 						reject(fmt.Sprintf("labor: panic: %v\n", r))
37 | 					}
38 | 				}
39 | 			}()
40 | 
41 | 			var res = NewResponseRecorder()
42 | 
43 | 			h.ServeHTTP(res, Request(args[0]))
44 | 
45 | 			resolve(res)
46 | 		}()
47 | 
48 | 		return resPromise
49 | 	})
50 | 
51 | 	js.Global().Get("labor").Call("setHandler", cb)
52 | 
53 | 	return cb.Release
54 | }
55 | 


--------------------------------------------------------------------------------