From 77ce55f48f112f6c6d0ddb1c581fbc87416357c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Fri, 17 Mar 2023 19:54:52 +0100 Subject: [PATCH 1/9] PoC of WASM pretty print of CloudEvents Conflicts fixed: - quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Endpoint.java --- quarkus/pom.xml | 5 + .../knative/showcase/events/Presenter.java | 38 ++++++ .../showcase/events/PrettyPrintWasm.java | 87 +++++++++++++ .../knative/showcase/events/Rest.java | 18 ++- .../openshift/oci/registry/Authorization.java | 18 +++ .../redhat/openshift/oci/registry/Config.java | 21 ++++ .../oci/registry/ContainerRegistry.java | 117 ++++++++++++++++++ .../redhat/openshift/oci/registry/Layer.java | 27 ++++ .../openshift/oci/registry/Manifest.java | 24 ++++ .../redhat/openshift/oci/registry/Quay.java | 11 ++ .../redhat/openshift/oci/registry/Scope.java | 20 +++ .../oci/registry/WasmDownloader.java | 113 +++++++++++++++++ .../com/redhat/openshift/wasm/c/CString.java | 38 ++++++ 13 files changed, 531 insertions(+), 6 deletions(-) create mode 100644 quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java create mode 100644 quarkus/src/main/java/com/redhat/openshift/wasm/c/CString.java diff --git a/quarkus/pom.xml b/quarkus/pom.xml index c547503..fb54eaa 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -105,6 +105,11 @@ frontend main + + io.github.kawamuray.wasmtime + wasmtime-java + 0.14.0 + org.assertj diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java new file mode 100644 index 0000000..da283d9 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java @@ -0,0 +1,38 @@ +package com.redhat.openshift.knative.showcase.events; + +import com.redhat.openshift.oci.registry.WasmDownloader; +import com.redhat.openshift.wasm.c.CString; +import io.cloudevents.CloudEvent; +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.runtime.ShutdownEvent; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; + +@ApplicationScoped +class Presenter { + + private final PrettyPrintWasm wasm; + + @Inject + Presenter(WasmDownloader downloader) { + this.wasm = new PrettyPrintWasm(downloader); + } + + void onStop(@Observes ShutdownEvent ignored) { + wasm.close(); + } + + byte[] toJson(CloudEvent ce) { + var serializer = new JsonFormat(); + return serializer.serialize(ce); + } + + String present(CloudEvent ce) { + var json = toJson(ce); + var input = new CString(json); + var output = wasm.execute(input); + return output.toString(); + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java new file mode 100644 index 0000000..ee490ef --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java @@ -0,0 +1,87 @@ +package com.redhat.openshift.knative.showcase.events; + +import com.redhat.openshift.oci.registry.ContainerRegistry; +import com.redhat.openshift.oci.registry.WasmDownloader; +import com.redhat.openshift.wasm.c.CString; +import io.github.kawamuray.wasmtime.Engine; +import io.github.kawamuray.wasmtime.Func; +import io.github.kawamuray.wasmtime.Linker; +import io.github.kawamuray.wasmtime.Memory; +import io.github.kawamuray.wasmtime.Module; +import io.github.kawamuray.wasmtime.Store; +import io.github.kawamuray.wasmtime.Val; +import io.github.kawamuray.wasmtime.wasi.WasiCtx; +import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; + +import java.nio.file.Files; +import java.nio.file.Path; + +class PrettyPrintWasm implements AutoCloseable { + public static final String MODULE_NAME = "wasm"; + private final WasiCtx wasi; + private final Store store; + private final Linker linker; + private final Engine engine; + private final WasmDownloader downloader; + + PrettyPrintWasm(WasmDownloader downloader) { + this.downloader = downloader; + this.wasi = new WasiCtxBuilder() + .inheritStdout() + .inheritStderr() + .inheritEnv() + .build(); + this.store = Store.withoutData(wasi); + this.engine = store.engine(); + this.linker = new Linker(this.engine); + WasiCtx.addToLinker(linker); + } + + CString execute(CString input) { + Path wasm = ensureWasmModuleIsDownloaded(); + try (var module = Module.fromFile(engine, wasm.toString())) { + if (!linker.modules(store).contains(MODULE_NAME)) { + linker.module(store, MODULE_NAME, module); + } + try(var mem = linker.get(store, MODULE_NAME, "memory").orElseThrow().memory()) { + return executeOnSharedMemory(input, mem); + } + } + } + + private CString executeOnSharedMemory(CString input, Memory mem) { + var buf = mem.buffer(store); + var offset = 0; + input.writeOn(buf, offset); + try (Func ppPrint = linker.get(store, MODULE_NAME, "pp_print").orElseThrow().func()) { + var inputPtr = Val.fromI32(offset); + var results = ppPrint.call(store, inputPtr); + assert results.length == 1; + var result = results[0].i32(); + if (result != 0) { + throw new IllegalArgumentException("pp_print returned " + result); + } + return CString.from(buf, offset); + } + } + + private Path ensureWasmModuleIsDownloaded() { + var repo = new WasmDownloader.Repository("cardil/cloudevents-pretty-print"); + var target = new WasmDownloader.Target(Path.of( + System.getProperty("java.io.tmpdir"), ContainerRegistry.USER_AGENT + )); + var wasm = downloader.computeDownloadPath(repo, target); + if (!Files.exists(wasm)) { + downloader.download(repo, wasm); + } + return wasm; + } + + @Override + public void close() { + linker.close(); + engine.close(); + store.close(); + wasi.close(); + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java index e1a2505..e211e58 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Rest.java @@ -1,12 +1,12 @@ package com.redhat.openshift.knative.showcase.events; import io.cloudevents.CloudEvent; -import io.cloudevents.jackson.JsonFormat; import io.smallrye.mutiny.Multi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; import javax.ws.rs.Path; @Path("") @@ -15,17 +15,24 @@ class Rest implements Endpoint { private static final Logger LOGGER = LoggerFactory.getLogger(Rest.class); private final EventStore events = new EventStore(); + private final Presenter presenter; + + @Inject + Rest(Presenter presenter) { + this.presenter = presenter; + } @Override public Multi events() { return events.stream() - .map(Rest::workaroundQuarkus31587); + .map(this::workaroundQuarkus31587); } @Override public void receive(CloudEvent event) { events.add(event); - LOGGER.debug("Received event: {}", event); + var pretty = presenter.present(event); + LOGGER.info("Received: \n{}", pretty); } @Override @@ -40,8 +47,7 @@ public void receiveOnIndex(CloudEvent event) { * * TODO: Remove this method once the above issues is fixed. */ - private static byte[] workaroundQuarkus31587(CloudEvent event) { - var serializer = new JsonFormat(); - return serializer.serialize(event); + private byte[] workaroundQuarkus31587(CloudEvent event) { + return presenter.toJson(event); } } diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java new file mode 100644 index 0000000..1564cb4 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java @@ -0,0 +1,18 @@ +package com.redhat.openshift.oci.registry; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Authorization { + public final String token; + + @JsonCreator + public Authorization(@JsonProperty("token") String token) { + this.token = token; + } + + @Override + public String toString() { + return "Bearer " + token; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java new file mode 100644 index 0000000..739144b --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java @@ -0,0 +1,21 @@ +package com.redhat.openshift.oci.registry; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Config { + public final String digest; + public final Long size; + public final String mediaType; + + @JsonCreator + public Config( + @JsonProperty("digest") String digest, + @JsonProperty("size") Long size, + @JsonProperty("mediaType") String mediaType + ) { + this.digest = digest; + this.size = size; + this.mediaType = mediaType; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java new file mode 100644 index 0000000..2d82172 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java @@ -0,0 +1,117 @@ +package com.redhat.openshift.oci.registry; + +import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; + +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import java.net.URI; +import java.net.http.HttpRequest; +import java.util.Collections; + +@RegisterClientHeaders(ContainerRegistry.ClientHeadersFactory.class) +public interface ContainerRegistry { + + String USER_AGENT = "OpenShift-OciRegistry/1.0"; + String API_VERSION = "registry/2.0"; + + /** + * TODO: replace the owner and repository parameters with a single repository + * name parameter, when the {@link javax.ws.rs.Encoded} annotation is + * supported by Quarkus Reactive REST Client. + * + * The currenct workaround is to split the repository name into owner and + * repository name. This prevents the use of slashes in the repository name, + * and now supported nested repository names. + * + * The parameter should have been annotated as follows: + *
+   * {@code @Encoded @PathParam("repository") String repository}
+   * 
+ * + * And the method call should have been annotated with: + *
+   * {@code @GET() @Path("/v2/{repository}/manifests/{reference}")}
+   * 
+ */ + @GET() + @Path("/v2/{owner}/{repository}/manifests/{reference}") + Manifest getManifest( + @PathParam("owner") String owner, + @PathParam("repository") String repository, + @PathParam("reference") String reference, + @HeaderParam("Authorization") String token, + @HeaderParam("Accept") String accept + ); + + default Manifest getManifest(String owner, String repository, String reference, String token) { + return getManifest(owner, repository, reference, token, + "application/vnd.oci.image.manifest.v1+json"); + } + + @GET() + @Path("/v2/auth") + Authorization authorize( + @QueryParam("scope") String scope, + @QueryParam("service") String service, + @HeaderParam("Authorization") String token, + @QueryParam("account") String account + ); + + default Authorization authorize(Scope scope, String service) { + return authorize(scope.toString(), service, null, null); + } + + default Authorization authorize(Scope scope) { + return authorize(scope, service()); + } + + String service(); + + @GET() + @Path("/v2/") + Response checkVersion(@HeaderParam("Authorization") String token); + + default Response checkVersion() { + return checkVersion(null); + } + + default HttpRequest.Builder getDigestContentRequest( + String repository, + String digest, + String token + ) { + URI uri = URI.create(String.format( + "https://%s/v2/%s/blobs/%s", + service(), repository, digest + )); + var req = HttpRequest.newBuilder(uri); + var chf = new ClientHeadersFactory(); + var outgoingHeaders = new MultivaluedHashMap<>(Collections.singletonMap( + "Authorization", token + )); + var headers = chf.update(new MultivaluedHashMap<>(), outgoingHeaders); + headers.forEach((k, v) -> v.forEach(h -> req.header(k, h))); + return req; + } + + class ClientHeadersFactory implements org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory { + @Override + public MultivaluedMap update( + MultivaluedMap incomingHeaders, + MultivaluedMap clientOutgoingHeaders + ) { + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.putAll(incomingHeaders); + headers.putAll(clientOutgoingHeaders); + headers.putSingle("Docker-Distribution-API-Version", API_VERSION); + headers.putSingle("User-Agent", USER_AGENT); + return headers; + } + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java new file mode 100644 index 0000000..e0f9741 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java @@ -0,0 +1,27 @@ +package com.redhat.openshift.oci.registry; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; + +public class Layer { + public final String digest; + public final Long size; + public final String mediaType; + public final Map annotations; + + @JsonCreator + public Layer( + @JsonProperty("digest") String digest, + @JsonProperty("size") Long size, + @JsonProperty("mediaType") String mediaType, + @JsonProperty("annotations") Map annotations + ) { + this.digest = digest; + this.size = size; + this.mediaType = mediaType; + this.annotations = new HashMap<>(annotations); + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java new file mode 100644 index 0000000..a9add1b --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java @@ -0,0 +1,24 @@ +package com.redhat.openshift.oci.registry; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; + +public class Manifest { + public final Integer schemaVersion; + public final List layers; + public final Config config; + + @JsonCreator + public Manifest( + @JsonProperty("schemaVersion") Integer schemaVersion, + @JsonProperty("layers") List layers, + @JsonProperty("config") Config config + ) { + this.schemaVersion = schemaVersion; + this.layers = new ArrayList<>(layers); + this.config = config; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java new file mode 100644 index 0000000..22db648 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java @@ -0,0 +1,11 @@ +package com.redhat.openshift.oci.registry; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@RegisterRestClient(baseUri = "https://quay.io") +public interface Quay extends ContainerRegistry { + + default String service() { + return "quay.io"; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java new file mode 100644 index 0000000..4758dca --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java @@ -0,0 +1,20 @@ +package com.redhat.openshift.oci.registry; + +public class Scope { + public final String repository; + public final String action; + + public Scope(String repository) { + this(repository, "pull"); + } + + public Scope(String repository, String action) { + this.repository = repository; + this.action = action; + } + + @Override + public String toString() { + return "repository:" + repository + ":" + action; + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java new file mode 100644 index 0000000..f983567 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java @@ -0,0 +1,113 @@ +package com.redhat.openshift.oci.registry; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response.Status; +import java.io.IOException; +import java.net.http.HttpClient; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +@ApplicationScoped +public class WasmDownloader { + + private static final Logger LOGGER = LoggerFactory.getLogger(WasmDownloader.class); + + public static final String WASM_MEDIA_TYPE = "application/vnd.wasm.content.layer.v1+wasm"; + private final Quay quay; + + @Inject + WasmDownloader(@RestClient Quay quay) { + this.quay = quay; + } + + public Path computeDownloadPath(Repository repository, Target target) { + return target.path.resolve( + repository.name.replace('/', '-') + ".wasm"); + } + + public void download(Repository repository, Path target) { + LOGGER.debug("Downloading quay.io/{} to {}", repository, target); + Authorization auth = null; + //noinspection EmptyTryBlock + try (var ignored = quay.checkVersion()) { + // noop + } catch (WebApplicationException e) { + if (e.getResponse().getStatusInfo().toEnum() == Status.UNAUTHORIZED) { + auth = quay.authorize(new Scope(repository.name, "pull")); + } else { + throw e; + } + } + var token = auth != null ? auth.toString() : null; + // TODO: remove the split when @Encoded is supported in Quarkus Reactive REST Client + var repoCoords = repository.name.split("/", 2); + assert repoCoords.length == 2; + var manifest = quay.getManifest(repoCoords[0], repoCoords[1], repository.version, token); + assert manifest.layers.size() == 1; + var layer = manifest.layers.get(0); + assert Objects.equals(layer.mediaType, WASM_MEDIA_TYPE); + HttpClient client = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.ALWAYS) + .build(); + + var req = quay.getDigestContentRequest(repository.name, layer.digest, token) + .build(); + try { + Files.createDirectories(target.getParent()); + var res = client.send(req, BodyHandlers.ofFile(target)); + var status = Status.fromStatusCode(res.statusCode()); + if (status.getFamily() != Status.Family.SUCCESSFUL) { + throw new IllegalStateException("Unexpected status code: " + res.statusCode()); + } + var actualSize = Files.size(target); + if (actualSize != layer.size) { + Files.delete(target); + throw new IllegalStateException("Unexpected file size: " + actualSize); + } + LOGGER.debug("Successfully downloaded quay.io/{}", repository); + } catch (IOException e) { + throw new IllegalStateException(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } + } + + public static final class Repository { + public final String name; + public final String version; + + public Repository(String name) { + this(name, "latest"); + } + + public Repository(String name, String version) { + this.name = name; + this.version = version; + } + + @Override + public String toString() { + if (version.startsWith("sha256")) { + return name + "@" + version; + } + return name + ":" + version; + } + } + + public static final class Target { + public final Path path; + + public Target(Path path) { + this.path = path; + } + } +} diff --git a/quarkus/src/main/java/com/redhat/openshift/wasm/c/CString.java b/quarkus/src/main/java/com/redhat/openshift/wasm/c/CString.java new file mode 100644 index 0000000..7607405 --- /dev/null +++ b/quarkus/src/main/java/com/redhat/openshift/wasm/c/CString.java @@ -0,0 +1,38 @@ +package com.redhat.openshift.wasm.c; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class CString { + private final byte[] bytes; + + public CString(byte[] bytes) { + this.bytes = bytes; + } + + public CString(String string) { + this.bytes = string.getBytes(StandardCharsets.UTF_8); + } + + public static CString from(ByteBuffer buffer, int offset) { + var bb = ByteBuffer.allocate(1024); + buffer.position(offset); + byte b; + while ((b = buffer.get(offset)) != 0) { + bb.put(b); + offset++; + } + return new CString(bb.array()); + } + + public void writeOn(ByteBuffer buffer, int offset) { + buffer.position(offset); + buffer.put(bytes); + buffer.put((byte) 0); + } + + @Override + public String toString() { + return new String(bytes, StandardCharsets.UTF_8); + } +} From b4022df3bd6c9d64aab0f7965f36e6d95935ceca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Mon, 17 Apr 2023 19:03:31 +0200 Subject: [PATCH 2/9] Using wasm-oci to download WASM from OCI registries --- .vscode/settings.json | 3 + frontend/package-lock.json | 38 ++++- frontend/package.json | 3 +- frontend/scripts/webjar.ts | 161 ++++++++++++++---- quarkus/pom.xml | 5 + .../knative/showcase/events/Presenter.java | 9 +- .../showcase/events/PrettyPrintWasm.java | 62 ++++--- .../openshift/oci/registry/Authorization.java | 18 -- .../redhat/openshift/oci/registry/Config.java | 21 --- .../oci/registry/ContainerRegistry.java | 117 ------------- .../redhat/openshift/oci/registry/Layer.java | 27 --- .../openshift/oci/registry/Manifest.java | 24 --- .../redhat/openshift/oci/registry/Quay.java | 11 -- .../redhat/openshift/oci/registry/Scope.java | 20 --- .../oci/registry/WasmDownloader.java | 113 ------------ 15 files changed, 206 insertions(+), 426 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java delete mode 100644 quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..849f79e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.compile.nullAnalysis.mode": "automatic" +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 410df31..16b3022 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -28,7 +28,8 @@ "adm-zip": "^0.5.10", "colorette": "^2.0.19", "react-scripts": "5.0.1", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "wasm-oci": "^0.1.3" } }, "node_modules/@adobe/css-tools": { @@ -15551,6 +15552,15 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -15611,6 +15621,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -15647,6 +15668,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -15883,6 +15910,15 @@ "makeerror": "1.0.12" } }, + "node_modules/wasm-oci": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/wasm-oci/-/wasm-oci-0.1.3.tgz", + "integrity": "sha512-NaEP1xa0KtyJ4xVAaaRVBZjHWLWlRraON/p5R8c9ObbRgp42VgSCriR7uDUCZ1UOZIEh8HEVs3Imgb4Avw2GLA==", + "dev": true, + "dependencies": { + "typed-rest-client": "^1.8.9" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 951fdbd..d6a1567 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,7 +26,8 @@ "adm-zip": "^0.5.10", "colorette": "^2.0.19", "react-scripts": "5.0.1", - "ts-node": "^10.9.1" + "ts-node": "^10.9.1", + "wasm-oci": "^0.1.3" }, "dependencies": { "@types/node": "^18.15.0", diff --git a/frontend/scripts/webjar.ts b/frontend/scripts/webjar.ts index 1212e43..a51991d 100644 --- a/frontend/scripts/webjar.ts +++ b/frontend/scripts/webjar.ts @@ -1,47 +1,134 @@ import AdmZip from 'adm-zip' -import fs from 'fs' -import { green, yellow } from 'colorette' +import fs from 'fs/promises' +import os from 'os' +import { green, yellow, cyan, magenta, Color, blue, white } from 'colorette' +import { WasmRegistry, Image } from 'wasm-oci' -const coords = { +type Packager = (packer: Packer, log: Logger) => Promise + +interface Packer { + /** + * Adds a file from the disk to the archive. + * @param localPath Path to a file on disk. + * @param zipPath Path to a directory in the archive. Defaults to the empty + * string. + * @param zipName Name for the file. + * @param comment Comment to be attached to the file + */ + addLocalFile(localPath: string, zipPath?: string, zipName?: string, comment?: string): void + /** + * Adds a local directory and all its nested files and directories to the + * archive. + * @param localPath Path to a folder on disk. + * @param zipPath Path to a folder in the archive. Default: `""`. + * @param filter RegExp or Function if files match will be included. + */ + addLocalFolder(localPath: string, zipPath?: string, filter?: (path: string) => boolean): void + /** + * Allows you to create a entry (file or directory) in the zip file. + * If you want to create a directory the `entryName` must end in `"/"` and a `null` + * buffer should be provided. + * @param entryName Entry path. + * @param content Content to add to the entry; must be a 0-length buffer + * for a directory. + * @param comment Comment to add to the entry. + * @param attr Attribute to add to the entry. + */ + addFile(entryName: string, content: Buffer, comment?: string, attr?: number): void +} + +interface Webjar { + group: string + artifact: string + version: string + color: Color, + build: Packager +} + +const webjars: Webjar[] = [{ group: 'com.redhat.openshift.knative.showcase', artifact: 'frontend', version: 'main', + color: cyan, + build: async p => { + p.addLocalFolder('./build', 'META-INF/resources', (path) => { + return !path.includes('index.html') + }) + p.addLocalFile('./build/index.html', 'META-INF/resources', 'home.html') + } +}, { + group: 'com.redhat.openshift.knative.showcase', + artifact: 'cloudevents-pp-wasm', + version: 'main', + color: magenta, + build: async (p, log) => { + const tmpDir = os.tmpdir() + const tmp = await fs.mkdtemp(`${tmpDir}/wasm-oci-`) + const reg = new WasmRegistry(tmp) + const image = Image.parse('quay.io/cardil/cloudevents-pretty-print@sha256:01b30983dda5eb42a8baefb523eb50d7d0e539fb10d7ab9498a2a59f35036afb') + log(`Pulling image: ${green(image.toString())}`) + const wasm = await reg.pull(image) + p.addFile('META-INF/cloudevents-pretty-print.wasm', await fs.readFile(wasm.file), 'Wasm') + await fs.rm(tmp, { recursive: true }) + } +}] + +type Logger = (message?: any, ...optionalParams: any[]) => void + +function createLogger(name: string): Logger { + return (msg) => { + console.log(`[${name}] ${msg}`) + } } -const jarDir = `${process.env.HOME}/.m2/repository/` + - `${coords.group.replace(/\./g, '/')}/` + - `${coords.artifact}/` + coords.version -const jarPath = `${jarDir}/${coords.artifact}-${coords.version}.jar` -const pomPath = `${jarDir}/${coords.artifact}-${coords.version}.pom` - -const pom = ` - - 4.0.0 - ${coords.group} - ${coords.artifact} - ${coords.version} - jar - // Build by: forntend/scripts/webjar.ts script +async function buildWebjar(webjar: Webjar) { + const log = createLogger(webjar.color(webjar.artifact)) + const jarDir = `${process.env.HOME}/.m2/repository/` + + `${webjar.group.replace(/\./g, '/')}/` + + `${webjar.artifact}/` + webjar.version + const jarPath = `${jarDir}/${webjar.artifact}-${webjar.version}.jar` + const pomPath = `${jarDir}/${webjar.artifact}-${webjar.version}.pom` + + const pom = ` + +4.0.0 +${webjar.group} +${webjar.artifact} +${webjar.version} +jar +// Build by: forntend/scripts/webjar.ts script ` + const zip = new AdmZip() + await webjar.build(zip, log) + zip.addFile(`META-INF/maven/${webjar.group}/${webjar.artifact}/pom.xml`, Buffer.from(pom)) + zip.writeZip(jarPath) + log(`Created webjar: ${yellow(jarPath)}`) + await fs.writeFile(pomPath, pom) + log(`Created webjar POM: ${yellow(pomPath)}`) + log('To use it, add following to your pom.xml file:\n' + blue( + ` + + ${white(webjar.group)} + ${white(webjar.artifact)} + ${white(webjar.version)} + + `)) +} + +async function build() { + const ps : Promise[] = [] + for (const webjar of webjars) { + ps.push(buildWebjar(webjar)) + } + await Promise.all(ps) +} -const zip = new AdmZip() -zip.addLocalFolder('./build', 'META-INF/resources', (path) => { - return !path.includes('index.html') -}) -zip.addLocalFile('./build/index.html', 'META-INF/resources', 'home.html') -zip.addFile(`META-INF/maven/${coords.group}/${coords.artifact}/pom.xml`, Buffer.from(pom)) -zip.writeZip(jarPath) -console.log(`Created webjar: ${yellow(jarPath)}`) -fs.writeFileSync(pomPath, pom) -console.log(`Created webjar POM: ${yellow(pomPath)}`) -console.log('\nTo use it, add following to your pom.xml file:\n\n' + green( - ` - - ${coords.group} - ${coords.artifact} - ${coords.version} - -`)) +build() + .catch(e => { + console.error(e) + process.exit(1) + }) + .then(() => process.exit(0)) diff --git a/quarkus/pom.xml b/quarkus/pom.xml index fb54eaa..32cd545 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -105,6 +105,11 @@ frontend main
+ + com.redhat.openshift.knative.showcase + cloudevents-pp-wasm + main + io.github.kawamuray.wasmtime wasmtime-java diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java index da283d9..04bf311 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/Presenter.java @@ -1,6 +1,5 @@ package com.redhat.openshift.knative.showcase.events; -import com.redhat.openshift.oci.registry.WasmDownloader; import com.redhat.openshift.wasm.c.CString; import io.cloudevents.CloudEvent; import io.cloudevents.jackson.JsonFormat; @@ -8,17 +7,11 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Observes; -import javax.inject.Inject; @ApplicationScoped class Presenter { - private final PrettyPrintWasm wasm; - - @Inject - Presenter(WasmDownloader downloader) { - this.wasm = new PrettyPrintWasm(downloader); - } + private final PrettyPrintWasm wasm = new PrettyPrintWasm(); void onStop(@Observes ShutdownEvent ignored) { wasm.close(); diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java index ee490ef..3501887 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java @@ -1,7 +1,5 @@ package com.redhat.openshift.knative.showcase.events; -import com.redhat.openshift.oci.registry.ContainerRegistry; -import com.redhat.openshift.oci.registry.WasmDownloader; import com.redhat.openshift.wasm.c.CString; import io.github.kawamuray.wasmtime.Engine; import io.github.kawamuray.wasmtime.Func; @@ -13,8 +11,7 @@ import io.github.kawamuray.wasmtime.wasi.WasiCtx; import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.Objects; class PrettyPrintWasm implements AutoCloseable { public static final String MODULE_NAME = "wasm"; @@ -22,10 +19,9 @@ class PrettyPrintWasm implements AutoCloseable { private final Store store; private final Linker linker; private final Engine engine; - private final WasmDownloader downloader; + private Module module; - PrettyPrintWasm(WasmDownloader downloader) { - this.downloader = downloader; + PrettyPrintWasm() { this.wasi = new WasiCtxBuilder() .inheritStdout() .inheritStderr() @@ -38,18 +34,37 @@ class PrettyPrintWasm implements AutoCloseable { } CString execute(CString input) { - Path wasm = ensureWasmModuleIsDownloaded(); - try (var module = Module.fromFile(engine, wasm.toString())) { - if (!linker.modules(store).contains(MODULE_NAME)) { - linker.module(store, MODULE_NAME, module); - } - try(var mem = linker.get(store, MODULE_NAME, "memory").orElseThrow().memory()) { - return executeOnSharedMemory(input, mem); - } + loadWasmModule(); + try(var mem = linker.get(store, MODULE_NAME, "memory").orElseThrow().memory()) { + return executeUsingSharedMemory(input, mem); } } - private CString executeOnSharedMemory(CString input, Memory mem) { + private synchronized Module loadWasmModule() { + if (module != null) { + return module; + } + byte[] wasm = loadWasmBinary(); + module = Module.fromBinary(engine, wasm); + if (!linker.modules(store).contains(MODULE_NAME)) { + linker.module(store, MODULE_NAME, module); + } + return module; + } + + private byte[] loadWasmBinary() { + try (var steam = PrettyPrintWasm.class.getResourceAsStream( + "/META-INF/cloudevents-pretty-print.wasm")) { + Objects.requireNonNull(steam, + "cloudevents-pretty-print.wasm not found"); + return steam.readAllBytes(); + } catch (Exception ex) { + throw new RuntimeException( + "Failed to load cloudevents-pretty-print.wasm", ex); + } + } + + private CString executeUsingSharedMemory(CString input, Memory mem) { var buf = mem.buffer(store); var offset = 0; input.writeOn(buf, offset); @@ -65,20 +80,11 @@ private CString executeOnSharedMemory(CString input, Memory mem) { } } - private Path ensureWasmModuleIsDownloaded() { - var repo = new WasmDownloader.Repository("cardil/cloudevents-pretty-print"); - var target = new WasmDownloader.Target(Path.of( - System.getProperty("java.io.tmpdir"), ContainerRegistry.USER_AGENT - )); - var wasm = downloader.computeDownloadPath(repo, target); - if (!Files.exists(wasm)) { - downloader.download(repo, wasm); - } - return wasm; - } - @Override public void close() { + if (module != null) { + module.close(); + } linker.close(); engine.close(); store.close(); diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java deleted file mode 100644 index 1564cb4..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Authorization.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Authorization { - public final String token; - - @JsonCreator - public Authorization(@JsonProperty("token") String token) { - this.token = token; - } - - @Override - public String toString() { - return "Bearer " + token; - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java deleted file mode 100644 index 739144b..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Config.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -public class Config { - public final String digest; - public final Long size; - public final String mediaType; - - @JsonCreator - public Config( - @JsonProperty("digest") String digest, - @JsonProperty("size") Long size, - @JsonProperty("mediaType") String mediaType - ) { - this.digest = digest; - this.size = size; - this.mediaType = mediaType; - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java deleted file mode 100644 index 2d82172..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/ContainerRegistry.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; - -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import java.net.URI; -import java.net.http.HttpRequest; -import java.util.Collections; - -@RegisterClientHeaders(ContainerRegistry.ClientHeadersFactory.class) -public interface ContainerRegistry { - - String USER_AGENT = "OpenShift-OciRegistry/1.0"; - String API_VERSION = "registry/2.0"; - - /** - * TODO: replace the owner and repository parameters with a single repository - * name parameter, when the {@link javax.ws.rs.Encoded} annotation is - * supported by Quarkus Reactive REST Client. - * - * The currenct workaround is to split the repository name into owner and - * repository name. This prevents the use of slashes in the repository name, - * and now supported nested repository names. - * - * The parameter should have been annotated as follows: - *
-   * {@code @Encoded @PathParam("repository") String repository}
-   * 
- * - * And the method call should have been annotated with: - *
-   * {@code @GET() @Path("/v2/{repository}/manifests/{reference}")}
-   * 
- */ - @GET() - @Path("/v2/{owner}/{repository}/manifests/{reference}") - Manifest getManifest( - @PathParam("owner") String owner, - @PathParam("repository") String repository, - @PathParam("reference") String reference, - @HeaderParam("Authorization") String token, - @HeaderParam("Accept") String accept - ); - - default Manifest getManifest(String owner, String repository, String reference, String token) { - return getManifest(owner, repository, reference, token, - "application/vnd.oci.image.manifest.v1+json"); - } - - @GET() - @Path("/v2/auth") - Authorization authorize( - @QueryParam("scope") String scope, - @QueryParam("service") String service, - @HeaderParam("Authorization") String token, - @QueryParam("account") String account - ); - - default Authorization authorize(Scope scope, String service) { - return authorize(scope.toString(), service, null, null); - } - - default Authorization authorize(Scope scope) { - return authorize(scope, service()); - } - - String service(); - - @GET() - @Path("/v2/") - Response checkVersion(@HeaderParam("Authorization") String token); - - default Response checkVersion() { - return checkVersion(null); - } - - default HttpRequest.Builder getDigestContentRequest( - String repository, - String digest, - String token - ) { - URI uri = URI.create(String.format( - "https://%s/v2/%s/blobs/%s", - service(), repository, digest - )); - var req = HttpRequest.newBuilder(uri); - var chf = new ClientHeadersFactory(); - var outgoingHeaders = new MultivaluedHashMap<>(Collections.singletonMap( - "Authorization", token - )); - var headers = chf.update(new MultivaluedHashMap<>(), outgoingHeaders); - headers.forEach((k, v) -> v.forEach(h -> req.header(k, h))); - return req; - } - - class ClientHeadersFactory implements org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory { - @Override - public MultivaluedMap update( - MultivaluedMap incomingHeaders, - MultivaluedMap clientOutgoingHeaders - ) { - MultivaluedMap headers = new MultivaluedHashMap<>(); - headers.putAll(incomingHeaders); - headers.putAll(clientOutgoingHeaders); - headers.putSingle("Docker-Distribution-API-Version", API_VERSION); - headers.putSingle("User-Agent", USER_AGENT); - return headers; - } - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java deleted file mode 100644 index e0f9741..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Layer.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.HashMap; -import java.util.Map; - -public class Layer { - public final String digest; - public final Long size; - public final String mediaType; - public final Map annotations; - - @JsonCreator - public Layer( - @JsonProperty("digest") String digest, - @JsonProperty("size") Long size, - @JsonProperty("mediaType") String mediaType, - @JsonProperty("annotations") Map annotations - ) { - this.digest = digest; - this.size = size; - this.mediaType = mediaType; - this.annotations = new HashMap<>(annotations); - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java deleted file mode 100644 index a9add1b..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Manifest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.ArrayList; -import java.util.List; - -public class Manifest { - public final Integer schemaVersion; - public final List layers; - public final Config config; - - @JsonCreator - public Manifest( - @JsonProperty("schemaVersion") Integer schemaVersion, - @JsonProperty("layers") List layers, - @JsonProperty("config") Config config - ) { - this.schemaVersion = schemaVersion; - this.layers = new ArrayList<>(layers); - this.config = config; - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java deleted file mode 100644 index 22db648..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Quay.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; - -@RegisterRestClient(baseUri = "https://quay.io") -public interface Quay extends ContainerRegistry { - - default String service() { - return "quay.io"; - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java deleted file mode 100644 index 4758dca..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/Scope.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.redhat.openshift.oci.registry; - -public class Scope { - public final String repository; - public final String action; - - public Scope(String repository) { - this(repository, "pull"); - } - - public Scope(String repository, String action) { - this.repository = repository; - this.action = action; - } - - @Override - public String toString() { - return "repository:" + repository + ":" + action; - } -} diff --git a/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java b/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java deleted file mode 100644 index f983567..0000000 --- a/quarkus/src/main/java/com/redhat/openshift/oci/registry/WasmDownloader.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.redhat.openshift.oci.registry; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response.Status; -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpResponse.BodyHandlers; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Objects; - -@ApplicationScoped -public class WasmDownloader { - - private static final Logger LOGGER = LoggerFactory.getLogger(WasmDownloader.class); - - public static final String WASM_MEDIA_TYPE = "application/vnd.wasm.content.layer.v1+wasm"; - private final Quay quay; - - @Inject - WasmDownloader(@RestClient Quay quay) { - this.quay = quay; - } - - public Path computeDownloadPath(Repository repository, Target target) { - return target.path.resolve( - repository.name.replace('/', '-') + ".wasm"); - } - - public void download(Repository repository, Path target) { - LOGGER.debug("Downloading quay.io/{} to {}", repository, target); - Authorization auth = null; - //noinspection EmptyTryBlock - try (var ignored = quay.checkVersion()) { - // noop - } catch (WebApplicationException e) { - if (e.getResponse().getStatusInfo().toEnum() == Status.UNAUTHORIZED) { - auth = quay.authorize(new Scope(repository.name, "pull")); - } else { - throw e; - } - } - var token = auth != null ? auth.toString() : null; - // TODO: remove the split when @Encoded is supported in Quarkus Reactive REST Client - var repoCoords = repository.name.split("/", 2); - assert repoCoords.length == 2; - var manifest = quay.getManifest(repoCoords[0], repoCoords[1], repository.version, token); - assert manifest.layers.size() == 1; - var layer = manifest.layers.get(0); - assert Objects.equals(layer.mediaType, WASM_MEDIA_TYPE); - HttpClient client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - - var req = quay.getDigestContentRequest(repository.name, layer.digest, token) - .build(); - try { - Files.createDirectories(target.getParent()); - var res = client.send(req, BodyHandlers.ofFile(target)); - var status = Status.fromStatusCode(res.statusCode()); - if (status.getFamily() != Status.Family.SUCCESSFUL) { - throw new IllegalStateException("Unexpected status code: " + res.statusCode()); - } - var actualSize = Files.size(target); - if (actualSize != layer.size) { - Files.delete(target); - throw new IllegalStateException("Unexpected file size: " + actualSize); - } - LOGGER.debug("Successfully downloaded quay.io/{}", repository); - } catch (IOException e) { - throw new IllegalStateException(e); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } - } - - public static final class Repository { - public final String name; - public final String version; - - public Repository(String name) { - this(name, "latest"); - } - - public Repository(String name, String version) { - this.name = name; - this.version = version; - } - - @Override - public String toString() { - if (version.startsWith("sha256")) { - return name + "@" + version; - } - return name + ":" + version; - } - } - - public static final class Target { - public final Path path; - - public Target(Path path) { - this.path = path; - } - } -} From bd18b84daf7b2d71b260d966ca6764ac4c9cb7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Tue, 18 Apr 2023 14:17:08 +0200 Subject: [PATCH 3/9] Extract multiple webjars --- expressjs/scripts/extract-webjar.js | 96 ++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 16 deletions(-) diff --git a/expressjs/scripts/extract-webjar.js b/expressjs/scripts/extract-webjar.js index ec78e99..76afb0d 100644 --- a/expressjs/scripts/extract-webjar.js +++ b/expressjs/scripts/extract-webjar.js @@ -3,38 +3,102 @@ const fs = require('fs').promises const chalk = require('chalk') const path = require('path') -const coords = { - group: 'com.redhat.openshift.knative.showcase', - artifact: 'frontend', - version: 'main', +class Webjar { + /** + * Webjar stores the information about the webjar extraction. + * + * @param {Object} vars - The variables to use. + * @param {string} vars.group - The group of the webjar. + * @param {string} vars.artifact - The artifact of the webjar. + * @param {string} vars.version - The version of the webjar. + * @param {string} vars.color - The chalk color func to use. + * @param {string} vars.source - The source directory in webjar to extract. + * @param {string} vars.target - The target directory to extract the webjar to. + */ + constructor({ group, artifact, version, color, source, target }) { + this.group = group + this.artifact = artifact + this.version = version + this.color = color + this.source = source + this.target = target + } } -const jarDir = `${process.env.HOME}/.m2/repository/${coords.group.replace(/\./g, '/')}/${coords.artifact}/${coords.version}` -const jarPath = `${jarDir}/${coords.artifact}-${coords.version}.jar` +const webjars = [ + new Webjar({ + group: 'com.redhat.openshift.knative.showcase', + artifact: 'frontend', + version: 'main', + color: chalk.cyan, + source: 'META-INF/resources', + target: 'public', + }), + new Webjar({ + group: 'com.redhat.openshift.knative.showcase', + artifact: 'cloudevents-pp-wasm', + version: 'main', + color: chalk.magenta, + source: 'META-INF', + target: 'build/wasm', + }), +] + +/** + * Extracts all webjars. + */ +async function extractWebjars() { + let ps = [] + for (const webjar of webjars) { + ps.push(extractWebjar(webjar)) + } + return await Promise.all(ps) +} + +/** + * Creates a log function for the given webjar. + * + * @param {Webjar} webjar + * @returns {(message: string, ...args: any[]) => void} + */ +function createLog(webjar) { + return (...message) => { + console.log(`[${webjar.color(webjar.artifact)}]`, ...message) + } +} + +/** + * Extracts the webjar to the target directory. + * + * @param {Webjar} webjar + */ +async function extractWebjar(webjar) { + const log = createLog(webjar) + const jarDir = `${process.env.HOME}/.m2/repository/${webjar.group.replace(/\./g, '/')}/${webjar.artifact}/${webjar.version}` + const jarPath = `${jarDir}/${webjar.artifact}-${webjar.version}.jar` -const extractWebjar = async () => { const zip = new AdmZip(jarPath) const zipEntries = zip.getEntries() - console.log(`Extracting ${chalk.cyan(jarPath)} to ${chalk.green('public')}\n`) - - console.log(`Deleting ${chalk.red('public')} directory\n`) - await fs.rm('public', { recursive: true, force: true }) + log(`Deleting ${chalk.red(webjar.target)} directory\n`) + await fs.rm(webjar.target, { recursive: true, force: true }) + + log(`Extracting ${chalk.cyan(jarPath)} to ${chalk.green(webjar.target)}\n`) zipEntries.forEach(async zipEntry => { // outputs zip entries information - if (zipEntry.entryName.startsWith('META-INF/resources') && !zipEntry.isDirectory) { - const targetPath = zipEntry.entryName.replace('META-INF/resources', 'public') - console.log(`${chalk.yellow(zipEntry.entryName)} -> ${chalk.green(targetPath)}`) + if (zipEntry.entryName.startsWith(webjar.source) && !zipEntry.isDirectory) { + const targetPath = zipEntry.entryName.replace(webjar.source, webjar.target) + log(`${chalk.yellow(zipEntry.entryName)} -> ${chalk.green(targetPath)}`) const targetDir = path.dirname(targetPath) await fs.mkdir(targetDir, { recursive: true }) await fs.writeFile(targetPath, zipEntry.getData()) } }) - console.log(`\nExtracting the webjar is ${chalk.green('Done')}.\n`) + log(`\nExtracting the webjar is ${chalk.green('Done')}.\n`) } module.exports = () => { - extractWebjar() + extractWebjars() } From 33d820d6daa22258303ede19d7b5c32f205d3f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Tue, 18 Apr 2023 19:25:40 +0200 Subject: [PATCH 4/9] Pretty printing the CE in Express using WASI --- expressjs/jest.config.js | 24 +- expressjs/package-lock.json | 2451 +++++++++++++++-- expressjs/package.json | 8 +- expressjs/scripts/extract-webjar.js | 26 +- expressjs/src/app.js | 8 +- expressjs/src/routes/events/endpoint.js | 106 +- expressjs/src/routes/events/pretty-print.js | 102 + expressjs/test/middleware/health.test.js | 4 +- expressjs/test/middleware/metrics.test.js | 2 +- expressjs/test/middleware/openapi.test.js | 4 +- expressjs/test/routes/events/endpoint.test.js | 4 +- expressjs/test/routes/hello/endpoint.test.js | 4 +- expressjs/test/routes/home/endpoint.test.js | 4 +- expressjs/test/routes/info/endpoint.test.js | 2 +- .../showcase/events/PrettyPrintWasm.java | 2 +- 15 files changed, 2412 insertions(+), 339 deletions(-) create mode 100644 expressjs/src/routes/events/pretty-print.js diff --git a/expressjs/jest.config.js b/expressjs/jest.config.js index 20be940..88df235 100644 --- a/expressjs/jest.config.js +++ b/expressjs/jest.config.js @@ -1,13 +1,19 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ module.exports = { - - /** - * Using Jest and testing under Node.JS env with axios+nock - * - * Ref: https://github.com/axios/axios/issues/305#issuecomment-602032195 - */ - testEnvironment: 'node', - verbose: true, - coverageDirectory: 'build/coverage', + projects: [{ + displayName: 'test', + testEnvironment: 'node', + }, { + displayName: 'lint', + runner: 'jest-runner-eslint', + testMatch: [ + '/src/**/*.js', + '/test/**/*.js', + '/scripts/**/*.ts', + ], + }] } diff --git a/expressjs/package-lock.json b/expressjs/package-lock.json index 5a248fe..92c859b 100644 --- a/expressjs/package-lock.json +++ b/expressjs/package-lock.json @@ -10,6 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@unleash/express-openapi": "^0.2.2", + "@wasmer/wasi": "^1.2.2", "axios": "^0.25.0", "cloudevents": "^6.0.4", "dotenv": "^14.3.2", @@ -31,6 +32,7 @@ "git-describe": "^4.1.1", "invoke-script": "^1.3.0", "jest": "^27.5.1", + "jest-runner-eslint": "^2.0.0", "nock": "^13.3.0", "nodemon": "^2.0.21", "shelljs": "^0.8.5", @@ -919,6 +921,358 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "jest-get-type": "^28.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect-utils/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/expect/node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/expect/node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/expect/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@jest/fake-timers": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", @@ -994,24 +1348,18 @@ } } }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@sinclair/typebox": "^0.24.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, "node_modules/@jest/source-map": { @@ -1205,6 +1553,14 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -1327,6 +1683,12 @@ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "node_modules/@types/prettier": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", @@ -1497,6 +1859,11 @@ "swagger-ui-dist": "^4.10.3" } }, + "node_modules/@wasmer/wasi": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@wasmer/wasi/-/wasi-1.2.2.tgz", + "integrity": "sha512-39ZB3gefOVhBmkhf7Ta79RRSV/emIV8LhdvcWhP/MOZEjMmtzoZWMzt7phdKj8CUXOze+AwbvGK60lKaKldn1w==" + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -2360,6 +2727,22 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2555,6 +2938,18 @@ "node": ">=8" } }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "14.3.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-14.3.2.tgz", @@ -3432,10 +3827,30 @@ "semver": "bin/semver" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { "is-glob": "^4.0.3" @@ -3883,6 +4298,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -4180,26 +4604,6 @@ } } }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -4419,80 +4823,782 @@ } } }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner-eslint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-runner-eslint/-/jest-runner-eslint-2.0.0.tgz", + "integrity": "sha512-7dQTbRxOhw8t+AQSEXtwezfgVomzME+enbjeWN2Emdr3FjFjJW15FLjj33GvKk/r3zq/nASihoaUVTptdBEBHA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "create-jest-runner": "^0.11.2", + "dot-prop": "^5.3.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": "^7 || ^8", + "jest": "^27 || ^28 || ^29" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runner-eslint/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner-eslint/node_modules/create-jest-runner": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/create-jest-runner/-/create-jest-runner-0.11.2.tgz", + "integrity": "sha512-6lwspphs4M1PLKV9baBNxHQtWVBPZuDU8kAP4MyrVWa6aEpEcpi2HZeeA6WncwaqgsGNXpP0N2STS7XNM/nHKQ==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "jest-worker": "^28.0.2", + "throat": "^6.0.1" + }, + "bin": { + "create-jest-runner": "generator/index.js" + }, + "peerDependencies": { + "@jest/test-result": "^28.0.0", + "jest-runner": "^28.0.0" + }, + "peerDependenciesMeta": { + "@jest/test-result": { + "optional": true + }, + "jest-runner": { + "optional": true + } + } + }, + "node_modules/jest-runner-eslint/node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-runner-eslint/node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "node_modules/jest-runner-eslint/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "node_modules/jest-runner-eslint/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner-eslint/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "node_modules/jest-runner-eslint/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/jest-runtime": { @@ -4528,26 +5634,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jest-serializer": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", @@ -5756,6 +6842,14 @@ "node": ">= 0.8" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "optional": true, + "peer": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5896,26 +6990,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/router": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/router/-/router-1.3.7.tgz", @@ -6194,26 +7268,6 @@ "node": ">=4" } }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -6549,26 +7603,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7120,6 +8154,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7841,16 +8884,310 @@ "strip-ansi": "^6.0.0" } }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "dependencies": { + "@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "optional": true, + "peer": true + }, + "expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true + }, + "jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "optional": true, + "peer": true + }, + "jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + } + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + } + } + }, + "@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", "dev": true, + "optional": true, + "peer": true, "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" + "jest-get-type": "^28.0.2" + }, + "dependencies": { + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true + } } }, "@jest/fake-timers": { @@ -7909,22 +9246,17 @@ "string-length": "^4.0.1", "terminal-link": "^2.0.0", "v8-to-istanbul": "^8.1.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@sinclair/typebox": "^0.24.1" } }, "@jest/source-map": { @@ -8085,6 +9417,14 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true, + "optional": true, + "peer": true + }, "@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -8204,6 +9544,12 @@ "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/prettier": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", @@ -8327,6 +9673,11 @@ "swagger-ui-dist": "^4.10.3" } }, + "@wasmer/wasi": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@wasmer/wasi/-/wasi-1.2.2.tgz", + "integrity": "sha512-39ZB3gefOVhBmkhf7Ta79RRSV/emIV8LhdvcWhP/MOZEjMmtzoZWMzt7phdKj8CUXOze+AwbvGK60lKaKldn1w==" + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -8983,6 +10334,19 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -9134,6 +10498,15 @@ } } }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, "dotenv": { "version": "14.3.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-14.3.2.tgz", @@ -9789,6 +11162,20 @@ } } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -10107,6 +11494,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -10320,22 +11713,6 @@ "pretty-format": "^27.5.1", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "jest-diff": { @@ -10573,6 +11950,578 @@ "throat": "^6.0.1" } }, + "jest-runner-eslint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-runner-eslint/-/jest-runner-eslint-2.0.0.tgz", + "integrity": "sha512-7dQTbRxOhw8t+AQSEXtwezfgVomzME+enbjeWN2Emdr3FjFjJW15FLjj33GvKk/r3zq/nASihoaUVTptdBEBHA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "create-jest-runner": "^0.11.2", + "dot-prop": "^5.3.0" + }, + "dependencies": { + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + } + }, + "@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + } + }, + "@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "optional": true, + "peer": true + }, + "create-jest-runner": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/create-jest-runner/-/create-jest-runner-0.11.2.tgz", + "integrity": "sha512-6lwspphs4M1PLKV9baBNxHQtWVBPZuDU8kAP4MyrVWa6aEpEcpi2HZeeA6WncwaqgsGNXpP0N2STS7XNM/nHKQ==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "jest-worker": "^28.0.2", + "throat": "^6.0.1" + } + }, + "diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "optional": true, + "peer": true + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "optional": true, + "peer": true + }, + "expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "optional": true, + "peer": true + }, + "jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*" + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "optional": true, + "peer": true + }, + "jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + } + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + } + } + }, "jest-runtime": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", @@ -10601,22 +12550,6 @@ "jest-util": "^27.5.1", "slash": "^3.0.0", "strip-bom": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "jest-serializer": { @@ -11536,6 +13469,14 @@ } } }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "optional": true, + "peer": true + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -11630,22 +13571,6 @@ "dev": true, "requires": { "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "router": { @@ -11861,22 +13786,6 @@ "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "side-channel": { @@ -12134,22 +14043,6 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "text-table": { @@ -12561,6 +14454,12 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/expressjs/package.json b/expressjs/package.json index cdb8ee1..9ed37e8 100644 --- a/expressjs/package.json +++ b/expressjs/package.json @@ -5,14 +5,14 @@ "main": "src/index.js", "scripts": { "start": "npm run build:prepare && nodemon src/index", - "test": "npm run lint && npm run test:jest:watch", + "test": "npm run test:jest:watch", "build": "npm run test:ci", "lint": "npm run lint:eslint", "serve": "npm run build:prepare && bpm run serve:only", "serve:only": "node src/index", "test:ci": "npm run build:prepare && npm run lint && npm run test:jest", - "test:jest:watch": "npm run build:prepare && jest --watch test/**", - "test:jest": "npm run build:prepare && jest --detectOpenHandles --coverage test/**", + "test:jest:watch": "npm run build:prepare && jest --watch", + "test:jest": "npm run build:prepare && jest --detectOpenHandles --coverage", "lint:eslint": "eslint .", "build:prepare": "npm run build:cache-git && npm run build:extract-webjar", "build:cache-git": "invoke-script scripts/cache-git-describe", @@ -38,6 +38,7 @@ "homepage": "https://github.com/openshift-knative/showcase#readme", "dependencies": { "@unleash/express-openapi": "^0.2.2", + "@wasmer/wasi": "^1.2.2", "axios": "^0.25.0", "cloudevents": "^6.0.4", "dotenv": "^14.3.2", @@ -59,6 +60,7 @@ "git-describe": "^4.1.1", "invoke-script": "^1.3.0", "jest": "^27.5.1", + "jest-runner-eslint": "^2.0.0", "nock": "^13.3.0", "nodemon": "^2.0.21", "shelljs": "^0.8.5", diff --git a/expressjs/scripts/extract-webjar.js b/expressjs/scripts/extract-webjar.js index 76afb0d..187a4e2 100644 --- a/expressjs/scripts/extract-webjar.js +++ b/expressjs/scripts/extract-webjar.js @@ -4,9 +4,10 @@ const chalk = require('chalk') const path = require('path') class Webjar { + /** * Webjar stores the information about the webjar extraction. - * + * * @param {Object} vars - The variables to use. * @param {string} vars.group - The group of the webjar. * @param {string} vars.artifact - The artifact of the webjar. @@ -46,7 +47,7 @@ const webjars = [ /** * Extracts all webjars. - */ + */ async function extractWebjars() { let ps = [] for (const webjar of webjars) { @@ -55,11 +56,17 @@ async function extractWebjars() { return await Promise.all(ps) } +/** + * @callback Log + * @param {string} message + * @param {...any} args + */ + /** * Creates a log function for the given webjar. - * - * @param {Webjar} webjar - * @returns {(message: string, ...args: any[]) => void} + * + * @param {Webjar} webjar + * @returns {Log} */ function createLog(webjar) { return (...message) => { @@ -69,8 +76,8 @@ function createLog(webjar) { /** * Extracts the webjar to the target directory. - * - * @param {Webjar} webjar + * + * @param {Webjar} webjar */ async function extractWebjar(webjar) { const log = createLog(webjar) @@ -88,7 +95,8 @@ async function extractWebjar(webjar) { zipEntries.forEach(async zipEntry => { // outputs zip entries information if (zipEntry.entryName.startsWith(webjar.source) && !zipEntry.isDirectory) { - const targetPath = zipEntry.entryName.replace(webjar.source, webjar.target) + const targetPath = zipEntry.entryName + .replace(webjar.source, webjar.target) log(`${chalk.yellow(zipEntry.entryName)} -> ${chalk.green(targetPath)}`) const targetDir = path.dirname(targetPath) @@ -96,7 +104,7 @@ async function extractWebjar(webjar) { await fs.writeFile(targetPath, zipEntry.getData()) } }) - log(`\nExtracting the webjar is ${chalk.green('Done')}.\n`) + log(`Extracting the webjar is ${chalk.green('Done')}.\n`) } module.exports = () => { diff --git a/expressjs/src/app.js b/expressjs/src/app.js index 74a76cb..e6eb792 100644 --- a/expressjs/src/app.js +++ b/expressjs/src/app.js @@ -17,7 +17,7 @@ const routes = { events: require('./routes/events/endpoint'), } -const loaders = app => { +const loaders = async app => { // Middleware Functions middleware.logging(app) middleware.public(app) @@ -30,16 +30,16 @@ const loaders = app => { routes.home(app) routes.info(app) routes.hello(app) - routes.events(app) + await routes.events(app) } -const createApp = () => { +const createApp = async () => { dotenv.config() const ex = express() // Start initializing - loaders(ex) + await loaders(ex) return ex } diff --git a/expressjs/src/routes/events/endpoint.js b/expressjs/src/routes/events/endpoint.js index c5763ee..af34bde 100644 --- a/expressjs/src/routes/events/endpoint.js +++ b/expressjs/src/routes/events/endpoint.js @@ -1,36 +1,90 @@ const { HTTP } = require('cloudevents') const openapi = require('../../lib/openapi') const EventStore = require('./store') +const { PrinterFactory } = require('./pretty-print') const devdata = require('./devdata') -const store = new EventStore() -devdata.forEach(event => store.add(event)) +const printerFactory = new PrinterFactory() -module.exports = async app => { - app.get('/events', streamDoc, (_, res) => { - res.set('Content-Type', 'text/event-stream') - res.set('Cache-Control', 'no-cache') - res.set('Connection', 'keep-alive') - res.set('X-SSE-Content-Type', 'application/cloudevents+json') - res.set('transfer-encoding', 'chunked') - res.flushHeaders() +/** + * @typedef {import('./pretty-print').Printer} Printer + * @typedef {import('express').Express} Express + * @typedef {import('express').Request} Request + * @typedef {import('express').Response} Response + */ - const stream = store.createStream(res) - stream.stream() - }) +/** + * @type {Printer} + */ +let printer - app.post('/events', eventDoc, (req, res) => { - try { - const ce = HTTP.toEvent({ headers: req.headers, body: req.body }) - ce.validate() - store.add(ce) - res.status(201).end() - } catch (err) { - console.error(err) - res.status(500) - res.json(err) - } - }) +/** + * @type {EventStore} + */ +let store + +/** + * Initializes the routes. + * + * @param {Express} app - the Express app + */ +async function events(app) { + printer = await printerFactory.create() + + app.get('/events', streamDoc, stream) + app.post('/', eventDoc, recv) + app.post('/events', eventDoc, recv) + + store = new EventStore() + devdata.forEach(event => recvEvent(event)) +} + +/** + * Streams all registered CloudEvents. + * + * @param {Request} _ + * @param {Response} res - the HTTP response + */ +function stream(_, res) { + res.set('Content-Type', 'text/event-stream') + res.set('Cache-Control', 'no-cache') + res.set('Connection', 'keep-alive') + res.set('X-SSE-Content-Type', 'application/cloudevents+json') + res.set('transfer-encoding', 'chunked') + res.flushHeaders() + + const str = store.createStream(res) + str.stream() +} + +/** + * Receives a CloudEvent from an HTTP request. + * @param {Request} req - the HTTP request + * @param {Response} res - the HTTP response + * @returns {void} + */ +function recv(req, res) { + try { + const ce = HTTP.toEvent({ headers: req.headers, body: req.body }) + recvEvent(ce) + res.status(201).end() + } catch (err) { + console.error(err) + res.status(500) + res.json(err) + } +} + +/** + * Receives a CloudEvent, logs, and stores it. + * + * @param {CloudEvent} ce - the CloudEvent to receive + */ +function recvEvent(ce) { + ce.validate() + store.add(ce) + const out = printer.print(ce) + console.log('Received:\n', out) } const streamDoc = openapi.path({ @@ -97,3 +151,5 @@ const eventDoc = openapi.path({ } } }) + +module.exports = events diff --git a/expressjs/src/routes/events/pretty-print.js b/expressjs/src/routes/events/pretty-print.js new file mode 100644 index 0000000..d922caa --- /dev/null +++ b/expressjs/src/routes/events/pretty-print.js @@ -0,0 +1,102 @@ +/* global WebAssembly */ + +const { WASI, init } = require('@wasmer/wasi') +const { HTTP } = require('cloudevents') +const fs = require('fs').promises +const path = require('path') + +class PrinterFactory { + + /** + * Creates a new Printer instance. + * + * @returns {Promise} - the new Printer instance + */ + async create() { + // Initialize WASI + await init() + const wasi = new WASI({ + args: ['cloudevents-pretty-print.wasm'], + env: {}, + }) + const wasm = path.join(__dirname, '../../../build/wasm/cloudevents-pretty-print.wasm') + const buf = new Uint8Array(await fs.readFile(wasm)) + const module = await WebAssembly.compile(buf) + + // Instantiate the WASI module + const instance = await wasi.instantiate(module, {}) + + return new Printer({ instance }) + } +} + +class Printer { + + /** + * Creates a new Printer instance. + * + * @param {Object} v - the params object + * @param {WebAssembly.Instance} v.instance - the WebAssembly instance + */ + constructor({ instance }) { + this.mem = instance.exports.memory + this.fn = instance.exports.pp_print + } + + /** + * Prints a CloudEvent into a human readable text. + * + * @param {CloudEvent} ce - the CloudEvent to print + * @returns {string} - the human readable text + */ + print(ce) { + const message = HTTP.structured(ce).body + + writeToMemory(message, this.mem) + + const rc = this.fn(0) + if (rc !== 0) { + throw new Error(`pp_print() returned ${rc}`) + } + + return readFromMemory(this.mem) + } +} + +/** + * Writes a string into the shared memory as a CString. + * + * @param {string} message - the string to write + * @param {WebAssembly.Memory} mem - the shared memory + */ +function writeToMemory(message, mem) { + const enc = new TextEncoder() + const view = new Uint8Array(mem.buffer) + const state = enc.encodeInto(message, view) + view[state.written] = 0 +} + +/** + * Reads a CString from the shared memory. + * + * @param {WebAssembly.Memory} mem - the shared memory + * @returns {string} - the string read from the shared memory + */ +function readFromMemory(mem) { + const view = new Uint8Array(mem.buffer) + let messageBytes = [] + for (let i = 0; i < view.length; i++) { + if (view[i] === 0) { + break + } + messageBytes.push(view[i]) + } + const dec = new TextDecoder('utf-8') + const res = dec.decode(new Uint8Array(messageBytes)) + return res +} + +module.exports = { + Printer, + PrinterFactory +} diff --git a/expressjs/test/middleware/health.test.js b/expressjs/test/middleware/health.test.js index e8d8335..1b6ec97 100644 --- a/expressjs/test/middleware/health.test.js +++ b/expressjs/test/middleware/health.test.js @@ -5,7 +5,7 @@ const { expect, describe, it } = require('@jest/globals') describe('Route', () => { const app = createApp() it('GET /health/ready', async () => { - const res = await request(app) + const res = await request(await app) .get('/health/ready') expect(res.status).toBe(200) expect(res.headers['content-type']).toMatch(/application\/json/) @@ -13,7 +13,7 @@ describe('Route', () => { }) it('GET /health/live', async () => { - const res = await request(app) + const res = await request(await app) .get('/health/live') expect(res.status).toBe(200) expect(res.headers['content-type']).toMatch(/application\/json/) diff --git a/expressjs/test/middleware/metrics.test.js b/expressjs/test/middleware/metrics.test.js index 664cf72..1bf17fa 100644 --- a/expressjs/test/middleware/metrics.test.js +++ b/expressjs/test/middleware/metrics.test.js @@ -4,7 +4,7 @@ const { expect, describe, it } = require('@jest/globals') describe('Route', () => { it('GET /metrics', async () => { - const app = createApp() + const app = await createApp() const res = await request(app) .get('/metrics') expect(res.status).toBe(200) diff --git a/expressjs/test/middleware/openapi.test.js b/expressjs/test/middleware/openapi.test.js index a22a067..54d21a1 100644 --- a/expressjs/test/middleware/openapi.test.js +++ b/expressjs/test/middleware/openapi.test.js @@ -5,14 +5,14 @@ const { expect, describe, it } = require('@jest/globals') describe('Route', () => { const app = createApp() it('GET /swagger-ui/', async () => { - const res = await request(app) + const res = await request(await app) .get('/swagger-ui/') expect(res.status).toBe(200) expect(res.headers['content-type']).toMatch(/text\/html/) }) it('GET /openapi.json', async () => { - const res = await request(app) + const res = await request(await app) .get('/openapi.json') expect(res.status).toBe(200) expect(res.headers['content-type']).toMatch(/application\/json/) diff --git a/expressjs/test/routes/events/endpoint.test.js b/expressjs/test/routes/events/endpoint.test.js index 9b02c9c..de836d6 100644 --- a/expressjs/test/routes/events/endpoint.test.js +++ b/expressjs/test/routes/events/endpoint.test.js @@ -7,8 +7,8 @@ const axios = require('axios').default const { expect, describe, it } = require('@jest/globals') describe('Route', () => { - const app = createApp() it('GET,POST /events', async () => { + const app = await createApp() await withServer(app, async port => { const messages = [] const endpoint = `http://localhost:${port}/events` @@ -46,7 +46,7 @@ const sendExampleEvent = async endpoint => { return ce } -const withServer = async (app, fn) => { +async function withServer(app, fn) { const port = await freePort() const listener = app.listen(port) await waitForExpect(async () => { diff --git a/expressjs/test/routes/hello/endpoint.test.js b/expressjs/test/routes/hello/endpoint.test.js index bc39e54..3002c66 100644 --- a/expressjs/test/routes/hello/endpoint.test.js +++ b/expressjs/test/routes/hello/endpoint.test.js @@ -16,7 +16,7 @@ describe('Route', () => { return [201, 'OK'] }) - const res = await request(app) + const res = await request(await app) .get('/hello') .query({ who: 'James' }) @@ -32,7 +32,7 @@ describe('Route', () => { }) it('GET /hello?who=nobody', async () => { - const res = await request(app) + const res = await request(await app) .get('/hello') .query({ who: 'nobody' }) diff --git a/expressjs/test/routes/home/endpoint.test.js b/expressjs/test/routes/home/endpoint.test.js index 220a9f2..cdee454 100644 --- a/expressjs/test/routes/home/endpoint.test.js +++ b/expressjs/test/routes/home/endpoint.test.js @@ -5,7 +5,7 @@ const { expect, describe, it } = require('@jest/globals') describe('Route', () => { const app = createApp() it('GET /', async () => { - const res = await request(app).get('/') + const res = await request(await app).get('/') expect(res.status).toBe(200) expect(res.headers['content-type']).toMatch(/application\/json/) @@ -16,7 +16,7 @@ describe('Route', () => { }) it('GET / as Browser', async () => { - const res = await request(app) + const res = await request(await app) .get('/') .set('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36') diff --git a/expressjs/test/routes/info/endpoint.test.js b/expressjs/test/routes/info/endpoint.test.js index 3e5d10e..d976199 100644 --- a/expressjs/test/routes/info/endpoint.test.js +++ b/expressjs/test/routes/info/endpoint.test.js @@ -3,8 +3,8 @@ const createApp = require('../../../src/app') const { expect, describe, it } = require('@jest/globals') describe('Route', () => { - const app = createApp() it('GET /info', async () => { + const app = await createApp() const res = await request(app) .get('/info') diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java index 3501887..e1741b7 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java @@ -64,7 +64,7 @@ private byte[] loadWasmBinary() { } } - private CString executeUsingSharedMemory(CString input, Memory mem) { + private synchronized CString executeUsingSharedMemory(CString input, Memory mem) { var buf = mem.buffer(store); var offset = 0; input.writeOn(buf, offset); From bff8677cd373e7b5f715808b10f3b6d40d4c0dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Wed, 19 Apr 2023 01:18:24 +0200 Subject: [PATCH 5/9] Include error from wasm module in pretty print error message --- expressjs/package.json | 2 +- expressjs/src/routes/events/pretty-print.js | 9 ++++++--- frontend/scripts/webjar.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/expressjs/package.json b/expressjs/package.json index 9ed37e8..f61f263 100644 --- a/expressjs/package.json +++ b/expressjs/package.json @@ -11,7 +11,7 @@ "serve": "npm run build:prepare && bpm run serve:only", "serve:only": "node src/index", "test:ci": "npm run build:prepare && npm run lint && npm run test:jest", - "test:jest:watch": "npm run build:prepare && jest --watch", + "test:jest:watch": "npm run build:prepare && jest --watchAll", "test:jest": "npm run build:prepare && jest --detectOpenHandles --coverage", "lint:eslint": "eslint .", "build:prepare": "npm run build:cache-git && npm run build:extract-webjar", diff --git a/expressjs/src/routes/events/pretty-print.js b/expressjs/src/routes/events/pretty-print.js index d922caa..00026c5 100644 --- a/expressjs/src/routes/events/pretty-print.js +++ b/expressjs/src/routes/events/pretty-print.js @@ -26,7 +26,7 @@ class PrinterFactory { // Instantiate the WASI module const instance = await wasi.instantiate(module, {}) - return new Printer({ instance }) + return new Printer({ instance, wasi }) } } @@ -37,10 +37,12 @@ class Printer { * * @param {Object} v - the params object * @param {WebAssembly.Instance} v.instance - the WebAssembly instance + * @param {WASI} v.wasi - the WASI instance */ - constructor({ instance }) { + constructor({ instance, wasi }) { this.mem = instance.exports.memory this.fn = instance.exports.pp_print + this.wasi = wasi } /** @@ -56,7 +58,8 @@ class Printer { const rc = this.fn(0) if (rc !== 0) { - throw new Error(`pp_print() returned ${rc}`) + const err = this.wasi.getStderrString() + throw new Error(`pp_print(${rc}): ${err}\nce: ${message}`) } return readFromMemory(this.mem) diff --git a/frontend/scripts/webjar.ts b/frontend/scripts/webjar.ts index a51991d..c77b8d1 100644 --- a/frontend/scripts/webjar.ts +++ b/frontend/scripts/webjar.ts @@ -65,7 +65,7 @@ const webjars: Webjar[] = [{ const tmpDir = os.tmpdir() const tmp = await fs.mkdtemp(`${tmpDir}/wasm-oci-`) const reg = new WasmRegistry(tmp) - const image = Image.parse('quay.io/cardil/cloudevents-pretty-print@sha256:01b30983dda5eb42a8baefb523eb50d7d0e539fb10d7ab9498a2a59f35036afb') + const image = Image.parse('quay.io/cardil/cloudevents-pretty-print@sha256:134269c7090bcb923d8b8764920bb25923e38411413eac7e4240524f1a40dc74') log(`Pulling image: ${green(image.toString())}`) const wasm = await reg.pull(image) p.addFile('META-INF/cloudevents-pretty-print.wasm', await fs.readFile(wasm.file), 'Wasm') From 68d46fdb25b1c3ed5070d2bf2ae9877877afa139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Wed, 19 Apr 2023 01:28:53 +0200 Subject: [PATCH 6/9] Remove useless jsdoc --- frontend/scripts/webjar.ts | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/frontend/scripts/webjar.ts b/frontend/scripts/webjar.ts index c77b8d1..e0ccd70 100644 --- a/frontend/scripts/webjar.ts +++ b/frontend/scripts/webjar.ts @@ -4,38 +4,7 @@ import os from 'os' import { green, yellow, cyan, magenta, Color, blue, white } from 'colorette' import { WasmRegistry, Image } from 'wasm-oci' -type Packager = (packer: Packer, log: Logger) => Promise - -interface Packer { - /** - * Adds a file from the disk to the archive. - * @param localPath Path to a file on disk. - * @param zipPath Path to a directory in the archive. Defaults to the empty - * string. - * @param zipName Name for the file. - * @param comment Comment to be attached to the file - */ - addLocalFile(localPath: string, zipPath?: string, zipName?: string, comment?: string): void - /** - * Adds a local directory and all its nested files and directories to the - * archive. - * @param localPath Path to a folder on disk. - * @param zipPath Path to a folder in the archive. Default: `""`. - * @param filter RegExp or Function if files match will be included. - */ - addLocalFolder(localPath: string, zipPath?: string, filter?: (path: string) => boolean): void - /** - * Allows you to create a entry (file or directory) in the zip file. - * If you want to create a directory the `entryName` must end in `"/"` and a `null` - * buffer should be provided. - * @param entryName Entry path. - * @param content Content to add to the entry; must be a 0-length buffer - * for a directory. - * @param comment Comment to add to the entry. - * @param attr Attribute to add to the entry. - */ - addFile(entryName: string, content: Buffer, comment?: string, attr?: number): void -} +type Packager = (packer: AdmZip, log: Logger) => Promise interface Webjar { group: string From 5afc41cecb4d95aa1a598df8c5e9d33a610d2f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Wed, 19 Apr 2023 13:45:34 +0200 Subject: [PATCH 7/9] Print WASM error message if any in Quarkus --- .../showcase/events/PrettyPrintWasm.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java index e1741b7..22beaf0 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java @@ -11,6 +11,8 @@ import io.github.kawamuray.wasmtime.wasi.WasiCtx; import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; class PrettyPrintWasm implements AutoCloseable { @@ -19,13 +21,14 @@ class PrettyPrintWasm implements AutoCloseable { private final Store store; private final Linker linker; private final Engine engine; + private final Path stderr; private Module module; PrettyPrintWasm() { + this.stderr = Path.of(System.getProperty("java.io.tmpdir"), + "cloudevents-pretty-print.stderr"); this.wasi = new WasiCtxBuilder() - .inheritStdout() - .inheritStderr() - .inheritEnv() + .stderr(stderr) .build(); this.store = Store.withoutData(wasi); this.engine = store.engine(); @@ -74,12 +77,25 @@ private synchronized CString executeUsingSharedMemory(CString input, Memory mem) assert results.length == 1; var result = results[0].i32(); if (result != 0) { - throw new IllegalArgumentException("pp_print returned " + result); + String err = readStderr(); + throw new IllegalArgumentException(String.format( + "pp_print(%d): %s\nce: %s", result, err, input + )); } return CString.from(buf, offset); } } + private String readStderr() { + try { + String err = Files.readString(stderr); + Files.delete(stderr); + return err; + } catch (Exception ex) { + throw new IllegalStateException("Failed to read WASI stderr", ex); + } + } + @Override public void close() { if (module != null) { From 117b06d539784971e875e5fb65197119a4275ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Wed, 19 Apr 2023 16:18:07 +0200 Subject: [PATCH 8/9] Central logging for Express (par with Quarkus) --- expressjs/.gitignore | 1 + expressjs/jest.config.js | 1 - expressjs/package-lock.json | 679 ++++++++++++++---- expressjs/package.json | 4 +- expressjs/src/index.js | 6 +- expressjs/src/lib/fs.js | 15 +- expressjs/src/lib/logging.js | 35 + expressjs/src/lib/project.js | 9 +- expressjs/src/middleware/logging.js | 30 +- expressjs/src/routes/events/devdata.js | 3 + expressjs/src/routes/events/endpoint.js | 5 +- expressjs/src/routes/hello/notifier.js | 5 +- expressjs/src/routes/home/endpoint.js | 3 +- expressjs/src/routes/info/endpoint.js | 3 +- expressjs/test/lib/fs.test.js | 24 + .../test/{config => lib}/project.test.js | 0 16 files changed, 668 insertions(+), 155 deletions(-) create mode 100644 expressjs/src/lib/logging.js create mode 100644 expressjs/test/lib/fs.test.js rename expressjs/test/{config => lib}/project.test.js (100%) diff --git a/expressjs/.gitignore b/expressjs/.gitignore index d88e380..8cdd6d7 100644 --- a/expressjs/.gitignore +++ b/expressjs/.gitignore @@ -1,4 +1,5 @@ node_modules/ build/ +coverage/ public/ npm-debug.log diff --git a/expressjs/jest.config.js b/expressjs/jest.config.js index 88df235..a2d088f 100644 --- a/expressjs/jest.config.js +++ b/expressjs/jest.config.js @@ -3,7 +3,6 @@ */ module.exports = { verbose: true, - coverageDirectory: 'build/coverage', projects: [{ displayName: 'test', testEnvironment: 'node', diff --git a/expressjs/package-lock.json b/expressjs/package-lock.json index 92c859b..9d7ed80 100644 --- a/expressjs/package-lock.json +++ b/expressjs/package-lock.json @@ -16,8 +16,8 @@ "dotenv": "^14.3.2", "express": "^4.18.2", "express-prom-bundle": "^6.6.0", + "express-winston": "^4.2.0", "joi": "^17.8.3", - "morgan": "^1.10.0", "prom-client": "^14.2.0" }, "devDependencies": { @@ -656,6 +656,26 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "peer": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", @@ -1707,6 +1727,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==", + "peer": true + }, "node_modules/@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -2106,6 +2132,12 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "peer": true + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2229,22 +2261,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2635,11 +2651,20 @@ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "dev": true }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -2647,8 +2672,27 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "peer": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "peer": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } }, "node_modules/combined-stream": { "version": "1.0.8", @@ -2987,6 +3031,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "peer": true + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -3431,6 +3481,72 @@ "prom-client": ">=12.0.0" } }, + "node_modules/express-winston": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", + "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", + "dependencies": { + "chalk": "^2.4.2", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "winston": ">=3.x <4" + } + }, + "node_modules/express-winston/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/express-winston/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/express-winston/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/express-winston/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/express-winston/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3532,6 +3648,12 @@ "bser": "2.1.1" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "peer": true + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3621,6 +3743,12 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "peer": true + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -4337,7 +4465,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "engines": { "node": ">=8" }, @@ -5925,6 +6052,12 @@ "node": ">=6" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "peer": true + }, "node_modules/lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -5979,8 +6112,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.get": { "version": "4.4.2", @@ -5998,6 +6130,20 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "peer": true, + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6173,50 +6319,10 @@ "node": ">=0.10.0" } }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -6387,14 +6493,6 @@ "node": ">= 0.8" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6404,6 +6502,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "peer": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -6850,6 +6957,20 @@ "optional": true, "peer": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7072,6 +7193,15 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7287,6 +7417,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "peer": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "peer": true + }, "node_modules/simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", @@ -7348,6 +7493,15 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "peer": true, + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -7377,6 +7531,15 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -7603,6 +7766,12 @@ "node": ">=8" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "peer": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7689,6 +7858,12 @@ "node": ">=8" } }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "peer": true + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -7865,6 +8040,12 @@ "which-typed-array": "^1.1.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "peer": true + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -8029,6 +8210,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/winston": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", + "peer": true, + "dependencies": { + "@colors/colors": "1.5.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "peer": true, + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -8692,6 +8909,23 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "peer": true + }, + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "peer": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@eslint/eslintrc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.0.tgz", @@ -9568,6 +9802,12 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/triple-beam": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==", + "peer": true + }, "@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -9854,6 +10094,12 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "peer": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -9950,21 +10196,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -10257,11 +10488,20 @@ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", "dev": true }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "peer": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -10269,8 +10509,27 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "peer": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "peer": true, + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } }, "combined-stream": { "version": "1.0.8", @@ -10535,6 +10794,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "peer": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -10888,6 +11153,53 @@ "url-value-parser": "^2.0.0" } }, + "express-winston": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-4.2.0.tgz", + "integrity": "sha512-EMD74g63nVHi7pFleQw7KHCxiA1pjF5uCwbCfzGqmFxs9KvlDPIVS3cMGpULm6MshExMT9TjC3SqmRGB9kb7yw==", + "requires": { + "chalk": "^2.4.2", + "lodash": "^4.17.21" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10952,6 +11264,12 @@ "bser": "2.1.1" } }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "peer": true + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11025,6 +11343,12 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "peer": true + }, "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -11523,8 +11847,7 @@ "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, "is-typed-array": { "version": "1.1.10", @@ -12781,6 +13104,12 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "peer": true + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -12820,8 +13149,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.get": { "version": "4.4.2", @@ -12839,6 +13167,20 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "peer": true, + "requires": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -12970,46 +13312,10 @@ } } }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "requires": { - "ee-first": "1.1.1" - } - } - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", @@ -13139,11 +13445,6 @@ "ee-first": "1.1.1" } }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -13153,6 +13454,15 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "peer": true, + "requires": { + "fn.name": "1.x.x" + } + }, "onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -13477,6 +13787,17 @@ "optional": true, "peer": true }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -13626,6 +13947,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "peer": true + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -13804,6 +14131,23 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "peer": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "peer": true + } + } + }, "simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", @@ -13855,6 +14199,12 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "peer": true + }, "stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -13877,6 +14227,15 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "peer": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -14045,6 +14404,12 @@ "minimatch": "^3.0.4" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "peer": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -14113,6 +14478,12 @@ "punycode": "^2.1.1" } }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "peer": true + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -14236,6 +14607,12 @@ "which-typed-array": "^1.1.2" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "peer": true + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -14362,6 +14739,36 @@ "is-typed-array": "^1.1.10" } }, + "winston": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", + "peer": true, + "requires": { + "@colors/colors": "1.5.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + } + }, + "winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "peer": true, + "requires": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/expressjs/package.json b/expressjs/package.json index f61f263..5638f82 100644 --- a/expressjs/package.json +++ b/expressjs/package.json @@ -11,7 +11,7 @@ "serve": "npm run build:prepare && bpm run serve:only", "serve:only": "node src/index", "test:ci": "npm run build:prepare && npm run lint && npm run test:jest", - "test:jest:watch": "npm run build:prepare && jest --watchAll", + "test:jest:watch": "npm run build:prepare && jest --watchAll --detectOpenHandles --coverage", "test:jest": "npm run build:prepare && jest --detectOpenHandles --coverage", "lint:eslint": "eslint .", "build:prepare": "npm run build:cache-git && npm run build:extract-webjar", @@ -44,8 +44,8 @@ "dotenv": "^14.3.2", "express": "^4.18.2", "express-prom-bundle": "^6.6.0", + "express-winston": "^4.2.0", "joi": "^17.8.3", - "morgan": "^1.10.0", "prom-client": "^14.2.0" }, "devDependencies": { diff --git a/expressjs/src/index.js b/expressjs/src/index.js index 209d993..1a89b49 100644 --- a/expressjs/src/index.js +++ b/expressjs/src/index.js @@ -1,5 +1,7 @@ const createApp = require('./app') const config = require('./lib/config') +const { log } = require('./lib/logging') +const colors = require('@colors/colors') const main = async () => { @@ -8,7 +10,9 @@ const main = async () => { const port = config.port() app.listen(port, () => { - console.log(`Listening at http://localhost:${port}/`) + const address = colors.cyan(colors.underline( + `http://localhost:${port}/`)) + log.info(`Listening at ${address}`) }) } diff --git a/expressjs/src/lib/fs.js b/expressjs/src/lib/fs.js index 59f4819..e7ff1df 100644 --- a/expressjs/src/lib/fs.js +++ b/expressjs/src/lib/fs.js @@ -12,6 +12,19 @@ async function isDirectory(filepath) { } } +async function isFile(filepath) { + try { + const stat = await fs.lstat(filepath) + return stat.isFile() + } catch (ex) { + if (ex.code === 'ENOENT') { + return false + } + throw ex + } +} + module.exports = { - isDirectory + isDirectory, + isFile, } diff --git a/expressjs/src/lib/logging.js b/expressjs/src/lib/logging.js new file mode 100644 index 0000000..1592761 --- /dev/null +++ b/expressjs/src/lib/logging.js @@ -0,0 +1,35 @@ +const winston = require('winston') +const colorizer = require('logform/colorize') +const { isProduction } = require('./env') + +/** + * @type {winston.LoggerOptions} + */ +const opts = { + level: isProduction ? 'info' : 'debug', + transports: [ + new winston.transports.Console() + ], + format: winston.format.combine( + winston.format.errors(), + winston.format.splat(), + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss,SSS' + }), + winston.format.printf(info => { + info.level = info.level.padStart(5).toUpperCase() + colorizer().transform(info, { all: true }) + const { timestamp, level, message } = info + + return `${level} ${timestamp} ${message}` + }), + ), + colorize: true, +} + +const log = winston.createLogger(opts) + +module.exports = { + log, + opts, +} diff --git a/expressjs/src/lib/project.js b/expressjs/src/lib/project.js index 49d7f6d..5aad7dd 100644 --- a/expressjs/src/lib/project.js +++ b/expressjs/src/lib/project.js @@ -1,5 +1,6 @@ -const { isDirectory } = require('./fs.js') +const { isFile } = require('./fs.js') const packageJson = require('../../package.json') +const path = require('path') class Project { constructor({ group, artifact, version, platform }) { @@ -34,10 +35,10 @@ async function resolveGitDescribe() { } async function computeGitDescribe() { - if (await isDirectory('../.git')) { - return await resolveGitDescribe() + if (await isFile(path.join(__dirname, '../../build/git-describe.js'))) { + return cachedGitDescribe() } - return cachedGitDescribe() + return await resolveGitDescribe() } function cachedGitDescribe() { diff --git a/expressjs/src/middleware/logging.js b/expressjs/src/middleware/logging.js index 65ce22d..bc9f95d 100644 --- a/expressjs/src/middleware/logging.js +++ b/expressjs/src/middleware/logging.js @@ -1,10 +1,32 @@ -const morgan = require('morgan') +const expressWinston = require('express-winston') +const { opts } = require('../lib/logging') +const { isProduction } = require('../lib/env') +/** + * @typedef {import('express').Express} Express + */ + + +/** + * Configure logging middleware + * + * @param {Express} app - Express app + */ module.exports = app => { - app.use(morgan('dev', { - skip(_req, _res) { - return typeof jest !== 'undefined' + if (isProduction) { + return + } + + app.use(expressWinston.logger({ + ...opts, + ...{ + meta: true, + expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true + colorize: true, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). + ignoreRoute: (_req, _res) => { + return typeof jest !== 'undefined' + } } })) diff --git a/expressjs/src/routes/events/devdata.js b/expressjs/src/routes/events/devdata.js index 9dd3787..9591c94 100644 --- a/expressjs/src/routes/events/devdata.js +++ b/expressjs/src/routes/events/devdata.js @@ -1,5 +1,6 @@ const { CloudEvent } = require('cloudevents') const { isProduction } = require('../../lib/env') +const { log } = require('../../lib/logging') const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min) @@ -19,4 +20,6 @@ function newEvent() { const devdata = [newEvent(), newEvent()].filter(() => !isProduction) +log.debug('Deploying dev data: %j', !isProduction) + module.exports = devdata diff --git a/expressjs/src/routes/events/endpoint.js b/expressjs/src/routes/events/endpoint.js index af34bde..d8eb913 100644 --- a/expressjs/src/routes/events/endpoint.js +++ b/expressjs/src/routes/events/endpoint.js @@ -3,6 +3,7 @@ const openapi = require('../../lib/openapi') const EventStore = require('./store') const { PrinterFactory } = require('./pretty-print') const devdata = require('./devdata') +const { log } = require('../../lib/logging') const printerFactory = new PrinterFactory() @@ -69,7 +70,7 @@ function recv(req, res) { recvEvent(ce) res.status(201).end() } catch (err) { - console.error(err) + log.error(err) res.status(500) res.json(err) } @@ -84,7 +85,7 @@ function recvEvent(ce) { ce.validate() store.add(ce) const out = printer.print(ce) - console.log('Received:\n', out) + log.info('Received:\n%s', out) } const streamDoc = openapi.path({ diff --git a/expressjs/src/routes/hello/notifier.js b/expressjs/src/routes/hello/notifier.js index a6ce66e..df6b0fc 100644 --- a/expressjs/src/routes/hello/notifier.js +++ b/expressjs/src/routes/hello/notifier.js @@ -1,5 +1,6 @@ const axios = require('axios').default const { HTTP, CloudEvent } = require('cloudevents') +const { log } = require('../../lib/logging') const type = 'com.redhat.openshift.knative.showcase.domain.entity.Hello' const source = '//events/showcase' @@ -31,9 +32,9 @@ class Notifier { data: message.body, headers: message.headers }) - console.log(`Event ${ce.id} sent to ${url}`) + log.info(`Event ${ce.id} sent to ${url}`) } catch (err) { - console.error(`Couldn't send an event ${ce.id} to ${url}`, err) + log.error(`Couldn't send an event ${ce.id} to ${url}: %s`, err) } } } diff --git a/expressjs/src/routes/home/endpoint.js b/expressjs/src/routes/home/endpoint.js index e2c12f2..7428cc0 100644 --- a/expressjs/src/routes/home/endpoint.js +++ b/expressjs/src/routes/home/endpoint.js @@ -1,6 +1,7 @@ const config = require('../../lib/config') const oapi = require('../../lib/openapi') const { resolveProject } = require('../../lib/project') +const { log } = require('../../lib/logging') /** * @param {express.Express} app @@ -20,7 +21,7 @@ const home = app => { res.status(200) .sendFile('home.html', { root: 'public' }) } catch (err) { - console.error(err) + log.error(err) res.status(500) res.json(err) } diff --git a/expressjs/src/routes/info/endpoint.js b/expressjs/src/routes/info/endpoint.js index 9e960d9..f8afe74 100644 --- a/expressjs/src/routes/info/endpoint.js +++ b/expressjs/src/routes/info/endpoint.js @@ -2,6 +2,7 @@ const config = require('../../lib/config') const openapi = require('../../lib/openapi') const { resolveProject } = require('../../lib/project') const Info = require('./info') +const { log } = require('../../lib/logging') /** * @param {express.Express} app @@ -20,7 +21,7 @@ module.exports = app => { res.status(200) res.json(info) } catch (err) { - console.error(err) + log.error(err) res.status(500) res.json(err) } diff --git a/expressjs/test/lib/fs.test.js b/expressjs/test/lib/fs.test.js new file mode 100644 index 0000000..4edaa41 --- /dev/null +++ b/expressjs/test/lib/fs.test.js @@ -0,0 +1,24 @@ +const { isDirectory, isFile } = require('../../src/lib/fs') +const { expect, describe, it } = require('@jest/globals') + +describe('fs', () => { + describe('isDirectory', () => { + it('should return true for a directory', async () => { + expect(await isDirectory(__dirname)).toBeTruthy() + }) + + it('should return false for a file', async () => { + expect(await isDirectory(__filename)).toBeFalsy() + }) + }) + + describe('isFile', () => { + it('should return true for a file', async () => { + expect(await isFile(__filename)).toBeTruthy() + }) + + it('should return false for a directory', async () => { + expect(await isFile(__dirname)).toBeFalsy() + }) + }) +}) diff --git a/expressjs/test/config/project.test.js b/expressjs/test/lib/project.test.js similarity index 100% rename from expressjs/test/config/project.test.js rename to expressjs/test/lib/project.test.js From fd54cc0da3a961fcd2d8cb58fa05c93e56c75c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Suszy=C5=84ski?= Date: Wed, 19 Apr 2023 16:18:07 +0200 Subject: [PATCH 9/9] Fixing Quarkus native build --- quarkus/.mvn/extensions.xml | 2 +- quarkus/pom.xml | 13 +++++++++---- quarkus/src/main/docker/graalvm-builder.Dockerfile | 6 ++++++ .../knative/showcase/events/PrettyPrintWasm.java | 9 ++++----- 4 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 quarkus/src/main/docker/graalvm-builder.Dockerfile diff --git a/quarkus/.mvn/extensions.xml b/quarkus/.mvn/extensions.xml index ff8b558..40644e0 100644 --- a/quarkus/.mvn/extensions.xml +++ b/quarkus/.mvn/extensions.xml @@ -5,6 +5,6 @@ me.qoomon maven-git-versioning-extension - 9.4.1 + 9.6.3 diff --git a/quarkus/pom.xml b/quarkus/pom.xml index 32cd545..c456494 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -11,13 +11,18 @@ UTF-8 com.redhat.quarkus.platform quarkus-bom - 2.13.5.Final-redhat-00002 + 2.13.7.Final-redhat-00003 2.4.0 true - 3.10.1 - 3.0.0-M7 - 3.2.0 + 3.11.0 + 3.0.0 + 3.3.0 2023-01-01T00:00:00Z + + quay.io/cardil/quarkus/graalvm/native-image:ol9-java17 diff --git a/quarkus/src/main/docker/graalvm-builder.Dockerfile b/quarkus/src/main/docker/graalvm-builder.Dockerfile new file mode 100644 index 0000000..1b678d3 --- /dev/null +++ b/quarkus/src/main/docker/graalvm-builder.Dockerfile @@ -0,0 +1,6 @@ +FROM ghcr.io/graalvm/native-image:ol9-java17 + +RUN mkdir -p /project && chown 1001:1001 /project +USER 1001 +WORKDIR /project +# tag as quay.io/cardil/quarkus/graalvm/native-image:ol9-java17 diff --git a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java index 22beaf0..2097b2f 100644 --- a/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java +++ b/quarkus/src/main/java/com/redhat/openshift/knative/showcase/events/PrettyPrintWasm.java @@ -43,16 +43,15 @@ CString execute(CString input) { } } - private synchronized Module loadWasmModule() { + private synchronized void loadWasmModule() { if (module != null) { - return module; + return; } byte[] wasm = loadWasmBinary(); module = Module.fromBinary(engine, wasm); if (!linker.modules(store).contains(MODULE_NAME)) { linker.module(store, MODULE_NAME, module); } - return module; } private byte[] loadWasmBinary() { @@ -62,7 +61,7 @@ private byte[] loadWasmBinary() { "cloudevents-pretty-print.wasm not found"); return steam.readAllBytes(); } catch (Exception ex) { - throw new RuntimeException( + throw new IllegalStateException( "Failed to load cloudevents-pretty-print.wasm", ex); } } @@ -79,7 +78,7 @@ private synchronized CString executeUsingSharedMemory(CString input, Memory mem) if (result != 0) { String err = readStderr(); throw new IllegalArgumentException(String.format( - "pp_print(%d): %s\nce: %s", result, err, input + "pp_print(%d): %s%nce: %s", result, err, input )); } return CString.from(buf, offset);