diff --git a/.eslintignore b/.eslintignore
index 849ddff..2a851e2 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1,2 @@
dist/
+wasm/
diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 832ac6e..c26a41c 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -21,7 +21,7 @@ jobs:
run: |
npm ci
npm run build
- npm run build-server
+ npm run build:server
npm test
env:
CI: true
diff --git a/.gitignore b/.gitignore
index d64c4ca..bf64ce2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
coverage/
dist/
+main.wasm
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..a9f8d70
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "n-dilation-webassembly"]
+ path = n-dilation-webassembly
+ url = git@github.com:acra5y/n-dilation-webassembly.git
diff --git a/app/components/WasmLoader.jsx b/app/components/WasmLoader.jsx
new file mode 100644
index 0000000..44f6976
--- /dev/null
+++ b/app/components/WasmLoader.jsx
@@ -0,0 +1,31 @@
+import { useEffect } from "react";
+
+import { useWindowContext } from "./WindowContext";
+
+const WasmLoader = () => {
+ const window = useWindowContext();
+
+ useEffect(() => {
+ if (!window.Go) {
+ const script = document.createElement("script");
+ script.onload = async function() {
+ const go = new window.Go();
+ const webAssembly = await window.WebAssembly.instantiateStreaming(
+ window.fetch("/public/wasm/main.wasm"),
+ go.importObject
+ );
+ go.run(webAssembly.instance); // populates window.UnitaryNDilation
+ };
+ script.src = "/public/wasm/wasm_exec.js";
+ script.type = "text/javascript";
+
+ document.head.appendChild(script);
+ }
+ }, []);
+
+ return null;
+};
+
+WasmLoader.displayName = "WasmLoader";
+
+export default WasmLoader;
diff --git a/app/components/nDilation/Calculator.jsx b/app/components/nDilation/Calculator.jsx
index 6036802..586968b 100644
--- a/app/components/nDilation/Calculator.jsx
+++ b/app/components/nDilation/Calculator.jsx
@@ -1,61 +1,105 @@
-import React, { useReducer } from "react";
+import React, { useReducer, useEffect, useRef } from "react";
-import fetchNDilation from "../../lib/fetchNDilation";
import { useWindowContext } from "../WindowContext";
import DilationForm from "./DilationForm";
import Result from "./Result";
+import WasmLoader from "../WasmLoader";
-const initialState = { isLoading: false, dilation: null, error: null };
+const initialState = {
+ isLoading: false,
+ dilation: null,
+ validationError: false,
+ runtimeError: false,
+ ready: false,
+};
function reducer(state, action) {
switch (action.type) {
- case "FETCH_START":
- return { isLoading: true, dilation: null, error: null };
- case "FETCH_OK":
- return { isLoading: false, dilation: action.payload, error: null };
- case "FETCH_NOT_OK":
- return { isLoading: false, dilation: null, error: action.payload };
- case "FETCH_ERROR":
- return { isLoading: false, dilation: null, error: action.payload };
+ case "WASM_INITIALIZED":
+ return { ...state, ready: true };
+ case "CALCULATE_START":
+ return {
+ ...state,
+ isLoading: true,
+ dilation: null,
+ runtimeError: false,
+ validationError: false,
+ };
+ case "CALCULATE_OK":
+ return {
+ ...state,
+ isLoading: false,
+ dilation: action.payload,
+ error: null,
+ };
+ case "VALIDATION_ERROR":
+ return {
+ ...state,
+ isLoading: false,
+ dilation: null,
+ runtimeError: false,
+ validationError: true,
+ };
+ case "CALCULATE_ERROR":
+ return {
+ ...state,
+ ready: false,
+ isLoading: false,
+ dilation: null,
+ runtimeError: true,
+ validationError: false,
+ };
default:
return state;
}
}
-const createOnSubmitHandler = (fetch, dispatch) => async (matrix, degree) => {
+const createOnSubmitHandler = (unitaryNDilationAsync, dispatch) => async (
+ matrix,
+ degree
+) => {
try {
- dispatch({ type: "FETCH_START" });
- const response = await fetchNDilation(fetch, matrix, degree);
-
- const body = await response.json();
+ dispatch({ type: "CALCULATE_START" });
+ const { error, value } = await unitaryNDilationAsync(matrix, degree);
- if (response.ok) {
- dispatch({ type: "FETCH_OK", payload: body.value });
+ if (!error) {
+ dispatch({ type: "CALCULATE_OK", payload: value });
} else {
- dispatch({ type: "FETCH_NOT_OK", payload: body });
+ dispatch({ type: "VALIDATION_ERROR", payload: error });
}
} catch (e) {
- dispatch({ type: "FETCH_ERROR", payload: e });
+ dispatch({ type: "CALCULATE_ERROR", payload: e });
}
};
const Calculator = () => {
- const [{ isLoading, dilation, error }, dispatch] = useReducer(
- reducer,
- initialState
- );
+ const [
+ { isLoading, dilation, validationError, runtimeError },
+ dispatch,
+ ] = useReducer(reducer, initialState);
+ const onSubmitHandler = useRef(null);
const window = useWindowContext();
+ useEffect(() => {
+ if (!onSubmitHandler.current) {
+ const unitaryNDilationAsync = (matrix, degree) =>
+ Promise.resolve(window.UnitaryNDilation(matrix, degree));
+ onSubmitHandler.current = createOnSubmitHandler(
+ unitaryNDilationAsync,
+ dispatch
+ );
+ dispatch({ type: "WASM_INITIALIZED" });
+ }
+ }, [window && window.UnitaryNDilation]);
+
return (
-
+
+
diff --git a/app/components/nDilation/ErrorMessage.jsx b/app/components/nDilation/ErrorMessage.jsx
index 84ebcf0..fb93d19 100644
--- a/app/components/nDilation/ErrorMessage.jsx
+++ b/app/components/nDilation/ErrorMessage.jsx
@@ -1,8 +1,13 @@
import React from "react";
-function ErrorMessage({ errorDetails }) {
- if (!errorDetails.validationError) {
- return Ooops, something went terribly wrong 🤯
;
+function ErrorMessage({ validationError }) {
+ if (!validationError) {
+ return (
+
+ Ooops, something went terribly wrong 🤯. You will probably have
+ to reload the page now.
+
+ );
}
return (
diff --git a/app/components/nDilation/Result.jsx b/app/components/nDilation/Result.jsx
index 964690a..3bd10ca 100644
--- a/app/components/nDilation/Result.jsx
+++ b/app/components/nDilation/Result.jsx
@@ -15,7 +15,8 @@ const AnimatedContentTransition = styled.div`
export const Result = ({
isLoading,
dilation,
- errorDetails,
+ validationError,
+ runtimeError,
opacity,
animationTimeInSeconds,
}) => {
@@ -29,9 +30,10 @@ export const Result = ({
) : (
<>
{dilation && }
- {errorDetails && (
-
- )}
+ {validationError ||
+ (runtimeError && (
+
+ ))}
>
)}
diff --git a/app/components/nDilation/test/Calculator.test.jsx b/app/components/nDilation/test/Calculator.test.jsx
index 2c01c3f..1b55d43 100644
--- a/app/components/nDilation/test/Calculator.test.jsx
+++ b/app/components/nDilation/test/Calculator.test.jsx
@@ -1,25 +1,22 @@
import React from "react";
-import { shallow } from "enzyme";
+import { act } from "react-dom/test-utils";
+import { mount, shallow } from "enzyme";
import DilationForm from "../DilationForm";
import Result from "../Result";
import * as WindowContext from "../../WindowContext";
+import * as WasmLoader from "../../WasmLoader";
import Calculator from "../Calculator";
-const createWindow = (
- response = {
- ok: true,
- json: () => Promise.resolve({}),
- },
- fetch = jest.fn(() => Promise.resolve(response))
-) => ({
- fetch,
+const createWindow = (result = {}) => ({
+ UnitaryNDilation: jest.fn(() => result),
});
const render = (window = {}) => {
jest.spyOn(WindowContext, "useWindowContext").mockImplementation(
() => window
);
- return shallow();
+ jest.spyOn(WasmLoader, "default").mockImplementation(() => null);
+ return mount();
};
describe("Calculator", () => {
@@ -27,92 +24,117 @@ describe("Calculator", () => {
jest.spyOn(WindowContext, "useWindowContext").mockImplementation(
() => ({})
);
- const component = render();
+ const component = shallow();
expect(component).toMatchSnapshot();
});
describe("on client", () => {
- it("call fetch when onSubmit on DilationForm is called", async () => {
+ it("call window.UnitaryNDilation when onSubmit on DilationForm is called", async () => {
const window = createWindow();
const component = render(window);
- await component.find(DilationForm).prop("onSubmit")();
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
- expect(window.fetch.mock.calls.length).toEqual(1);
+ expect(window.UnitaryNDilation.mock.calls.length).toEqual(1);
});
it("should set isLoading prop during fetch", async () => {
- const response = {
- ok: false,
- json: () => Promise.resolve({}),
- };
- const window = createWindow(response);
+ const window = createWindow();
const component = render(window);
- response.json = () => {
+ window.UnitaryNDilation = () => {
expect(component.find(Result).prop("isLoading")).toEqual(true);
return Promise.resolve({});
};
expect(component.find(Result).prop("isLoading")).toEqual(false);
- await component.find(DilationForm).prop("onSubmit")();
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
expect(component.find(Result).prop("isLoading")).toEqual(false);
});
it("should render the dilation after is has been fetched", async () => {
const dilation = [1, 2, 3, 4];
- const response = {
- ok: true,
- json: () => Promise.resolve({ value: dilation }),
- };
- const component = render(createWindow(response));
- await component.find(DilationForm).prop("onSubmit")();
+ const component = render(createWindow({ value: dilation }));
+
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
+ component.update();
+
expect(component.find(Result).prop("dilation")).toEqual(dilation);
});
describe("should handle errors", () => {
- it("and not throw an exception if the fetch is not ok", async () => {
- const responseBody = {
- validationError: {
- value: ["value must represent a real contraction"],
- },
- };
- const response = {
- ok: false,
- json: () => Promise.resolve(responseBody),
- };
- const component = render(createWindow(response));
- await component.find(DilationForm).prop("onSubmit")();
- expect(component.find(Result).prop("errorDetails")).toEqual(
- responseBody
+ it("when window.UnitaryNDilation returns an error", async () => {
+ const error = "value must represent a real contraction";
+ const component = render(createWindow({ error }));
+
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
+ component.update();
+
+ expect(component.find(Result).prop("validationError")).toEqual(
+ true
+ );
+ expect(component.find(Result).prop("runtimeError")).toEqual(
+ false
);
});
- it("and catch an exception thrown by fetch", async () => {
- const error = new Error("Mock Error");
- const fetch = jest.fn(() => Promise.reject(error));
- const component = render(createWindow(null, fetch));
- await component.find(DilationForm).prop("onSubmit")();
- expect(component.find(Result).prop("errorDetails")).toEqual(
- error
+ it("when window.UnitaryNDilation throws an exception", async () => {
+ const window = createWindow();
+ window.UnitaryNDilation.mockImplementation(() => {
+ throw new Error("Mock Error");
+ });
+ const component = render(window);
+
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
+ component.update();
+
+ expect(component.find(Result).prop("validationError")).toEqual(
+ false
+ );
+ expect(component.find(Result).prop("runtimeError")).toEqual(
+ true
);
});
- it("and not render dilation from a first fetch if a second fetch throws", async () => {
+ it("and not render dilation from a first calculation if a second calculation throws", async () => {
const window = createWindow();
const component = render(window);
- await component.find(DilationForm).prop("onSubmit")();
- const mockError = new Error("Mock Error");
- window.fetch.mockImplementation(() =>
- Promise.reject(mockError)
- );
- await component.find(DilationForm).prop("onSubmit")();
+
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
+ component.update();
+
+ window.UnitaryNDilation.mockImplementation(() => {
+ throw new Error("Mock Error");
+ });
+
+ await act(async () => {
+ component.find(DilationForm).prop("onSubmit")();
+ });
+ component.update();
+
const result = component.find(Result);
expect(result.prop("dilation")).toBe(null);
- expect(result.prop("errorDetails")).toEqual(mockError);
+ expect(component.find(Result).prop("validationError")).toEqual(
+ false
+ );
+ expect(component.find(Result).prop("runtimeError")).toEqual(
+ true
+ );
});
});
});
diff --git a/app/components/nDilation/test/ErrorMessage.test.jsx b/app/components/nDilation/test/ErrorMessage.test.jsx
index 3468417..36e2d3b 100644
--- a/app/components/nDilation/test/ErrorMessage.test.jsx
+++ b/app/components/nDilation/test/ErrorMessage.test.jsx
@@ -4,7 +4,7 @@ import { shallow } from "enzyme";
import ErrorMessage from "../ErrorMessage";
const defaultProps = {
- errorDetails: "whatever",
+ validationError: false,
};
const render = overrideProps =>
@@ -17,9 +17,9 @@ describe("ErrorMessage", () => {
expect(component).toMatchSnapshot();
});
- it("should render error message for validation errors", () => {
+ it("should render error message for validationError", () => {
const component = render({
- errorDetails: { validationError: "whatever" },
+ validationError: true,
});
expect(component).toMatchSnapshot();
diff --git a/app/components/nDilation/test/Result.test.jsx b/app/components/nDilation/test/Result.test.jsx
index f7cccf4..6d0ea80 100644
--- a/app/components/nDilation/test/Result.test.jsx
+++ b/app/components/nDilation/test/Result.test.jsx
@@ -22,15 +22,14 @@ describe("Result component", () => {
{
props: {
dilation: [1, 2, 3, 4],
- errorDetails: new Error("Test-Error"),
+ runtimeError: true,
},
description:
- "should render correct markup dilation and errorDetail props are defined",
+ "should render correct markup dilation and runtimeError is true",
},
{
- props: { errorDetails: new Error("Test-Error") },
- description:
- "should render correct markup errorDetail props is defined",
+ props: { validationError: true },
+ description: "should render correct markup validationError is true",
},
];
testCases.forEach(({ props, description }) => {
diff --git a/app/components/nDilation/test/__snapshots__/Calculator.test.jsx.snap b/app/components/nDilation/test/__snapshots__/Calculator.test.jsx.snap
index 5f57196..c5c5458 100644
--- a/app/components/nDilation/test/__snapshots__/Calculator.test.jsx.snap
+++ b/app/components/nDilation/test/__snapshots__/Calculator.test.jsx.snap
@@ -2,13 +2,15 @@
exports[`Calculator should render 1`] = `
+
`;
diff --git a/app/components/nDilation/test/__snapshots__/ErrorMessage.test.jsx.snap b/app/components/nDilation/test/__snapshots__/ErrorMessage.test.jsx.snap
index e3c7a09..21f2044 100644
--- a/app/components/nDilation/test/__snapshots__/ErrorMessage.test.jsx.snap
+++ b/app/components/nDilation/test/__snapshots__/ErrorMessage.test.jsx.snap
@@ -2,11 +2,11 @@
exports[`ErrorMessage should render 1`] = `
- Ooops, something went terribly wrong 🤯
+ Ooops, something went terribly wrong 🤯. You will probably have to reload the page now.
`;
-exports[`ErrorMessage should render error message for validation errors 1`] = `
+exports[`ErrorMessage should render error message for validationError 1`] = `
This did not work. Are you sure, your matrix is a square contraction with real numbers? 🤔
diff --git a/app/components/nDilation/test/__snapshots__/Result.test.jsx.snap b/app/components/nDilation/test/__snapshots__/Result.test.jsx.snap
index ccf4d9b..5be017f 100644
--- a/app/components/nDilation/test/__snapshots__/Result.test.jsx.snap
+++ b/app/components/nDilation/test/__snapshots__/Result.test.jsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Result component should render correct markup dilation and errorDetail props are defined 1`] = `
+exports[`Result component should render correct markup dilation and runtimeError is true 1`] = `
<_default
matrixInRowMajorOrder={
@@ -12,9 +12,7 @@ exports[`Result component should render correct markup dilation and errorDetail
]
}
/>
-
+
`;
@@ -33,11 +31,9 @@ exports[`Result component should render correct markup dilation prop is defined
`;
-exports[`Result component should render correct markup errorDetail props is defined 1`] = `
+exports[`Result component should render correct markup validationError is true 1`] = `
-
+
`;
diff --git a/app/lib/fetchNDilation.js b/app/lib/fetchNDilation.js
deleted file mode 100644
index 494271c..0000000
--- a/app/lib/fetchNDilation.js
+++ /dev/null
@@ -1,19 +0,0 @@
-const fetchNDilation = (fetch, value, degree) => {
- return fetch("http://localhost:8080/dilation", {
- method: "POST",
- mode: "cors",
- cache: "no-cache",
- credentials: "same-origin",
- headers: {
- "Content-Type": "application/json",
- },
- redirect: "follow",
- referrer: "no-referrer",
- body: JSON.stringify({
- value,
- degree,
- }),
- });
-};
-
-export default fetchNDilation;
diff --git a/app/lib/test/fetchNDilation.test.js b/app/lib/test/fetchNDilation.test.js
deleted file mode 100644
index 8c4e644..0000000
--- a/app/lib/test/fetchNDilation.test.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import fetchNDilation from "../fetchNDilation";
-
-describe("fetchNDilation", () => {
- const fetch = jest.fn(() => Promise.resolve("whatever"));
- const value = [1, 2, 3, 4];
- const degree = 2;
-
- it("should call fetch with correct url", () => {
- fetchNDilation(fetch, value, degree);
-
- expect(fetch.mock.calls.length).toBe(1);
- expect(fetch.mock.calls[0][0]).toEqual(
- "http://localhost:8080/dilation"
- );
- });
-
- it("should call fetch with correct body (value from params and fixed degree of 2)", () => {
- fetchNDilation(fetch, value, degree);
-
- expect(fetch.mock.calls.length).toBe(1);
- expect(JSON.parse(fetch.mock.calls[0][1].body)).toEqual({
- value: [1, 2, 3, 4],
- degree: 2,
- });
- });
-
- it("should return whatever fetch returns", async () => {
- const result = await fetchNDilation(fetch, value, degree);
-
- expect(result).toEqual("whatever");
- });
-});
diff --git a/n-dilation-webassembly b/n-dilation-webassembly
new file mode 160000
index 0000000..748003f
--- /dev/null
+++ b/n-dilation-webassembly
@@ -0,0 +1 @@
+Subproject commit 748003f1633a9195a6e3a892f7d3c3a0d089542c
diff --git a/package-lock.json b/package-lock.json
index 53aa42e..eaaee43 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1903,12 +1903,27 @@
"dev": true
},
"accepts": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
- "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
- "mime-types": "~2.1.18",
- "negotiator": "0.6.1"
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+ },
+ "mime-types": {
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+ "requires": {
+ "mime-db": "1.44.0"
+ }
+ }
}
},
"acorn": {
@@ -1989,6 +2004,12 @@
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"dev": true
},
+ "ansi-colors": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
+ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
+ "dev": true
+ },
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
@@ -2096,6 +2117,21 @@
"es-abstract": "^1.7.0"
}
},
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -2457,20 +2493,27 @@
"dev": true
},
"body-parser": {
- "version": "1.18.3",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
- "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
- "bytes": "3.0.0",
+ "bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
- "http-errors": "~1.6.3",
- "iconv-lite": "0.4.23",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
- "qs": "6.5.2",
- "raw-body": "2.3.3",
- "type-is": "~1.6.16"
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ }
}
},
"boolbase": {
@@ -2668,9 +2711,9 @@
"dev": true
},
"bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"cacache": {
"version": "12.0.4",
@@ -3058,9 +3101,12 @@
"dev": true
},
"content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
},
"content-type": {
"version": "1.0.4",
@@ -3077,9 +3123,9 @@
}
},
"cookie": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
- "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
@@ -3106,6 +3152,64 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
+ "copy-webpack-plugin": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz",
+ "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==",
+ "dev": true,
+ "requires": {
+ "cacache": "^12.0.3",
+ "find-cache-dir": "^2.1.0",
+ "glob-parent": "^3.1.0",
+ "globby": "^7.1.1",
+ "is-glob": "^4.0.1",
+ "loader-utils": "^1.2.3",
+ "minimatch": "^3.0.4",
+ "normalize-path": "^3.0.0",
+ "p-limit": "^2.2.1",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^2.1.2",
+ "webpack-log": "^2.0.0"
+ },
+ "dependencies": {
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
+ "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,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ }
+ }
+ },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -3451,6 +3555,26 @@
"randombytes": "^2.0.0"
}
},
+ "dir-glob": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+ "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "dev": true,
+ "requires": {
+ "path-type": "^3.0.0"
+ },
+ "dependencies": {
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ }
+ }
+ },
"discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
@@ -4088,40 +4212,47 @@
}
},
"express": {
- "version": "4.16.4",
- "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
- "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
- "accepts": "~1.3.5",
+ "accepts": "~1.3.7",
"array-flatten": "1.1.1",
- "body-parser": "1.18.3",
- "content-disposition": "0.5.2",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
"content-type": "~1.0.4",
- "cookie": "0.3.1",
+ "cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "finalhandler": "1.1.1",
+ "finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
+ "parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.4",
- "qs": "6.5.2",
- "range-parser": "~1.2.0",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
- "send": "0.16.2",
- "serve-static": "1.13.2",
- "setprototypeof": "1.1.0",
- "statuses": "~1.4.0",
- "type-is": "~1.6.16",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ }
}
},
"extend": {
@@ -4335,16 +4466,16 @@
}
},
"finalhandler": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
- "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
- "statuses": "~1.4.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
@@ -5149,6 +5280,34 @@
"integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
"dev": true
},
+ "globby": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+ "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "dir-glob": "^2.0.0",
+ "glob": "^7.1.2",
+ "ignore": "^3.3.5",
+ "pify": "^3.0.0",
+ "slash": "^1.0.0"
+ },
+ "dependencies": {
+ "ignore": {
+ "version": "3.3.10",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+ "dev": true
+ },
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ }
+ }
+ },
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@@ -5327,14 +5486,15 @@
}
},
"http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
}
},
"http-signature": {
@@ -5361,9 +5521,9 @@
"dev": true
},
"iconv-lite": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
- "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
@@ -5522,9 +5682,9 @@
"dev": true
},
"ipaddr.js": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
- "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"is-accessor-descriptor": {
"version": "0.1.6",
@@ -8447,19 +8607,21 @@
}
},
"mime": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
- "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.38.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
- "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg=="
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
+ "dev": true
},
"mime-types": {
"version": "2.1.22",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
"integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
+ "dev": true,
"requires": {
"mime-db": "~1.38.0"
}
@@ -8630,9 +8792,9 @@
}
},
"negotiator": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
- "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"neo-async": {
"version": "2.6.1",
@@ -9097,9 +9259,9 @@
"dev": true
},
"parseurl": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
- "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"pascalcase": {
"version": "0.1.1",
@@ -9428,12 +9590,12 @@
}
},
"proxy-addr": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
- "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
- "ipaddr.js": "1.8.0"
+ "ipaddr.js": "1.9.1"
}
},
"prr": {
@@ -9510,7 +9672,8 @@
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
},
"querystring": {
"version": "0.2.0",
@@ -9569,41 +9732,40 @@
}
},
"range-parser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
- "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
- "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
- "bytes": "3.0.0",
- "http-errors": "1.6.3",
- "iconv-lite": "0.4.23",
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"react": {
- "version": "16.8.3",
- "resolved": "https://registry.npmjs.org/react/-/react-16.8.3.tgz",
- "integrity": "sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==",
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
+ "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.13.3"
+ "prop-types": "^15.6.2"
}
},
"react-dom": {
- "version": "16.8.3",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.3.tgz",
- "integrity": "sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==",
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
+ "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
- "scheduler": "^0.13.3"
+ "scheduler": "^0.19.1"
}
},
"react-is": {
@@ -10127,9 +10289,9 @@
}
},
"scheduler": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.3.tgz",
- "integrity": "sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ==",
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
+ "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -10153,9 +10315,9 @@
"dev": true
},
"send": {
- "version": "0.16.2",
- "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
- "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
@@ -10164,12 +10326,19 @@
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
- "http-errors": "~1.6.2",
- "mime": "1.4.1",
- "ms": "2.0.0",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
"on-finished": "~2.3.0",
- "range-parser": "~1.2.0",
- "statuses": "~1.4.0"
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
}
},
"serialize-javascript": {
@@ -10179,14 +10348,14 @@
"dev": true
},
"serve-static": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
- "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
- "parseurl": "~1.3.2",
- "send": "0.16.2"
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
}
},
"set-blocking": {
@@ -10225,9 +10394,9 @@
"dev": true
},
"setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"sha.js": {
"version": "2.4.11",
@@ -10561,9 +10730,9 @@
}
},
"statuses": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
- "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stealthy-require": {
"version": "1.1.1",
@@ -11054,6 +11223,11 @@
"repeat-string": "^1.6.1"
}
},
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
"tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
@@ -11140,12 +11314,27 @@
"dev": true
},
"type-is": {
- "version": "1.6.16",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
- "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
- "mime-types": "~2.1.18"
+ "mime-types": "~2.1.24"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+ },
+ "mime-types": {
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+ "requires": {
+ "mime-db": "1.44.0"
+ }
+ }
}
},
"typedarray": {
@@ -11640,6 +11829,16 @@
}
}
},
+ "webpack-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^3.0.0",
+ "uuid": "^3.3.2"
+ }
+ },
"webpack-node-externals": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz",
diff --git a/package.json b/package.json
index cbef8ae..12dfdf2 100644
--- a/package.json
+++ b/package.json
@@ -8,9 +8,10 @@
"main": "index.js",
"scripts": {
"start": "NODE_ENV=production node ./dist/server.js",
- "dev": "./node_modules/.bin/concurrently --kill-others \"npm run build-dev-server\" \"npm run build-dev\" \"./node_modules/.bin/node-dev ./dist/server.js\"",
- "build-dev-server": "webpack --mode development --config server.production.js --watch",
- "build-server": "webpack --mode production --config server.production.js",
+ "dev": "npm run build:wasm && ./node_modules/.bin/concurrently --kill-others \"npm run build:dev-server\" \"npm run build-dev\" \"./node_modules/.bin/node-dev ./dist/server.js\"",
+ "build:wasm": "cd ./n-dilation-webassembly && GOOS=js GOARCH=wasm go build -o ../wasm/main.wasm",
+ "build:dev-server": "webpack --mode development --config server.production.js --watch",
+ "build:server": "webpack --mode production --config server.production.js",
"build-dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"lint": "./node_modules/.bin/eslint . --ext .jsx --ext .js",
@@ -34,6 +35,7 @@
"babel-loader": "^8.0.5",
"babel-plugin-styled-components": "^1.10.6",
"concurrently": "^4.1.0",
+ "copy-webpack-plugin": "^5.1.1",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.12.1",
"enzyme-to-json": "^3.3.5",
@@ -48,9 +50,9 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {
- "express": "^4.16.4",
- "react": "^16.8.3",
- "react-dom": "^16.8.3",
+ "express": "^4.17.1",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
"react-textarea-autosize": "^7.1.0",
"styled-components": "^4.3.2",
"webpack-node-externals": "^1.7.2"
diff --git a/wasm/wasm_exec.js b/wasm/wasm_exec.js
new file mode 100644
index 0000000..a54bb9a
--- /dev/null
+++ b/wasm/wasm_exec.js
@@ -0,0 +1,533 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(() => {
+ // Map multiple JavaScript environments to a single common API,
+ // preferring web standards over Node.js API.
+ //
+ // Environments considered:
+ // - Browsers
+ // - Node.js
+ // - Electron
+ // - Parcel
+
+ if (typeof global !== "undefined") {
+ // global already exists
+ } else if (typeof window !== "undefined") {
+ window.global = window;
+ } else if (typeof self !== "undefined") {
+ self.global = self;
+ } else {
+ throw new Error("cannot export Go (neither global, window nor self is defined)");
+ }
+
+ if (!global.require && typeof require !== "undefined") {
+ global.require = require;
+ }
+
+ if (!global.fs && global.require) {
+ global.fs = require("fs");
+ }
+
+ if (!global.fs) {
+ let outputBuf = "";
+ global.fs = {
+ constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
+ writeSync(fd, buf) {
+ outputBuf += decoder.decode(buf);
+ const nl = outputBuf.lastIndexOf("\n");
+ if (nl != -1) {
+ console.log(outputBuf.substr(0, nl));
+ outputBuf = outputBuf.substr(nl + 1);
+ }
+ return buf.length;
+ },
+ write(fd, buf, offset, length, position, callback) {
+ if (offset !== 0 || length !== buf.length || position !== null) {
+ throw new Error("not implemented");
+ }
+ const n = this.writeSync(fd, buf);
+ callback(null, n);
+ },
+ open(path, flags, mode, callback) {
+ const err = new Error("not implemented");
+ err.code = "ENOSYS";
+ callback(err);
+ },
+ read(fd, buffer, offset, length, position, callback) {
+ const err = new Error("not implemented");
+ err.code = "ENOSYS";
+ callback(err);
+ },
+ fsync(fd, callback) {
+ callback(null);
+ },
+ };
+ }
+
+ if (!global.crypto) {
+ const nodeCrypto = require("crypto");
+ global.crypto = {
+ getRandomValues(b) {
+ nodeCrypto.randomFillSync(b);
+ },
+ };
+ }
+
+ if (!global.performance) {
+ global.performance = {
+ now() {
+ const [sec, nsec] = process.hrtime();
+ return sec * 1000 + nsec / 1000000;
+ },
+ };
+ }
+
+ if (!global.TextEncoder) {
+ global.TextEncoder = require("util").TextEncoder;
+ }
+
+ if (!global.TextDecoder) {
+ global.TextDecoder = require("util").TextDecoder;
+ }
+
+ // End of polyfills for common API.
+
+ const encoder = new TextEncoder("utf-8");
+ const decoder = new TextDecoder("utf-8");
+
+ global.Go = class {
+ constructor() {
+ this.argv = ["js"];
+ this.env = {};
+ this.exit = (code) => {
+ if (code !== 0) {
+ console.warn("exit code:", code);
+ }
+ };
+ this._exitPromise = new Promise((resolve) => {
+ this._resolveExitPromise = resolve;
+ });
+ this._pendingEvent = null;
+ this._scheduledTimeouts = new Map();
+ this._nextCallbackTimeoutID = 1;
+
+ const mem = () => {
+ // The buffer may change when requesting more memory.
+ return new DataView(this._inst.exports.mem.buffer);
+ }
+
+ const setInt64 = (addr, v) => {
+ mem().setUint32(addr + 0, v, true);
+ mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
+ }
+
+ const getInt64 = (addr) => {
+ const low = mem().getUint32(addr + 0, true);
+ const high = mem().getInt32(addr + 4, true);
+ return low + high * 4294967296;
+ }
+
+ const loadValue = (addr) => {
+ const f = mem().getFloat64(addr, true);
+ if (f === 0) {
+ return undefined;
+ }
+ if (!isNaN(f)) {
+ return f;
+ }
+
+ const id = mem().getUint32(addr, true);
+ return this._values[id];
+ }
+
+ const storeValue = (addr, v) => {
+ const nanHead = 0x7FF80000;
+
+ if (typeof v === "number") {
+ if (isNaN(v)) {
+ mem().setUint32(addr + 4, nanHead, true);
+ mem().setUint32(addr, 0, true);
+ return;
+ }
+ if (v === 0) {
+ mem().setUint32(addr + 4, nanHead, true);
+ mem().setUint32(addr, 1, true);
+ return;
+ }
+ mem().setFloat64(addr, v, true);
+ return;
+ }
+
+ switch (v) {
+ case undefined:
+ mem().setFloat64(addr, 0, true);
+ return;
+ case null:
+ mem().setUint32(addr + 4, nanHead, true);
+ mem().setUint32(addr, 2, true);
+ return;
+ case true:
+ mem().setUint32(addr + 4, nanHead, true);
+ mem().setUint32(addr, 3, true);
+ return;
+ case false:
+ mem().setUint32(addr + 4, nanHead, true);
+ mem().setUint32(addr, 4, true);
+ return;
+ }
+
+ let ref = this._refs.get(v);
+ if (ref === undefined) {
+ ref = this._values.length;
+ this._values.push(v);
+ this._refs.set(v, ref);
+ }
+ let typeFlag = 0;
+ switch (typeof v) {
+ case "string":
+ typeFlag = 1;
+ break;
+ case "symbol":
+ typeFlag = 2;
+ break;
+ case "function":
+ typeFlag = 3;
+ break;
+ }
+ mem().setUint32(addr + 4, nanHead | typeFlag, true);
+ mem().setUint32(addr, ref, true);
+ }
+
+ const loadSlice = (addr) => {
+ const array = getInt64(addr + 0);
+ const len = getInt64(addr + 8);
+ return new Uint8Array(this._inst.exports.mem.buffer, array, len);
+ }
+
+ const loadSliceOfValues = (addr) => {
+ const array = getInt64(addr + 0);
+ const len = getInt64(addr + 8);
+ const a = new Array(len);
+ for (let i = 0; i < len; i++) {
+ a[i] = loadValue(array + i * 8);
+ }
+ return a;
+ }
+
+ const loadString = (addr) => {
+ const saddr = getInt64(addr + 0);
+ const len = getInt64(addr + 8);
+ return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
+ }
+
+ const timeOrigin = Date.now() - performance.now();
+ this.importObject = {
+ go: {
+ // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
+ // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
+ // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
+ // This changes the SP, thus we have to update the SP used by the imported function.
+
+ // func wasmExit(code int32)
+ "runtime.wasmExit": (sp) => {
+ const code = mem().getInt32(sp + 8, true);
+ this.exited = true;
+ delete this._inst;
+ delete this._values;
+ delete this._refs;
+ this.exit(code);
+ },
+
+ // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+ "runtime.wasmWrite": (sp) => {
+ const fd = getInt64(sp + 8);
+ const p = getInt64(sp + 16);
+ const n = mem().getInt32(sp + 24, true);
+ fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
+ },
+
+ // func nanotime() int64
+ "runtime.nanotime": (sp) => {
+ setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
+ },
+
+ // func walltime() (sec int64, nsec int32)
+ "runtime.walltime": (sp) => {
+ const msec = (new Date).getTime();
+ setInt64(sp + 8, msec / 1000);
+ mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
+ },
+
+ // func scheduleTimeoutEvent(delay int64) int32
+ "runtime.scheduleTimeoutEvent": (sp) => {
+ const id = this._nextCallbackTimeoutID;
+ this._nextCallbackTimeoutID++;
+ this._scheduledTimeouts.set(id, setTimeout(
+ () => {
+ this._resume();
+ while (this._scheduledTimeouts.has(id)) {
+ // for some reason Go failed to register the timeout event, log and try again
+ // (temporary workaround for https://github.com/golang/go/issues/28975)
+ console.warn("scheduleTimeoutEvent: missed timeout event");
+ this._resume();
+ }
+ },
+ getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
+ ));
+ mem().setInt32(sp + 16, id, true);
+ },
+
+ // func clearTimeoutEvent(id int32)
+ "runtime.clearTimeoutEvent": (sp) => {
+ const id = mem().getInt32(sp + 8, true);
+ clearTimeout(this._scheduledTimeouts.get(id));
+ this._scheduledTimeouts.delete(id);
+ },
+
+ // func getRandomData(r []byte)
+ "runtime.getRandomData": (sp) => {
+ crypto.getRandomValues(loadSlice(sp + 8));
+ },
+
+ // func stringVal(value string) ref
+ "syscall/js.stringVal": (sp) => {
+ storeValue(sp + 24, loadString(sp + 8));
+ },
+
+ // func valueGet(v ref, p string) ref
+ "syscall/js.valueGet": (sp) => {
+ const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
+ sp = this._inst.exports.getsp(); // see comment above
+ storeValue(sp + 32, result);
+ },
+
+ // func valueSet(v ref, p string, x ref)
+ "syscall/js.valueSet": (sp) => {
+ Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
+ },
+
+ // func valueIndex(v ref, i int) ref
+ "syscall/js.valueIndex": (sp) => {
+ storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
+ },
+
+ // valueSetIndex(v ref, i int, x ref)
+ "syscall/js.valueSetIndex": (sp) => {
+ Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
+ },
+
+ // func valueCall(v ref, m string, args []ref) (ref, bool)
+ "syscall/js.valueCall": (sp) => {
+ try {
+ const v = loadValue(sp + 8);
+ const m = Reflect.get(v, loadString(sp + 16));
+ const args = loadSliceOfValues(sp + 32);
+ const result = Reflect.apply(m, v, args);
+ sp = this._inst.exports.getsp(); // see comment above
+ storeValue(sp + 56, result);
+ mem().setUint8(sp + 64, 1);
+ } catch (err) {
+ storeValue(sp + 56, err);
+ mem().setUint8(sp + 64, 0);
+ }
+ },
+
+ // func valueInvoke(v ref, args []ref) (ref, bool)
+ "syscall/js.valueInvoke": (sp) => {
+ try {
+ const v = loadValue(sp + 8);
+ const args = loadSliceOfValues(sp + 16);
+ const result = Reflect.apply(v, undefined, args);
+ sp = this._inst.exports.getsp(); // see comment above
+ storeValue(sp + 40, result);
+ mem().setUint8(sp + 48, 1);
+ } catch (err) {
+ storeValue(sp + 40, err);
+ mem().setUint8(sp + 48, 0);
+ }
+ },
+
+ // func valueNew(v ref, args []ref) (ref, bool)
+ "syscall/js.valueNew": (sp) => {
+ try {
+ const v = loadValue(sp + 8);
+ const args = loadSliceOfValues(sp + 16);
+ const result = Reflect.construct(v, args);
+ sp = this._inst.exports.getsp(); // see comment above
+ storeValue(sp + 40, result);
+ mem().setUint8(sp + 48, 1);
+ } catch (err) {
+ storeValue(sp + 40, err);
+ mem().setUint8(sp + 48, 0);
+ }
+ },
+
+ // func valueLength(v ref) int
+ "syscall/js.valueLength": (sp) => {
+ setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
+ },
+
+ // valuePrepareString(v ref) (ref, int)
+ "syscall/js.valuePrepareString": (sp) => {
+ const str = encoder.encode(String(loadValue(sp + 8)));
+ storeValue(sp + 16, str);
+ setInt64(sp + 24, str.length);
+ },
+
+ // valueLoadString(v ref, b []byte)
+ "syscall/js.valueLoadString": (sp) => {
+ const str = loadValue(sp + 8);
+ loadSlice(sp + 16).set(str);
+ },
+
+ // func valueInstanceOf(v ref, t ref) bool
+ "syscall/js.valueInstanceOf": (sp) => {
+ mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
+ },
+
+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
+ "syscall/js.copyBytesToGo": (sp) => {
+ const dst = loadSlice(sp + 8);
+ const src = loadValue(sp + 32);
+ if (!(src instanceof Uint8Array)) {
+ mem().setUint8(sp + 48, 0);
+ return;
+ }
+ const toCopy = src.subarray(0, dst.length);
+ dst.set(toCopy);
+ setInt64(sp + 40, toCopy.length);
+ mem().setUint8(sp + 48, 1);
+ },
+
+ // func copyBytesToJS(dst ref, src []byte) (int, bool)
+ "syscall/js.copyBytesToJS": (sp) => {
+ const dst = loadValue(sp + 8);
+ const src = loadSlice(sp + 16);
+ if (!(dst instanceof Uint8Array)) {
+ mem().setUint8(sp + 48, 0);
+ return;
+ }
+ const toCopy = src.subarray(0, dst.length);
+ dst.set(toCopy);
+ setInt64(sp + 40, toCopy.length);
+ mem().setUint8(sp + 48, 1);
+ },
+
+ "debug": (value) => {
+ console.log(value);
+ },
+ }
+ };
+ }
+
+ async run(instance) {
+ this._inst = instance;
+ this._values = [ // TODO: garbage collection
+ NaN,
+ 0,
+ null,
+ true,
+ false,
+ global,
+ this,
+ ];
+ this._refs = new Map();
+ this.exited = false;
+
+ const mem = new DataView(this._inst.exports.mem.buffer)
+
+ // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
+ let offset = 4096;
+
+ const strPtr = (str) => {
+ const ptr = offset;
+ const bytes = encoder.encode(str + "\0");
+ new Uint8Array(mem.buffer, offset, bytes.length).set(bytes);
+ offset += bytes.length;
+ if (offset % 8 !== 0) {
+ offset += 8 - (offset % 8);
+ }
+ return ptr;
+ };
+
+ const argc = this.argv.length;
+
+ const argvPtrs = [];
+ this.argv.forEach((arg) => {
+ argvPtrs.push(strPtr(arg));
+ });
+
+ const keys = Object.keys(this.env).sort();
+ argvPtrs.push(keys.length);
+ keys.forEach((key) => {
+ argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
+ });
+
+ const argv = offset;
+ argvPtrs.forEach((ptr) => {
+ mem.setUint32(offset, ptr, true);
+ mem.setUint32(offset + 4, 0, true);
+ offset += 8;
+ });
+
+ this._inst.exports.run(argc, argv);
+ if (this.exited) {
+ this._resolveExitPromise();
+ }
+ await this._exitPromise;
+ }
+
+ _resume() {
+ if (this.exited) {
+ throw new Error("Go program has already exited");
+ }
+ this._inst.exports.resume();
+ if (this.exited) {
+ this._resolveExitPromise();
+ }
+ }
+
+ _makeFuncWrapper(id) {
+ const go = this;
+ return function () {
+ const event = { id: id, this: this, args: arguments };
+ go._pendingEvent = event;
+ go._resume();
+ return event.result;
+ };
+ }
+ }
+
+ if (
+ global.require &&
+ global.require.main === module &&
+ global.process &&
+ global.process.versions &&
+ !global.process.versions.electron
+ ) {
+ if (process.argv.length < 3) {
+ console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
+ process.exit(1);
+ }
+
+ const go = new Go();
+ go.argv = process.argv.slice(2);
+ go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
+ go.exit = process.exit;
+ WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
+ process.on("exit", (code) => { // Node.js exits if no event handler is pending
+ if (code === 0 && !go.exited) {
+ // deadlock, make Go print error and stack traces
+ go._pendingEvent = { id: 0 };
+ go._resume();
+ }
+ });
+ return go.run(result.instance);
+ }).catch((err) => {
+ console.error(err);
+ process.exit(1);
+ });
+ }
+})();
diff --git a/webpack.config.js b/webpack.config.js
index c7de736..3142b4d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,4 +1,5 @@
const path = require("path");
+const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./app/index.jsx",
@@ -16,10 +17,15 @@ module.exports = {
use: {
loader: "babel-loader"
}
- }
+ },
]
},
resolve: {
- extensions: [".js", ".jsx"]
- }
+ extensions: [".js", ".jsx",]
+ },
+ plugins: [
+ new CopyPlugin([
+ { from: "wasm", to: "wasm" },
+ ]),
+ ],
};