From ad63b122baf32f29b7d8be5ca2ef18e4d802fbf0 Mon Sep 17 00:00:00 2001 From: bcoll Date: Sat, 15 Oct 2022 19:39:44 +0100 Subject: [PATCH 1/5] Add RTTI export script --- src/workerd/README.md | 1 + src/workerd/api/api-rtti-test.c++ | 6 +- src/workerd/jsg/rtti-test.c++ | 62 +++++++---- src/workerd/jsg/rtti.capnp | 42 ++++++- src/workerd/jsg/rtti.h | 110 ++++++++++++------ src/workerd/jsg/util.c++ | 8 +- src/workerd/jsg/util.h | 1 + src/workerd/server/workerd-api.c++ | 4 + src/workerd/tools/BUILD.bazel | 25 +++++ src/workerd/tools/api-encoder.c++ | 173 +++++++++++++++++++++++++++++ 10 files changed, 369 insertions(+), 63 deletions(-) create mode 100644 src/workerd/tools/BUILD.bazel create mode 100644 src/workerd/tools/api-encoder.c++ diff --git a/src/workerd/README.md b/src/workerd/README.md index 6a5ae523be6..f1bb7ea2e1c 100644 --- a/src/workerd/README.md +++ b/src/workerd/README.md @@ -5,3 +5,4 @@ The subdirectories are organized as follows: * **io:** Generally contains code that handles the I/O layer which allows APIs to talk to the rest of the world. Also includes basic Worker lifecycle and event delivery. * **api:** Contains implementations of publicly documented application-visible JavaScript APIs. * **server:** Contains the high-level server implementation. +* **tools:** Contains additional meta-programs, notably a script for exporting API types. \ No newline at end of file diff --git a/src/workerd/api/api-rtti-test.c++ b/src/workerd/api/api-rtti-test.c++ index fa492134ba5..24d45ef2e58 100644 --- a/src/workerd/api/api-rtti-test.c++ +++ b/src/workerd/api/api-rtti-test.c++ @@ -15,14 +15,14 @@ namespace { KJ_TEST("WorkerGlobalScope") { jsg::rtti::Builder builder((CompatibilityFlags::Reader())); builder.structure(); - KJ_EXPECT(builder.structure("Event"_kj) != nullptr); - KJ_EXPECT(builder.structure("ObviouslyWrongName"_kj) == nullptr); + KJ_EXPECT(builder.structure("workerd::api::Event"_kj) != nullptr); + KJ_EXPECT(builder.structure("workerd::api::ObviouslyWrongName"_kj) == nullptr); } KJ_TEST("ServiceWorkerGlobalScope") { jsg::rtti::Builder builder((CompatibilityFlags::Reader())); builder.structure(); - KJ_EXPECT(builder.structure("DurableObjectId"_kj) != nullptr); + KJ_EXPECT(builder.structure("workerd::api::DurableObjectId"_kj) != nullptr); } } // namespace diff --git a/src/workerd/jsg/rtti-test.c++ b/src/workerd/jsg/rtti-test.c++ index 953aaf78e59..bcc58202b08 100644 --- a/src/workerd/jsg/rtti-test.c++ +++ b/src/workerd/jsg/rtti-test.c++ @@ -81,13 +81,15 @@ KJ_TEST("generic types") { KJ_EXPECT(tType>() == "(object = void)"); KJ_EXPECT(tType>() == "(object = void)"); KJ_EXPECT(tType>() == "(object = void)"); + KJ_EXPECT(tType>() == "(string = (name = \"kj::String\"))"); - KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\"))))"); - KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\"))))"); - KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\"))))"); + KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\")), name = \"kj::Array\"))"); + KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\")), name = \"kj::ArrayPtr\"))"); + KJ_EXPECT(tType>() == "(array = (element = (number = (name = \"int\")), name = \"jsg::Sequence\"))"); - KJ_EXPECT(tType>() == "(maybe = (value = (number = (name = \"int\"))))"); - KJ_EXPECT(tType>() == "(maybe = (value = (number = (name = \"int\"))))"); + KJ_EXPECT(tType>() == "(maybe = (value = (number = (name = \"int\")), name = \"kj::Maybe\"))"); + KJ_EXPECT(tType>() == "(maybe = (value = (number = (name = \"int\")), name = \"jsg::Optional\"))"); + KJ_EXPECT(tType>() == "(maybe = (value = (number = (name = \"int\")), name = \"jsg::LenientOptional\"))"); KJ_EXPECT(tType>() == "(dict = (key = (string = (name = \"kj::String\")), value = (number = (name = \"int\"))))"); KJ_EXPECT((tType>()) == "(dict = (key = (number = (name = \"double\")), value = (number = (name = \"int\"))))"); @@ -111,11 +113,14 @@ KJ_TEST("builtins") { KJ_TEST("jsgImpl") { KJ_EXPECT(tType() == "(jsgImpl = (type = jsgLock))"); + KJ_EXPECT(tType() == "(jsgImpl = (type = jsgSelfRef))"); KJ_EXPECT(tType() == "(jsgImpl = (type = jsgUnimplemented))"); KJ_EXPECT(tType() == "(jsgImpl = (type = jsgVarargs))"); KJ_EXPECT(tType() == "(jsgImpl = (type = v8Isolate))"); KJ_EXPECT(tType() == "(jsgImpl = (type = configuration))"); KJ_EXPECT(tType>() == "(jsgImpl = (type = jsgTypeHandler))"); + KJ_EXPECT(tType>() == "(jsgImpl = (type = v8FunctionCallbackInfo))"); + KJ_EXPECT(tType>() == "(jsgImpl = (type = v8PropertyCallbackInfo))"); } KJ_TEST("functions") { @@ -162,11 +167,14 @@ struct TestResource: public Base { }; KJ_TEST("resource reference") { - KJ_EXPECT(tType() == "(structure = (name = \"TestResource\"))"); + KJ_EXPECT(tType() == "(structure = (name = \"TestResource\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\"))"); } KJ_TEST("resource structure") { - KJ_EXPECT(tStructure() == "(name = \"Base\", members = [], extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), iterable = false, asyncIterable = false)"); + KJ_EXPECT(tStructure() == "(name = \"Base\", members = [], " + "extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), " + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\")"); KJ_EXPECT(tStructure() == "(name = \"TestResource\", members = [" "(method = (name = \"instanceMethod\", returnType = (voidt = void), args = [(number = (name = \"int\")), (number = (name = \"double\"))], static = false)), " @@ -177,8 +185,10 @@ KJ_TEST("resource structure") { "(property = (name = \"lazyReadonlySize\", type = (number = (name = \"int\")), readonly = true, lazy = true, prototype = false)), " "(property = (name = \"protoSize\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = true)), " "(property = (name = \"protoReadonlySize\", type = (number = (name = \"int\")), readonly = true, lazy = false, prototype = true)), " - "(constructor = (args = [(maybe = (value = (string = (name = \"kj::String\"))))]))], " - "extends = (structure = (name = \"Base\")), iterable = false, asyncIterable = false)"); + "(constructor = (args = [(maybe = (value = (string = (name = \"kj::String\")), name = \"jsg::Optional\"))]))], " + "extends = (structure = (name = \"Base\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\")), " + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\")"); } struct TestNested : jsg::Object { @@ -186,9 +196,14 @@ struct TestNested : jsg::Object { }; KJ_TEST("nested structure") { - KJ_EXPECT(tStructure() == "(name = \"TestNested\", members = [" - "(nested = (name = \"Base\", members = [], extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), iterable = false, asyncIterable = false))], " - "iterable = false, asyncIterable = false)"); + KJ_EXPECT(tStructure() == "(name = \"TestNested\", members = [(nested = (" + "name = \"Base\", members = [], " + "extends = (intrinsic = (name = \"v8::kIteratorPrototype\")), " + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::Base\")" + ")], " + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestNested\")"); } struct TestConstant : jsg::Object { @@ -208,7 +223,8 @@ KJ_TEST("constant members") { KJ_EXPECT(tStructure() == "(name = \"TestConstant\", members = [" "(constant = (name = \"ENABLED\", value = 1)), " "(constant = (name = \"CIRCLE\", value = 2))], " - "iterable = false, asyncIterable = false)"); + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestConstant\")"); } struct TestStruct { @@ -218,14 +234,15 @@ struct TestStruct { }; KJ_TEST("struct reference") { - KJ_EXPECT(tType() == "(structure = (name = \"TestStruct\"))"); + KJ_EXPECT(tType() == "(structure = (name = \"TestStruct\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestStruct\"))"); } KJ_TEST("struct structure") { KJ_EXPECT(tStructure() == "(name = \"TestStruct\", members = [" "(property = (name = \"a\", type = (number = (name = \"int\")), readonly = false, lazy = false, prototype = false)), " "(property = (name = \"b\", type = (boolt = void), readonly = false, lazy = false, prototype = false))], " - "iterable = false, asyncIterable = false)"); + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestStruct\")"); } struct TestSymbolTable: public jsg::Object { @@ -244,13 +261,14 @@ KJ_TEST("symbol table") { capnp::TextCodec codec; KJ_EXPECT(codec.encode(type) == "(name = \"TestSymbolTable\", members = [" - "(method = (name = \"acceptResource\", returnType = (voidt = void), args = [(structure = (name = \"TestResource\"))], static = false)), " - "(method = (name = \"recursiveTypeFunction\", returnType = (voidt = void), args = [(structure = (name = \"TestSymbolTable\"))], static = false))], " - "iterable = false, asyncIterable = false)"); - - KJ_EXPECT(builder.structure("TestSymbolTable"_kj) != nullptr); - KJ_EXPECT(builder.structure("TestResource"_kj) != nullptr); - KJ_EXPECT(KJ_REQUIRE_NONNULL(builder.structure("TestResource"_kj)).getMembers().size() > 0); + "(method = (name = \"acceptResource\", returnType = (voidt = void), args = [(structure = (name = \"TestResource\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestResource\"))], static = false)), " + "(method = (name = \"recursiveTypeFunction\", returnType = (voidt = void), args = [(structure = (name = \"TestSymbolTable\", fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable\"))], static = false))], " + "iterable = false, asyncIterable = false, " + "fullyQualifiedName = \"workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable\")"); + + KJ_EXPECT(builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestSymbolTable"_kj) != nullptr); + KJ_EXPECT(builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestResource"_kj) != nullptr); + KJ_EXPECT(KJ_REQUIRE_NONNULL(builder.structure("workerd::jsg::rtti::(anonymous namespace)::TestResource"_kj)).getMembers().size() > 0); } } // namespace diff --git a/src/workerd/jsg/rtti.capnp b/src/workerd/jsg/rtti.capnp index ddd74f053be..8691865dfa9 100644 --- a/src/workerd/jsg/rtti.capnp +++ b/src/workerd/jsg/rtti.capnp @@ -76,6 +76,8 @@ struct StructureType { # Structure types need to be resolved separately to prevent circular references with types name @0 :Text; + + fullyQualifiedName @1 :Text; } struct StringType { @@ -92,11 +94,15 @@ struct IntrinsicType { struct ArrayType { # Array like structure element @0 :Type; + + name @1 :Text; } struct MaybeType { - # kj::Maybe or jsg::Optional + # kj::Maybe, jsg::Optional, jsg::LenientOptional value @0 :Type; + + name @1 :Text; } struct DictType { @@ -157,6 +163,12 @@ struct JsgImplType { jsgUnimplemented @4; jsgVarargs @5; + + jsgSelfRef @6; + + v8FunctionCallbackInfo @7; + + v8PropertyCallbackInfo @8; } type @0 :Type; @@ -168,6 +180,9 @@ struct Structure { name @0 :Text; # Structure name + fullyQualifiedName @5 :Text; + # Fully-qualified structure name including namespaces and parents + members @1 :List(Member); # All members in declaration order @@ -176,9 +191,13 @@ struct Structure { iterable @3 :Bool; # true if the structure is iterable + iterator @6 :Method; + # Method returning iterator if the structure is iterable asyncIterable @4 :Bool; # true if the structure is async iterable + asyncIterator @7 :Method; + # Method returning async iterator if the structure is async iterable } struct Member { @@ -191,7 +210,12 @@ struct Member { property @1 :Property; # any kind of property - nested @2 :Structure; + nested :group { + structure @2 :Structure; + + name @5 :Text; + # For JSG_NESTED_TYPE_NAMED, if name is different to structure + } # nested type constant @3 :Constant; @@ -229,3 +253,17 @@ struct Constant { struct Constructor { args @0 :List(Type); } + +struct DefinitionGeneratorRequest { + # Collection of structure groups, consumed by TypeScript definitions generator + + groups @0 :List(StructureGroup); + + struct StructureGroup { + # Collection of related structures + + name @0 :Text; + + structures @1 :List(Structure); + } +} diff --git a/src/workerd/jsg/rtti.h b/src/workerd/jsg/rtti.h index 2d9bd4bded7..eb2ef5cd1d2 100644 --- a/src/workerd/jsg/rtti.h +++ b/src/workerd/jsg/rtti.h @@ -49,7 +49,7 @@ class Builder { template Structure::Reader structure() { - auto name = jsg::typeName(typeid(T)); + auto name = jsg::fullyQualifiedTypeName(typeid(T)); KJ_IF_MAYBE(builder, symbols.find(name)) { return (*builder)->template getRoot(); } @@ -257,43 +257,59 @@ struct BuildRtti> { } }; -// Generic Types - template -struct BuildRtti> { +struct BuildRtti> { static void build(Type::Builder builder, Builder& rtti) { - BuildRtti::build(builder.initMaybe().initValue(), rtti); + BuildRtti::build(builder, rtti); } }; -template -struct BuildRtti> { - static void build(Type::Builder builder, Builder& rtti) { - BuildRtti::build(builder.initMaybe().initValue(), rtti); - } -}; +// Maybe Types -template -struct BuildRtti> { - static void build(Type::Builder builder, Builder& rtti) { - BuildRtti::build(builder.initArray().initElement(), rtti); - } +#define DECLARE_MAYBE_TYPE(T) \ +template \ +struct BuildRtti> { \ + static void build(Type::Builder builder, Builder& rtti) { \ + auto maybe = builder.initMaybe(); \ + BuildRtti::build(maybe.initValue(), rtti); \ + maybe.setName(#T); \ + } \ }; -template -struct BuildRtti> { - static void build(Type::Builder builder, Builder& rtti) { - BuildRtti::build(builder.initArray().initElement(), rtti); - } -}; +#define FOR_EACH_MAYBE_TYPE(F) \ + F(kj::Maybe) \ + F(jsg::Optional) \ + F(jsg::LenientOptional) -template -struct BuildRtti> { - static void build(Type::Builder builder, Builder& rtti) { - BuildRtti::build(builder.initArray().initElement(), rtti); - } +FOR_EACH_MAYBE_TYPE(DECLARE_MAYBE_TYPE) + +#undef FOR_EACH_MAYBE_TYPE +#undef DECLARE_MAYBE_TYPE + +// Array Types + +#define DECLARE_ARRAY_TYPE(T) \ +template \ +struct BuildRtti> { \ + static void build(Type::Builder builder, Builder& rtti) { \ + auto array = builder.initArray(); \ + BuildRtti::build(array.initElement(), rtti); \ + array.setName(#T); \ + } \ }; +#define FOR_EACH_ARRAY_TYPE(F) \ + F(kj::Array) \ + F(kj::ArrayPtr) \ + F(jsg::Sequence) + +FOR_EACH_ARRAY_TYPE(DECLARE_ARRAY_TYPE) + +#undef FOR_EACH_ARRAY_TYPE +#undef DECLARE_ARRAY_TYPE + +// Misc Generic Types + template struct BuildRtti> { static void build(Type::Builder builder, Builder& rtti) { @@ -384,9 +400,12 @@ struct BuildRtti { \ #define FOR_EACH_JSG_IMPL_TYPE(F, ...) \ F(jsg::Lock, JsgImplType::Type::JSG_LOCK) \ + F(jsg::SelfRef, JsgImplType::Type::JSG_SELF_REF) \ F(jsg::Unimplemented, JsgImplType::Type::JSG_UNIMPLEMENTED) \ F(jsg::Varargs, JsgImplType::Type::JSG_VARARGS) \ - F(v8::Isolate*, JsgImplType::Type::V8_ISOLATE) + F(v8::Isolate*, JsgImplType::Type::V8_ISOLATE) \ + F(v8::FunctionCallbackInfo, JsgImplType::Type::V8_FUNCTION_CALLBACK_INFO) \ + F(v8::PropertyCallbackInfo, JsgImplType::Type::V8_PROPERTY_CALLBACK_INFO) FOR_EACH_JSG_IMPL_TYPE(DECLARE_JSG_IMPL_TYPE) @@ -526,7 +545,8 @@ struct MembersBuilder { template inline void registerNestedType() { auto nested = members[index++].initNested(); - BuildRtti::build(nested, rtti); + nested.setName(name); + BuildRtti::build(nested.initStructure(), rtti); } template @@ -613,11 +633,29 @@ struct MembersBuilder { TupleRttiBuilder::build(method.initArgs(std::tuple_size_v), rtti); } - template - inline void registerIterable() { structure.setIterable(true); } + template + inline void registerIterable() { + structure.setIterable(true); - template - inline void registerAsyncIterable() { structure.setAsyncIterable(true); } + auto method = structure.initIterator(); + method.setName(name); + using Traits = FunctionTraits; + BuildRtti::build(method.initReturnType(), rtti); + using Args = typename Traits::ArgsTuple; + TupleRttiBuilder::build(method.initArgs(std::tuple_size_v), rtti); + } + + template + inline void registerAsyncIterable() { + structure.setAsyncIterable(true); + + auto method = structure.initAsyncIterator(); + method.setName(name); + using Traits = FunctionTraits; + BuildRtti::build(method.initReturnType(), rtti); + using Args = typename Traits::ArgsTuple; + TupleRttiBuilder::build(method.initArgs(std::tuple_size_v), rtti); + } }; template @@ -637,13 +675,15 @@ struct HasConstructor : std::true_type { }; template struct BuildRtti::value>> { static void build(Type::Builder builder, Builder& rtti) { - auto name = jsg::typeName(typeid(T)); - builder.initStructure().setName(name); + auto structure = builder.initStructure(); + structure.setName(jsg::typeName(typeid(T))); + structure.setFullyQualifiedName(jsg::fullyQualifiedTypeName(typeid(T))); rtti.template structure(); } static void build(Structure::Builder builder, Builder& rtti) { builder.setName(jsg::typeName(typeid(T))); + builder.setFullyQualifiedName(jsg::fullyQualifiedTypeName(typeid(T))); MemberCounter counter; if constexpr (isDetected()) { diff --git a/src/workerd/jsg/util.c++ b/src/workerd/jsg/util.c++ index 36a47da75ab..b08ad9bc4e7 100644 --- a/src/workerd/jsg/util.c++ +++ b/src/workerd/jsg/util.c++ @@ -22,12 +22,18 @@ bool getCommonJsExportDefault(v8::Isolate* isolate) { return jsgIsolate.getCommonJsExportDefault(); } -kj::String typeName(const std::type_info& type) { +kj::String fullyQualifiedTypeName(const std::type_info& type) { int status; char* buf = abi::__cxa_demangle(type.name(), nullptr, nullptr, &status); kj::String result = kj::str(buf == nullptr ? type.name() : buf); free(buf); + return kj::mv(result); +} + +kj::String typeName(const std::type_info& type) { + auto result = fullyQualifiedTypeName(type); + // Strip namespace, if any. KJ_IF_MAYBE(pos, result.findLast(':')) { result = kj::str(result.slice(*pos + 1)); diff --git a/src/workerd/jsg/util.h b/src/workerd/jsg/util.h index f6ba41da31c..85bca00e298 100644 --- a/src/workerd/jsg/util.h +++ b/src/workerd/jsg/util.h @@ -23,6 +23,7 @@ typedef unsigned int uint; bool getCaptureThrowsAsRejections(v8::Isolate* isolate); bool getCommonJsExportDefault(v8::Isolate* isolate); +kj::String fullyQualifiedTypeName(const std::type_info& type); kj::String typeName(const std::type_info& type); kj::String annotateBroken(kj::StringPtr internalMessage, kj::StringPtr brokenessReason); diff --git a/src/workerd/server/workerd-api.c++ b/src/workerd/server/workerd-api.c++ index 33591fe657c..54742a846f5 100644 --- a/src/workerd/server/workerd-api.c++ +++ b/src/workerd/server/workerd-api.c++ @@ -33,6 +33,10 @@ JSG_DECLARE_ISOLATE_TYPE(JsgWorkerdIsolate, // of the list is in alphabetical order for easier readability (the // actual order of the items is unimportant), followed by additional // types defined in worker.c++ or as part of jsg. + // + // When adding a new NNNN_ISOLATE_TYPES macro, remember to add it to + // src/workerd/tools/api-encoder.c++ too, so it gets included in the + // TypeScript types. EW_GLOBAL_SCOPE_ISOLATE_TYPES, EW_ACTOR_ISOLATE_TYPES, diff --git a/src/workerd/tools/BUILD.bazel b/src/workerd/tools/BUILD.bazel new file mode 100644 index 00000000000..8b904599bf1 --- /dev/null +++ b/src/workerd/tools/BUILD.bazel @@ -0,0 +1,25 @@ +load("//:build/wd_cc_binary.bzl", "wd_cc_binary") +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") + +wd_cc_binary( + name = "api_encoder_bin", + srcs = ["api-encoder.c++"], + visibility = ["//visibility:public"], + deps = [ + "//src/workerd/io", + "//src/workerd/jsg", + "//src/workerd/jsg:rtti", + "@capnp-cpp//src/capnp:capnpc", + ], +) + +run_binary( + name = "api_encoder", + outs = ["api.capnp.bin"], + args = [ + "--output", + "$(location api.capnp.bin)", + ], + tool = "api_encoder_bin", + visibility = ["//visibility:public"], +) diff --git a/src/workerd/tools/api-encoder.c++ b/src/workerd/tools/api-encoder.c++ new file mode 100644 index 00000000000..23a588468e4 --- /dev/null +++ b/src/workerd/tools/api-encoder.c++ @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EW_TYPE_GROUP_FOR_EACH(F) \ + F("dom-exception", jsg::DOMException) \ + F("global-scope", EW_GLOBAL_SCOPE_ISOLATE_TYPES) \ + F("durable-objects", EW_ACTOR_ISOLATE_TYPES) \ + F("durable-objects-state", EW_ACTOR_STATE_ISOLATE_TYPES) \ + F("analytics-engine", EW_ANALYTICS_ENGINE_ISOLATE_TYPES) \ + F("basics", EW_BASICS_ISOLATE_TYPES) \ + F("blob", EW_BLOB_ISOLATE_TYPES) \ + F("cache", EW_CACHE_ISOLATE_TYPES) \ + F("crypto", EW_CRYPTO_ISOLATE_TYPES) \ + F("encoding", EW_ENCODING_ISOLATE_TYPES) \ + F("form-data", EW_FORMDATA_ISOLATE_TYPES) \ + F("html-rewriter", EW_HTML_REWRITER_ISOLATE_TYPES) \ + F("http", EW_HTTP_ISOLATE_TYPES) \ + F("kv", EW_KV_ISOLATE_TYPES) \ + F("r2-admin", EW_R2_PUBLIC_BETA_ADMIN_ISOLATE_TYPES) \ + F("r2", EW_R2_PUBLIC_BETA_ISOLATE_TYPES) \ + F("scheduled", EW_SCHEDULED_ISOLATE_TYPES) \ + F("streams", EW_STREAMS_ISOLATE_TYPES) \ + F("trace", EW_TRACE_ISOLATE_TYPES) \ + F("url", EW_URL_ISOLATE_TYPES) \ + F("url-standard", EW_URL_STANDARD_ISOLATE_TYPES) \ + F("url-pattern", EW_URLPATTERN_ISOLATE_TYPES) \ + F("websocket", EW_WEBSOCKET_ISOLATE_TYPES) + +namespace workerd::api { +namespace { + +using namespace jsg; + +struct ApiEncoderErrorReporterImpl : public Worker::ValidationErrorReporter { + void addError(kj::String error) override { errors.add(kj::mv(error)); } + void addHandler(kj::Maybe exportName, + kj::StringPtr type) override { + KJ_UNREACHABLE; + } + + kj::Vector errors; +}; + +struct ApiEncoderMain { + explicit ApiEncoderMain(kj::ProcessContext &context) : context(context) {} + + kj::MainFunc getMain() { + return kj::MainBuilder(context, "", "API Encoder") + .addOptionWithArg({"o", "output"}, KJ_BIND_METHOD(*this, setOutput), + "", "Output to ") + .callAfterParsing(KJ_BIND_METHOD(*this, run)) + .build(); + } + + kj::MainBuilder::Validity setOutput(kj::StringPtr value) { + output = value; + return true; + } + + CompatibilityFlags::Reader + compileFlags(capnp::MessageBuilder &message, kj::StringPtr compatDate, + kj::ArrayPtr compatFlags) { + // Based on src/workerd/io/compatibility-date-test.c++ + auto orphanage = message.getOrphanage(); + auto flagListOrphan = + orphanage.newOrphan>(compatFlags.size()); + auto flagList = flagListOrphan.get(); + for (auto i : kj::indices(compatFlags)) { + flagList.set(i, compatFlags.begin()[i]); + } + + auto output = message.initRoot(); + ApiEncoderErrorReporterImpl errorReporter; + + compileCompatibilityFlags(compatDate, flagList.asReader(), output, + errorReporter, + CompatibilityDateValidation::FUTURE_FOR_TEST); + + if (!errorReporter.errors.empty()) { + KJ_FAIL_ASSERT(kj::strArray(errorReporter.errors, "\n")); + } + + auto reader = output.asReader(); + return kj::mv(reader); + } + + bool run() { + // Create RTTI builder with all non-experimental compatibility flags enabled + // TODO(soon): generate different types for different flags, for now, we + // set the compatibility date in the future such that all flags with a + // $compatEnableDate are enabled. + capnp::MallocMessageBuilder flagsMessage; + auto flags = compileFlags(flagsMessage, "2023-01-01", {}); + auto builder = rtti::Builder(flags); + + // Build definition generator request + capnp::MallocMessageBuilder message; + auto req = message.initRoot(); + +#define EW_TYPE_GROUP_COUNT(Name, Types) groupsSize++; +#define EW_TYPE_GROUP_WRITE(Name, Types) \ + writeGroup(groups, builder, Name); + + unsigned int groupsSize = 0; + EW_TYPE_GROUP_FOR_EACH(EW_TYPE_GROUP_COUNT) + auto groups = req.initGroups(groupsSize); + groupsIndex = 0; + EW_TYPE_GROUP_FOR_EACH(EW_TYPE_GROUP_WRITE) + KJ_ASSERT(groupsIndex == groupsSize); + +#undef EW_TYPE_GROUP_COUNT +#undef EW_TYPE_GROUP_WRITE + + // Write generator request to a file or stdout if none specifed + KJ_IF_MAYBE (value, output) { + auto fs = kj::newDiskFilesystem(); + auto path = kj::Path::parse(*value); + auto writeMode = kj::WriteMode::CREATE | kj::WriteMode::MODIFY | + kj::WriteMode::CREATE_PARENT; + auto file = fs->getCurrent().openFile(path, writeMode); + auto words = capnp::messageToFlatArray(message); + auto bytes = words.asBytes(); + file->writeAll(bytes); + } else { + capnp::writeMessageToFd(1 /* stdout */, message); + } + + return true; + } + + template + void writeStructure(rtti::Builder &builder, + capnp::List::Builder structures) { + auto reader = builder.structure(); + structures.setWithCaveats(structureIndex++, reader); + } + + template + void writeGroup( + capnp::List::Builder + &groups, + rtti::Builder &builder, kj::StringPtr name) { + auto group = groups[groupsIndex++]; + group.setName(name); + + unsigned int structuresSize = sizeof...(Types); + auto structures = group.initStructures(structuresSize); + structureIndex = 0; + (writeStructure(builder, structures), ...); + KJ_ASSERT(structureIndex == structuresSize); + } + +private: + kj::ProcessContext &context; + kj::Maybe output; + + unsigned int groupsIndex = 0; + unsigned int structureIndex = 0; +}; + +} // namespace +} // namespace workerd::api + +KJ_MAIN(workerd::api::ApiEncoderMain); From 8192143c4cbc474d3c3f9b164ba06015e8b769c6 Mon Sep 17 00:00:00 2001 From: bcoll Date: Sat, 15 Oct 2022 19:41:54 +0100 Subject: [PATCH 2/5] Setup Bazel for JavaScript and compile `rtti.capnp` to JavaScript --- BUILD.bazel | 16 + WORKSPACE | 52 + build/js_capnp_library.bzl | 124 +++ package.json | 25 + patches/capnp-ts@0.7.0.patch | 11 + pnpm-lock.yaml | 1767 ++++++++++++++++++++++++++++++++++ src/workerd/jsg/BUILD.bazel | 14 + 7 files changed, 2009 insertions(+) create mode 100644 build/js_capnp_library.bzl create mode 100644 package.json create mode 100644 patches/capnp-ts@0.7.0.patch create mode 100644 pnpm-lock.yaml diff --git a/BUILD.bazel b/BUILD.bazel index f6d1d33e741..cefdd5e3b92 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,5 +1,8 @@ load("@capnp-cpp//src/capnp:cc_capnp_library.bzl", "cc_capnp_library") load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") +load("@aspect_rules_js//npm:defs.bzl", "npm_link_package", "npm_package") +load("@npm//:defs.bzl", "npm_link_all_packages") +load("@npm//:capnpc-ts/package_json.bzl", capnpc_ts_bin = "bin") cc_capnp_library( name = "icudata-embed", @@ -13,3 +16,16 @@ cc_capnp_library( refresh_compile_commands( name = "refresh_compile_commands", ) + +npm_link_all_packages(name = "node_modules") + +npm_link_package( + name = "node_modules/@workerd/jsg", + src = "//src/workerd/jsg:jsg_js", + package = "@workerd/jsg", +) + +capnpc_ts_bin.capnpc_ts_binary( + name = "capnpc_ts", + visibility = ["//visibility:public"], +) diff --git a/WORKSPACE b/WORKSPACE index e43598b5850..4ee56e8ed4e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -153,6 +153,58 @@ load("//rust-deps/cxxbridge_crates:crates.bzl", cxxbridge_repositories = "crate_ cxxbridge_repositories() +# ======================================================================================== +# Node.js bootstrap +# +# workerd uses Node.js scripts for generating TypeScript types. + +http_archive( + name = "aspect_rules_js", + sha256 = "b9fde0f20de6324ad443500ae738bda00facbd73900a12b417ce794856e01407", + strip_prefix = "rules_js-1.5.0", + url = "https://github.com/aspect-build/rules_js/archive/refs/tags/v1.5.0.tar.gz", +) + +http_archive( + name = "aspect_rules_ts", + sha256 = "743f0e988e4e3f1e25e52c79f9dc3da1ddd77507ae88787ae95b4e70c537872b", + strip_prefix = "rules_ts-1.0.0-rc4", + url = "https://github.com/aspect-build/rules_ts/archive/refs/tags/v1.0.0-rc4.tar.gz", +) + +load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies") + +rules_js_dependencies() + +load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains") + +nodejs_register_toolchains( + name = "nodejs", + node_version = "18.10.0", +) + +load("@aspect_rules_ts//ts:repositories.bzl", TS_LATEST_VERSION = "LATEST_VERSION", "rules_ts_dependencies") + +rules_ts_dependencies(ts_version = TS_LATEST_VERSION) + +load("@aspect_rules_js//npm:npm_import.bzl", "npm_translate_lock") + +npm_translate_lock( + name = "npm", + pnpm_lock = "//:pnpm-lock.yaml", + # Patches required for `capnp-ts` to type-check + patches = { + "capnp-ts@0.7.0": ["//:patches/capnp-ts@0.7.0.patch"], + }, + patch_args = { + "capnp-ts@0.7.0": ["-p1"], + }, +) + +load("@npm//:repositories.bzl", "npm_repositories") + +npm_repositories() + # ======================================================================================== # V8 and its dependencies # diff --git a/build/js_capnp_library.bzl b/build/js_capnp_library.bzl new file mode 100644 index 00000000000..85f190fe06f --- /dev/null +++ b/build/js_capnp_library.bzl @@ -0,0 +1,124 @@ +""" +Bazel rule to compile .capnp files into JavaScript using capnp-ts. +Based on https://github.com/capnproto/capnproto/blob/3b2e368cecc4b1419b40c5970d74a7a342224fac/c++/src/capnp/cc_capnp_library.bzl. +""" + +load("@aspect_rules_js//js:defs.bzl", "js_library") + +capnp_provider = provider("Capnproto Provider", fields = { + "includes": "includes for this target (transitive)", + "inputs": "src + data for the target", + "src_prefix": "src_prefix of the target", +}) + +def _workspace_path(label, path): + if label.workspace_root == "": + return path + return label.workspace_root + "/" + path + +def _capnp_gen_impl(ctx): + label = ctx.label + src_prefix = _workspace_path(label, ctx.attr.src_prefix) + includes = [] + + inputs = ctx.files.srcs + ctx.files.data + for dep_target in ctx.attr.deps: + includes += dep_target[capnp_provider].includes + inputs += dep_target[capnp_provider].inputs + + if src_prefix != "": + includes.append(src_prefix) + + system_include = ctx.files._capnp_system[0].dirname.removesuffix("/capnp") + + out_dir = ctx.var["GENDIR"] + if src_prefix != "": + out_dir = out_dir + "/" + src_prefix + + js_out = "-o%s:%s" % (ctx.executable._capnpc_ts.path, out_dir) + args = ctx.actions.args() + args.add_all(["compile", "--verbose", js_out]) + args.add_all(["-I" + inc for inc in includes]) + args.add_all(["-I", system_include]) + if src_prefix != "": + args.add_all(["--src-prefix", src_prefix]) + + args.add_all([s for s in ctx.files.srcs]) + + ctx.actions.run( + inputs = inputs + ctx.files._capnpc_ts + ctx.files._capnpc_capnp + ctx.files._capnp_system, + tools = [ctx.executable._capnpc_ts], # Include required js_binary runfiles + outputs = ctx.outputs.outs, + executable = ctx.executable._capnpc, + arguments = [args], + mnemonic = "GenCapnp", + ) + + return [ + capnp_provider( + includes = includes, + inputs = inputs, + src_prefix = src_prefix, + ), + ] + +_capnp_gen = rule( + attrs = { + "srcs": attr.label_list(allow_files = True), + "deps": attr.label_list(providers = [capnp_provider]), + "data": attr.label_list(allow_files = True), + "outs": attr.output_list(), + "src_prefix": attr.string(), + "_capnpc": attr.label(executable = True, allow_single_file = True, cfg = "exec", default = "@capnp-cpp//src/capnp:capnp_tool"), + "_capnpc_ts": attr.label(executable = True, allow_single_file = True, cfg = "exec", default = "//:capnpc_ts"), + "_capnpc_capnp": attr.label(executable = True, allow_single_file = True, cfg = "exec", default = "@capnp-cpp//src/capnp:capnpc-capnp"), + "_capnp_system": attr.label(default = "@capnp-cpp//src/capnp:capnp_system_library"), + }, + output_to_genfiles = True, + implementation = _capnp_gen_impl, +) + +def js_capnp_library( + name, + srcs = [], + data = [], + deps = [], + src_prefix = "", + visibility = None, + target_compatible_with = None, + **kwargs): + """Bazel rule to create a JavaScript capnproto library from capnp source files + + Args: + name: library name + srcs: list of files to compile + data: additional files to provide to the compiler - data files and includes that need not to + be compiled + deps: other js_capnp_library rules to depend on + src_prefix: src_prefix for capnp compiler to the source root + visibility: rule visibility + target_compatible_with: target compatibility + **kwargs: rest of the arguments to js_library rule + """ + + js_files = [s + ".js" for s in srcs] + d_ts_files = [s + ".d.ts" for s in srcs] + + _capnp_gen( + name = name + "_gen", + srcs = srcs, + deps = [s + "_gen" for s in deps], + data = data, + outs = js_files + d_ts_files, + src_prefix = src_prefix, + visibility = visibility, + target_compatible_with = target_compatible_with, + ) + js_library( + name = name, + srcs = js_files + d_ts_files, + deps = deps + ["//:node_modules/capnp-ts"], + visibility = visibility, + target_compatible_with = target_compatible_with, + **kwargs + ) diff --git a/package.json b/package.json new file mode 100644 index 00000000000..24f7097d374 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "@cloudflare/workerd-root", + "private": true, + "scripts": { + "lint": "eslint types/src" + }, + "dependencies": { + "capnp-ts": "^0.7.0", + "prettier": "^2.7.1", + "typescript": "~4.7.4" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/node": "^18.7.18", + "@types/prettier": "^2.7.1", + "@typescript-eslint/eslint-plugin": "^5.37.0", + "@typescript-eslint/parser": "^5.37.0", + "capnpc-ts": "^0.7.0", + "esbuild": "^0.15.7", + "eslint": "^8.22.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-prettier": "^4.2.1" + } +} diff --git a/patches/capnp-ts@0.7.0.patch b/patches/capnp-ts@0.7.0.patch new file mode 100644 index 00000000000..a022c775563 --- /dev/null +++ b/patches/capnp-ts@0.7.0.patch @@ -0,0 +1,11 @@ +--- a/src/serialization/pointers/struct.ts ++++ b/src/serialization/pointers/struct.ts +@@ -107,8 +107,6 @@ export class Struct extends Pointer { + static readonly setText = setText; + static readonly testWhich = testWhich; + +- readonly _capnp!: _Struct; +- + /** + * Create a new pointer to a struct. + * diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000000..e00637c3ac9 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1767 @@ +lockfileVersion: 5.4 + +specifiers: + '@types/debug': ^4.1.7 + '@types/node': ^18.7.18 + '@types/prettier': ^2.7.1 + '@typescript-eslint/eslint-plugin': ^5.37.0 + '@typescript-eslint/parser': ^5.37.0 + capnp-ts: ^0.7.0 + capnpc-ts: ^0.7.0 + esbuild: ^0.15.7 + eslint: ^8.22.0 + eslint-config-prettier: ^8.5.0 + eslint-plugin-import: ^2.26.0 + eslint-plugin-prettier: ^4.2.1 + prettier: ^2.7.1 + typescript: ~4.7.4 + +dependencies: + capnp-ts: 0.7.0 + prettier: 2.7.1 + typescript: 4.7.4 + +devDependencies: + '@types/debug': 4.1.7 + '@types/node': 18.8.5 + '@types/prettier': 2.7.1 + '@typescript-eslint/eslint-plugin': 5.40.0_ik43yucnjuzsopqc3tao6tk3x4 + '@typescript-eslint/parser': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + capnpc-ts: 0.7.0 + esbuild: 0.15.10 + eslint: 8.25.0 + eslint-config-prettier: 8.5.0_eslint@8.25.0 + eslint-plugin-import: 2.26.0_zb5prbqp7qzcgafjm73dfpyyvm + eslint-plugin-prettier: 4.2.1_hvbqyfstm4urdpm6ffpwfka4e4 + +packages: + + /@esbuild/android-arm/0.15.10: + resolution: {integrity: sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.15.10: + resolution: {integrity: sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@eslint/eslintrc/1.3.3: + resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.4.0 + globals: 13.17.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/config-array/0.10.7: + resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer/1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@types/debug/4.1.7: + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + dependencies: + '@types/ms': 0.7.31 + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/json5/0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/ms/0.7.31: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: true + + /@types/node/18.8.5: + resolution: {integrity: sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==} + dev: true + + /@types/prettier/2.7.1: + resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} + dev: true + + /@typescript-eslint/eslint-plugin/5.40.0_ik43yucnjuzsopqc3tao6tk3x4: + resolution: {integrity: sha512-FIBZgS3DVJgqPwJzvZTuH4HNsZhHMa9SjxTKAZTlMsPw/UzpEjcf9f4dfgDJEHjK+HboUJo123Eshl6niwEm/Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + '@typescript-eslint/scope-manager': 5.40.0 + '@typescript-eslint/type-utils': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + '@typescript-eslint/utils': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + debug: 4.3.4 + eslint: 8.25.0 + ignore: 5.2.0 + regexpp: 3.2.0 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/5.40.0_l76llshcx5hpzqzl7lfng4kpb4: + resolution: {integrity: sha512-Ah5gqyX2ySkiuYeOIDg7ap51/b63QgWZA7w6AHtFrag7aH0lRQPbLzUjk0c9o5/KZ6JRkTTDKShL4AUrQa6/hw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.40.0 + '@typescript-eslint/types': 5.40.0 + '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.7.4 + debug: 4.3.4 + eslint: 8.25.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.40.0: + resolution: {integrity: sha512-d3nPmjUeZtEWRvyReMI4I1MwPGC63E8pDoHy0BnrYjnJgilBD3hv7XOiETKLY/zTwI7kCnBDf2vWTRUVpYw0Uw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.40.0 + '@typescript-eslint/visitor-keys': 5.40.0 + dev: true + + /@typescript-eslint/type-utils/5.40.0_l76llshcx5hpzqzl7lfng4kpb4: + resolution: {integrity: sha512-nfuSdKEZY2TpnPz5covjJqav+g5qeBqwSHKBvz7Vm1SAfy93SwKk/JeSTymruDGItTwNijSsno5LhOHRS1pcfw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.7.4 + '@typescript-eslint/utils': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + debug: 4.3.4 + eslint: 8.25.0 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.40.0: + resolution: {integrity: sha512-V1KdQRTXsYpf1Y1fXCeZ+uhjW48Niiw0VGt4V8yzuaDTU8Z1Xl7yQDyQNqyAFcVhpYXIVCEuxSIWTsLDpHgTbw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.40.0_typescript@4.7.4: + resolution: {integrity: sha512-b0GYlDj8TLTOqwX7EGbw2gL5EXS2CPEWhF9nGJiGmEcmlpNBjyHsTwbqpyIEPVpl6br4UcBOYlcI2FJVtJkYhg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.40.0 + '@typescript-eslint/visitor-keys': 5.40.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.40.0_l76llshcx5hpzqzl7lfng4kpb4: + resolution: {integrity: sha512-MO0y3T5BQ5+tkkuYZJBjePewsY+cQnfkYeRqS6tPh28niiIwPnQ1t59CSRcs1ZwJJNOdWw7rv9pF8aP58IMihA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 5.40.0 + '@typescript-eslint/types': 5.40.0 + '@typescript-eslint/typescript-estree': 5.40.0_typescript@4.7.4 + eslint: 8.25.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.25.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.40.0: + resolution: {integrity: sha512-ijJ+6yig+x9XplEpG2K6FUdJeQGGj/15U3S56W9IqXKJqleuD7zJ2AX/miLezwxpd7ZxDAqO87zWufKg+RPZyQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.40.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.0: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.0 + dev: true + + /acorn/8.8.0: + resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-includes/3.1.5: + resolution: {integrity: sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + get-intrinsic: 1.1.3 + is-string: 1.0.7 + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat/1.3.0: + resolution: {integrity: sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + es-shim-unscopables: 1.0.0 + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.3 + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /capnp-ts/0.5.1: + resolution: {integrity: sha512-Bg68xRboJiEyUqOaxAM5Xbozadqkc/Q7Lu01Qv6CoF4wCtf8fCsm0uh/OzgcTJCa8d0dTm8+ELfm9WR95XcOmA==} + dependencies: + debug: 4.3.4 + format: 0.2.2 + tslib: 2.4.0 + utf8-encoding: 0.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /capnp-ts/0.7.0: + resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} + dependencies: + debug: 4.3.4 + tslib: 2.4.0 + transitivePeerDependencies: + - supports-color + dev: false + + /capnpc-ts/0.7.0: + resolution: {integrity: sha512-aIvGuLQJMEqRYeT92Lr3rzRyyb2hPePg7XpjIKfGLa/whaZ582IU8W4VqFvbbjKimlNE41ISCorX6kv4lSFk4A==} + hasBin: true + dependencies: + capnp-ts: 0.5.1 + debug: 4.3.4 + hex2dec: 1.1.2 + mkdirp: 1.0.4 + tslib: 2.4.0 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-properties/1.1.4: + resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /es-abstract/1.20.4: + resolution: {integrity: sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.3 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 + dev: true + + /es-shim-unscopables/1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild-android-64/0.15.10: + resolution: {integrity: sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.15.10: + resolution: {integrity: sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.15.10: + resolution: {integrity: sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.15.10: + resolution: {integrity: sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.15.10: + resolution: {integrity: sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.15.10: + resolution: {integrity: sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-32/0.15.10: + resolution: {integrity: sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.15.10: + resolution: {integrity: sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.15.10: + resolution: {integrity: sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.15.10: + resolution: {integrity: sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.15.10: + resolution: {integrity: sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.15.10: + resolution: {integrity: sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.15.10: + resolution: {integrity: sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.15.10: + resolution: {integrity: sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.15.10: + resolution: {integrity: sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.15.10: + resolution: {integrity: sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64/0.15.10: + resolution: {integrity: sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.15.10: + resolution: {integrity: sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.15.10: + resolution: {integrity: sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.15.10: + resolution: {integrity: sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.15.10: + resolution: {integrity: sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.10 + '@esbuild/linux-loong64': 0.15.10 + esbuild-android-64: 0.15.10 + esbuild-android-arm64: 0.15.10 + esbuild-darwin-64: 0.15.10 + esbuild-darwin-arm64: 0.15.10 + esbuild-freebsd-64: 0.15.10 + esbuild-freebsd-arm64: 0.15.10 + esbuild-linux-32: 0.15.10 + esbuild-linux-64: 0.15.10 + esbuild-linux-arm: 0.15.10 + esbuild-linux-arm64: 0.15.10 + esbuild-linux-mips64le: 0.15.10 + esbuild-linux-ppc64le: 0.15.10 + esbuild-linux-riscv64: 0.15.10 + esbuild-linux-s390x: 0.15.10 + esbuild-netbsd-64: 0.15.10 + esbuild-openbsd-64: 0.15.10 + esbuild-sunos-64: 0.15.10 + esbuild-windows-32: 0.15.10 + esbuild-windows-64: 0.15.10 + esbuild-windows-arm64: 0.15.10 + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier/8.5.0_eslint@8.25.0: + resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.25.0 + dev: true + + /eslint-import-resolver-node/0.3.6: + resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} + dependencies: + debug: 3.2.7 + resolve: 1.22.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils/2.7.4_c3hlus4v72tewog5wytziddckm: + resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + debug: 3.2.7 + eslint: 8.25.0 + eslint-import-resolver-node: 0.3.6 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import/2.26.0_zb5prbqp7qzcgafjm73dfpyyvm: + resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.40.0_l76llshcx5hpzqzl7lfng4kpb4 + array-includes: 3.1.5 + array.prototype.flat: 1.3.0 + debug: 2.6.9 + doctrine: 2.1.0 + eslint: 8.25.0 + eslint-import-resolver-node: 0.3.6 + eslint-module-utils: 2.7.4_c3hlus4v72tewog5wytziddckm + has: 1.0.3 + is-core-module: 2.10.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.5 + resolve: 1.22.1 + tsconfig-paths: 3.14.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-prettier/4.2.1_hvbqyfstm4urdpm6ffpwfka4e4: + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.25.0 + eslint-config-prettier: 8.5.0_eslint@8.25.0 + prettier: 2.7.1 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.25.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.25.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.25.0: + resolution: {integrity: sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.3.3 + '@humanwhocodes/config-array': 0.10.7 + '@humanwhocodes/module-importer': 1.0.1 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.25.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.0 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.17.0 + globby: 11.1.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-sdsl: 4.1.5 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/9.4.0: + resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.0 + acorn-jsx: 5.3.2_acorn@8.8.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff/1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted/3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /format/0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-intrinsic/1.1.3: + resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals/13.17.0: + resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.1.3 + dev: true + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /hex2dec/1.1.2: + resolution: {integrity: sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA==} + dev: true + + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot/1.0.3: + resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.3 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable/1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module/2.10.0: + resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-negative-zero/2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-weakref/1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-sdsl/4.1.5: + resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==} + dev: true + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5/1.0.1: + resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + hasBin: true + dependencies: + minimist: 1.2.7 + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist/1.2.7: + resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + dev: true + + /mkdirp/1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /ms/2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /object-inspect/1.12.2: + resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + dev: true + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign/4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.values/1.1.5: + resolution: {integrity: sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /obtain-unicode/0.0.5: + resolution: {integrity: sha512-0BQHvF6VCYEdXiRYlxRo5X/2fKFau/eymrhs453IA8W610+nEelS5mhvanWeiSnSyKfw/hjhB4MlogxannjQVA==} + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers/1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.2.0 + dev: true + + /prettier/2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} + engines: {node: '>=10.13.0'} + hasBin: true + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.10.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-regex-test/1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + is-regex: 1.1.4 + dev: true + + /semver/7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.3 + object-inspect: 1.12.2 + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.4 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tsconfig-paths/3.14.1: + resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.1 + minimist: 1.2.7 + strip-bom: 3.0.0 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + /tsutils/3.21.0_typescript@4.7.4: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.7.4 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + engines: {node: '>=4.2.0'} + hasBin: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /utf8-encoding/0.1.2: + resolution: {integrity: sha512-TMrA9HlIUzIbBiEKeCzjFcNrDyjaz0kdpJtcSXu1UT0f/fYyo2Ij9oH2ChIHEA+UUPwtA/mRXfgu+OUa2s3x6A==} + dependencies: + obtain-unicode: 0.0.5 + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/workerd/jsg/BUILD.bazel b/src/workerd/jsg/BUILD.bazel index 8b88e3cc4b9..fc24013f401 100644 --- a/src/workerd/jsg/BUILD.bazel +++ b/src/workerd/jsg/BUILD.bazel @@ -1,6 +1,8 @@ load("//:build/wd_cc_library.bzl", "wd_cc_library") +load("//:build/js_capnp_library.bzl", "js_capnp_library") load("//:build/wd_cc_capnp_library.bzl", "wd_cc_capnp_library") load("//:build/kj_test.bzl", "kj_test") +load("@aspect_rules_js//npm:defs.bzl", "npm_package") wd_cc_library( name = "jsg", @@ -26,6 +28,18 @@ wd_cc_capnp_library( visibility = ["//visibility:public"], ) +js_capnp_library( + name = "rtti_capnp_js", + srcs = ["rtti.capnp"], + visibility = ["//visibility:public"], +) + +npm_package( + name = "jsg_js", + srcs = [":rtti_capnp_js"], + visibility = ["//visibility:public"], +) + wd_cc_library( name = "rtti", hdrs = ["rtti.h"], From 7fc1fc592f8eab60dbb0205b95967d5714b9c492 Mon Sep 17 00:00:00 2001 From: bcoll Date: Sat, 15 Oct 2022 19:43:30 +0100 Subject: [PATCH 3/5] Generate TypeScript types from RTTI --- .gitignore | 3 + types/.eslintrc.js | 25 ++ types/BUILD.bazel | 44 ++++ types/README.md | 28 +++ types/src/generator/index.ts | 183 ++++++++++++++ types/src/generator/structure.ts | 405 +++++++++++++++++++++++++++++++ types/src/generator/type.ts | 317 ++++++++++++++++++++++++ types/src/index.ts | 76 ++++++ types/src/print.ts | 22 ++ types/src/program.ts | 35 +++ types/tsconfig.json | 23 ++ 11 files changed, 1161 insertions(+) create mode 100644 types/.eslintrc.js create mode 100644 types/BUILD.bazel create mode 100644 types/README.md create mode 100644 types/src/generator/index.ts create mode 100644 types/src/generator/structure.ts create mode 100644 types/src/generator/type.ts create mode 100644 types/src/index.ts create mode 100644 types/src/print.ts create mode 100644 types/src/program.ts create mode 100644 types/tsconfig.json diff --git a/.gitignore b/.gitignore index 01f62dca2b0..73c23ed5610 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.idea +.DS_Store + /rust-deps/target /rust-deps/Cargo.toml diff --git a/types/.eslintrc.js b/types/.eslintrc.js new file mode 100644 index 00000000000..1f66af022f0 --- /dev/null +++ b/types/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: "@typescript-eslint/parser", + extends: ["plugin:prettier/recommended"], + plugins: ["import"], + rules: { + "import/order": ["warn", { alphabetize: { order: "asc" } }], + "sort-imports": ["warn", { ignoreDeclarationSort: true }], + }, + overrides: [ + { + files: ["*.ts"], + extends: ["plugin:@typescript-eslint/recommended"], + rules: { + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_" }, + ], + }, + }, + ], +}; diff --git a/types/BUILD.bazel b/types/BUILD.bazel new file mode 100644 index 00000000000..8a4e9c8e010 --- /dev/null +++ b/types/BUILD.bazel @@ -0,0 +1,44 @@ +load("@aspect_rules_ts//ts:defs.bzl", "ts_project") +load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary") + +ts_project( + name = "types_lib", + srcs = glob(["src/**/*"]), + allow_js = True, + source_map = True, + tsconfig = "//types:tsconfig.json", + deps = [ + "//:node_modules/@types", + "//:node_modules/@workerd/jsg", + "//:node_modules/capnp-ts", + "//:node_modules/prettier", + "//:node_modules/typescript", + ], +) + +js_binary( + name = "types_bin", + data = [ + ":types_lib", + ], + entry_point = "src/index.js", +) + +js_run_binary( + name = "types", + srcs = [ + "//src/workerd/tools:api_encoder", + ], + outs = ["api.d.ts"], # TODO(soon) switch to out_dirs when generating multiple files + args = [ + "src/workerd/tools/api.capnp.bin", + "--output", + "types/api.d.ts", + "--format", + ], + env = { + "NODE_OPTIONS": "--enable-source-maps", + }, + silent_on_success = False, # Always enable logging for debugging + tool = ":types_bin", +) diff --git a/types/README.md b/types/README.md new file mode 100644 index 00000000000..3cd95bb31e2 --- /dev/null +++ b/types/README.md @@ -0,0 +1,28 @@ +# Workers Types Generator + +This directory contains scripts for automatically generating TypeScript types +from [JSG RTTI](../src/workerd/jsg/rtti.h). + +## Generating Types + +```shell +# Generates types to `../bazel-bin/types/api.d.ts` +$ bazel build //types:types +``` + +## Developing Generator Scripts + +```shell +# Generates JSG RTTI Cap’n Proto JavaScript/TypeScript files +$ bazel build //src/workerd/jsg:rtti_capnp_js +# Install dependencies (note pnpm is required by https://github.com/aspect-build/rules_js) +$ pnpm install +``` + +## Structure + +- `src/generator`: generating TypeScript AST nodes from JSG RTTI +- `src/transforms`: post-processing TypeScript AST transforms +- `src/index.ts`: main entrypoint +- `src/{print,program}.ts`: helpers for printing nodes and creating programs +- `workerd`: symlink required to resolve JSG RTTI Cap’n Proto files during development diff --git a/types/src/generator/index.ts b/types/src/generator/index.ts new file mode 100644 index 00000000000..ccad1dd0bad --- /dev/null +++ b/types/src/generator/index.ts @@ -0,0 +1,183 @@ +import assert from "assert"; +import { + DefinitionGeneratorRequest, + FunctionType, + Member, + Member_Which, + Method, + Structure, + Type, + Type_Which, +} from "@workerd/jsg/rtti.capnp.js"; +import ts from "typescript"; +import { createStructureNode } from "./structure"; + +type StructureMap = Map; +// Builds a lookup table mapping type names to structures +function collectStructureMap(req: DefinitionGeneratorRequest): StructureMap { + const map = new Map(); + req.getGroups().forEach((group) => { + group.getStructures().forEach((structure) => { + map.set(structure.getFullyQualifiedName(), structure); + }); + }); + return map; +} + +// Types to visit in `collectIncluded` for finding types to include +// (global scope and bindings types) +// TODO(soon): replace this with a macro like JSG_TS_ROOT or JSG_TS_BINDING_TYPE +const TYPE_ROOTS = [ + "workerd::api::ServiceWorkerGlobalScope", + "workerd::api::DurableObjectNamespace", + "workerd::api::AnalyticsEngine", + "workerd::api::KvNamespace", + "workerd::api::public_beta::R2Bucket", +]; + +// Builds a set containing the names of structures that should be included +// in the definitions, because they are referenced by root types or any of their +// children. +// +// We need to do this as some types should only be included in the definitions +// when certain compatibility flags are enabled (e.g. `Navigator`, +// standards-compliant `URL`). However, these types are always included in +// the *_TYPES macros. +function collectIncluded(map: StructureMap): Set { + const included = new Set(); + + function visitType(type: Type): void { + switch (type.which()) { + case Type_Which.PROMISE: + return visitType(type.getPromise().getValue()); + case Type_Which.STRUCTURE: + const name = type.getStructure().getFullyQualifiedName(); + const structure = map.get(name); + assert(structure !== undefined, `Unknown structure type: ${name}`); + return visitStructure(structure); + case Type_Which.ARRAY: + return visitType(type.getArray().getElement()); + case Type_Which.MAYBE: + return visitType(type.getMaybe().getValue()); + case Type_Which.DICT: + const dict = type.getDict(); + visitType(dict.getKey()); + return visitType(dict.getValue()); + case Type_Which.ONE_OF: + return type.getOneOf().getVariants().forEach(visitType); + case Type_Which.FUNCTION: + return visitFunction(type.getFunction()); + } + } + + function visitFunction(func: FunctionType | Method) { + func.getArgs().forEach(visitType); + return visitType(func.getReturnType()); + } + + function visitMember(member: Member) { + switch (member.which()) { + case Member_Which.METHOD: + return visitFunction(member.getMethod()); + case Member_Which.PROPERTY: + return visitType(member.getProperty().getType()); + case Member_Which.NESTED: + return visitStructure(member.getNested().getStructure()); + case Member_Which.CONSTRUCTOR: + return member.getConstructor().getArgs().forEach(visitType); + } + } + + function visitStructure(structure: Structure): void { + const name = structure.getFullyQualifiedName(); + if (included.has(name)) return; + included.add(name); + structure.getMembers().forEach(visitMember); + if (structure.hasExtends()) { + visitType(structure.getExtends()); + } + if (structure.hasIterator()) { + visitFunction(structure.getIterator()); + } + if (structure.hasAsyncIterator()) { + visitFunction(structure.getAsyncIterator()); + } + } + + for (const rootName of TYPE_ROOTS) { + const root = map.get(rootName); + assert(root !== undefined, `Unknown root type: ${rootName}`); + visitStructure(root); + } + + return included; +} + +// Builds a set containing the names of structures that must be declared as +// `class`es rather than `interface`s because they either: +// 1) Get inherited by another class (`class` `extends` requires another `class`) +// 2) Are constructible (`constructor(...)`s can only appear in `class`es) +// 3) Have `static` methods (`static`s can only appear in `class`es) +// 4) Are a nested type (users could call `instanceof` with the type) +function collectClasses(map: StructureMap): Set { + const classes = new Set(); + for (const structure of map.values()) { + // 1) Add all classes inherited by this class + if (structure.hasExtends()) { + const extendsType = structure.getExtends(); + if (extendsType.isStructure()) { + classes.add(extendsType.getStructure().getFullyQualifiedName()); + } + } + + structure.getMembers().forEach((member) => { + // 2) Add this class if it's constructible + if (member.isConstructor()) { + classes.add(structure.getFullyQualifiedName()); + } + // 3) Add this class if it contains static methods + if (member.isMethod() && member.getMethod().getStatic()) { + classes.add(structure.getFullyQualifiedName()); + } + // 4) Add all nested types defined by this class + if (member.isNested()) { + classes.add(member.getNested().getStructure().getFullyQualifiedName()); + } + }); + } + return classes; +} + +export function generateDefinitions( + req: DefinitionGeneratorRequest +): ts.Node[] { + const map = collectStructureMap(req); + const included = collectIncluded(map); + const classes = collectClasses(map); + + // Can't use `flatMap()` here as `getGroups()` returns a `capnp.List` + const nodes = req.getGroups().map((group) => { + const structureNodes: ts.Node[] = []; + group.getStructures().forEach((structure) => { + const name = structure.getFullyQualifiedName(); + if (included.has(name)) { + const asClass = classes.has(name); + structureNodes.push(createStructureNode(structure, asClass)); + } + }); + + // Add group label to first in group + if (structureNodes.length > 0) { + ts.addSyntheticLeadingComment( + structureNodes[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ` ${group.getName()}`, + /* hasTrailingNewLine */ true + ); + } + + return structureNodes; + }); + + return nodes.flat(); +} diff --git a/types/src/generator/structure.ts b/types/src/generator/structure.ts new file mode 100644 index 00000000000..4635a890d35 --- /dev/null +++ b/types/src/generator/structure.ts @@ -0,0 +1,405 @@ +import assert from "assert"; +import { + Constant, + Member, + Member_Nested, + Member_Which, + Method, + Property, + Structure, +} from "@workerd/jsg/rtti.capnp.js"; +import ts, { factory as f } from "typescript"; +import { printNode } from "../print"; +import { + createParamDeclarationNodes, + createTypeNode, + getTypeName, + isUnsatisfiable, + maybeUnwrapOptional, +} from "./type"; + +function createMethodPartial( + method: Method +): [ts.Modifier[], string, ts.ParameterDeclaration[], ts.TypeNode] { + const modifiers: ts.Modifier[] = []; + if (method.getStatic()) { + modifiers.push(f.createToken(ts.SyntaxKind.StaticKeyword)); + } + const name = method.getName(); + const params = createParamDeclarationNodes(method.getArgs().toArray()); + const result = createTypeNode(method.getReturnType()); + return [modifiers, name, params, result]; +} + +function createIteratorMethodPartial( + method: Method, + isAsync: boolean +): [ts.Modifier[], ts.PropertyName, ts.ParameterDeclaration[], ts.TypeNode] { + const [modifiers, , params, result] = createMethodPartial(method); + const symbolIteratorExpression = f.createPropertyAccessExpression( + f.createIdentifier("Symbol"), + isAsync ? "asyncIterator" : "iterator" + ); + const name = f.createComputedPropertyName(symbolIteratorExpression); + return [modifiers, name, params, result]; +} + +function createInstancePropertyPartial( + prop: Property +): [ts.Modifier[], string, ts.QuestionToken | undefined, ts.TypeNode] { + assert(!prop.getPrototype()); + const modifiers: ts.Modifier[] = []; + if (prop.getReadonly()) { + modifiers.push(f.createToken(ts.SyntaxKind.ReadonlyKeyword)); + } + const name = prop.getName(); + let value = createTypeNode(prop.getType()); + + // If this is an optional type, use an optional property with a `?` + let questionToken: ts.QuestionToken | undefined; + const unwrappedValue = maybeUnwrapOptional(value); + if (unwrappedValue !== undefined) { + value = unwrappedValue; + questionToken = f.createToken(ts.SyntaxKind.QuestionToken); + } + + return [modifiers, name, questionToken, value]; +} + +function createPrototypeProperty( + prop: Property +): + | ts.GetAccessorDeclaration + | [ts.GetAccessorDeclaration, ts.SetAccessorDeclaration] { + assert(prop.getPrototype()); + const name = prop.getName(); + const value = createTypeNode(prop.getType()); + + const getter = f.createGetAccessorDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + name, + /* params */ [], + value, + /* body */ undefined + ); + + if (prop.getReadonly()) { + return getter; + } else { + const param = f.createParameterDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + /* dotDotToken */ undefined, + "value", + /* questionToken */ undefined, + value + ); + const setter = f.createSetAccessorDeclaration( + undefined, + undefined, + name, + [param], + undefined + ); + return [getter, setter]; + } +} + +function createNestedPartial(nested: Member_Nested): [string, ts.TypeNode] { + const name = nested.getName(); + const targetName = getTypeName(nested.getStructure()); + const value = f.createTypeQueryNode(f.createIdentifier(targetName)); + return [name, value]; +} + +function createConstantPartial( + constant: Constant +): [ts.Modifier[], string, ts.TypeNode] { + const modifiers: ts.Modifier[] = [ + f.createToken(ts.SyntaxKind.StaticKeyword), + f.createToken(ts.SyntaxKind.ReadonlyKeyword), + ]; + const name = constant.getName(); + const value = constant.getValue().valueOf(); + const valueNode = f.createLiteralTypeNode(f.createNumericLiteral(value)); + return [modifiers, name, valueNode]; +} + +function createInterfaceMemberNode( + member: Member +): ts.TypeElement | ts.TypeElement[] { + let modifiers: ts.Modifier[]; + let name: string; + let params: ts.ParameterDeclaration[]; + let result: ts.TypeNode; + let questionToken: ts.QuestionToken | undefined; + + // noinspection FallThroughInSwitchStatementJS + switch (member.which()) { + case Member_Which.METHOD: + const method = member.getMethod(); + [modifiers, name, params, result] = createMethodPartial(method); + return f.createMethodSignature( + modifiers, + name, + /* questionToken */ undefined, + /* typeParams */ undefined, + params, + result + ); + case Member_Which.PROPERTY: + const prop = member.getProperty(); + if (prop.getPrototype()) { + return createPrototypeProperty(prop); + } else { + [modifiers, name, questionToken, result] = + createInstancePropertyPartial(prop); + return f.createPropertySignature( + modifiers, + name, + questionToken, + result + ); + } + case Member_Which.NESTED: + const nested = member.getNested(); + [name, result] = createNestedPartial(nested); + return f.createPropertySignature( + /* modifiers */ undefined, + name, + /* questionToken */ undefined, + result + ); + case Member_Which.CONSTANT: + const constant = member.getConstant(); + [modifiers, name, result] = createConstantPartial(constant); + return f.createPropertySignature( + modifiers, + name, + /* questionToken */ undefined, + result + ); + case Member_Which.CONSTRUCTOR: + assert.fail("Unexpected constructor member inside interface"); + default: + assert.fail(`Unknown member: ${member.which()}`); + } +} + +function createIteratorInterfaceMemberNode( + method: Method, + isAsync: boolean +): ts.TypeElement { + const [modifiers, name, params, result] = createIteratorMethodPartial( + method, + isAsync + ); + return f.createMethodSignature( + modifiers, + name, + /* questionToken */ undefined, + /* typeParams */ undefined, + params, + result + ); +} + +function createClassMemberNode( + member: Member +): ts.ClassElement | ts.ClassElement[] { + let modifiers: ts.Modifier[]; + let name: string; + let params: ts.ParameterDeclaration[]; + let result: ts.TypeNode; + let questionToken: ts.QuestionToken | undefined; + + switch (member.which()) { + case Member_Which.METHOD: + const method = member.getMethod(); + [modifiers, name, params, result] = createMethodPartial(method); + return f.createMethodDeclaration( + /* decorators */ undefined, + modifiers, + /* asteriskToken */ undefined, + name, + /* questionToken */ undefined, + /* typeParameters */ undefined, + params, + result, + /* body */ undefined + ); + case Member_Which.PROPERTY: + const prop = member.getProperty(); + if (prop.getPrototype()) { + return createPrototypeProperty(prop); + } else { + [modifiers, name, questionToken, result] = + createInstancePropertyPartial(prop); + return f.createPropertyDeclaration( + /* decorators */ undefined, + modifiers, + name, + questionToken, + result, + /* initialiser */ undefined + ); + } + case Member_Which.NESTED: + const nested = member.getNested(); + [name, result] = createNestedPartial(nested); + return f.createPropertyDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + name, + /* questionToken */ undefined, + result, + /* initialiser */ undefined + ); + case Member_Which.CONSTANT: + const constant = member.getConstant(); + [modifiers, name, result] = createConstantPartial(constant); + return f.createPropertyDeclaration( + /* decorators */ undefined, + modifiers, + name, + /* questionToken */ undefined, + result, + /* initialiser */ undefined + ); + case Member_Which.CONSTRUCTOR: + const constructor = member.getConstructor(); + params = createParamDeclarationNodes(constructor.getArgs().toArray()); + return f.createConstructorDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + params, + /* body */ undefined + ); + default: + assert.fail(`Unknown member: ${member.which()}`); + } +} + +function createIteratorClassMemberNode( + method: Method, + isAsync: boolean +): ts.ClassElement { + const [modifiers, name, params, result] = createIteratorMethodPartial( + method, + isAsync + ); + return f.createMethodDeclaration( + /* decorators */ undefined, + modifiers, + /* asteriskToken */ undefined, + name, + /* questionToken */ undefined, + /* typeParams */ undefined, + params, + result, + /* body */ undefined + ); +} + +// Remove all properties with type `never` and methods with return type `never` +function filterUnimplementedProperties< + T extends ts.TypeElement | ts.ClassElement +>(members: T[]): T[] { + return members.filter((member) => { + // Could collapse these `if` statements, but this is much clearer + if ( + ts.isPropertySignature(member) || + ts.isPropertyDeclaration(member) || + ts.isGetAccessorDeclaration(member) || + ts.isSetAccessorDeclaration(member) || + ts.isMethodSignature(member) || + ts.isMethodDeclaration(member) + ) { + if (member.type !== undefined && isUnsatisfiable(member.type)) { + return false; + } + } + return true; + }); +} + +export function createStructureNode(structure: Structure, asClass: boolean) { + const modifiers: ts.Modifier[] = [f.createToken(ts.SyntaxKind.ExportKeyword)]; + const name = getTypeName(structure); + + const heritage: ts.HeritageClause[] = []; + if (structure.hasExtends()) { + const typeNode = createTypeNode(structure.getExtends()); + assert( + ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName), + `Expected type reference, got "${printNode(typeNode)}"` + ); + const expr = f.createExpressionWithTypeArguments( + typeNode.typeName, + typeNode.typeArguments + ); + heritage.push(f.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [expr])); + } + + const members = structure.getMembers(); + if (asClass) { + modifiers.push(f.createToken(ts.SyntaxKind.DeclareKeyword)); + // Can't use `flatMap()` here as `members` is a `capnp.List` + const classMembers = members.map(createClassMemberNode).flat(); + + const constructorIndex = classMembers.findIndex((member) => + ts.isConstructorDeclaration(member) + ); + if (constructorIndex === -1) { + // If this class doesn't have a constructor, it must be `abstract`, as we + // never rely on the implicit default constructor. If a class can be + // constructed using the empty constructor, it always defines it. + modifiers.push(f.createToken(ts.SyntaxKind.AbstractKeyword)); + } else { + // Otherwise, ensure that the constructor always comes first + classMembers.unshift(...classMembers.splice(constructorIndex, 1)); + } + + // Add iterator members + if (structure.hasIterator()) { + const iterator = structure.getIterator(); + classMembers.push(createIteratorClassMemberNode(iterator, false)); + } + if (structure.hasAsyncIterator()) { + const iterator = structure.getAsyncIterator(); + classMembers.push(createIteratorClassMemberNode(iterator, true)); + } + + return f.createClassDeclaration( + /* decorators */ undefined, + modifiers, + name, + /* typeParams */ undefined, + heritage, + filterUnimplementedProperties(classMembers) + ); + } else { + // Can't use `flatMap()` here as `members` is a `capnp.List` + const interfaceMembers = members.map(createInterfaceMemberNode).flat(); + + // Add iterator members + if (structure.hasIterator()) { + const iterator = structure.getIterator(); + interfaceMembers.push(createIteratorInterfaceMemberNode(iterator, false)); + } + if (structure.hasAsyncIterator()) { + const iterator = structure.getAsyncIterator(); + interfaceMembers.push(createIteratorInterfaceMemberNode(iterator, true)); + } + + return f.createInterfaceDeclaration( + /* decorators */ undefined, + modifiers, + name, + /* typeParams */ undefined, + heritage, + filterUnimplementedProperties(interfaceMembers) + ); + } +} diff --git a/types/src/generator/type.ts b/types/src/generator/type.ts new file mode 100644 index 00000000000..ab6d9b4e587 --- /dev/null +++ b/types/src/generator/type.ts @@ -0,0 +1,317 @@ +import assert from "assert"; +import { + ArrayType, + BuiltinType_Type, + JsgImplType_Type, + MaybeType, + NumberType, + Structure, + StructureType, + Type, + Type_Which, +} from "@workerd/jsg/rtti.capnp.js"; +import ts, { factory as f } from "typescript"; +import { printNode } from "../print"; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex +export function findLastIndex( + array: T[], + predicate: (value: T, index: number, array: T[]) => unknown +) { + for (let i = array.length - 1; i >= 0; i--) { + if (predicate(array[i], i, array)) return i; + } + return -1; +} + +// If `typeNode` has the shape `T | undefined`, returns `T`, otherwise returns +// `undefined`. +export function maybeUnwrapOptional( + typeNode: ts.TypeNode +): ts.TypeNode | undefined { + if ( + ts.isUnionTypeNode(typeNode) && + typeNode.types.length === 2 && + ts.isTypeReferenceNode(typeNode.types[1]) && + ts.isIdentifier(typeNode.types[1].typeName) && + typeNode.types[1].typeName.escapedText === "undefined" + ) { + return typeNode.types[0]; + } +} + +// Returns `true` iff this maybe type represents `T | null`, not `T | undefined` +function isNullMaybe(maybe: MaybeType) { + // https://github.com/cloudflare/workerd/blob/33e692f2216704b7226c8c59b1455eefedf79068/src/workerd/jsg/jsg.h#L220-L221 + return maybe.getName() === "kj::Maybe"; +} + +// Returns `true` iff this number type represents a byte +function isByteNumber(number: NumberType) { + // https://github.com/cloudflare/workerd/blob/33e692f2216704b7226c8c59b1455eefedf79068/src/workerd/jsg/rtti.h#L160 + const name = number.getName(); + return name === "char" || name === "unsigned char"; +} + +// Returns `true` iff this number type represents `number | bigint` +function isBigNumber(number: NumberType) { + // https://github.com/cloudflare/workerd/blob/33e692f2216704b7226c8c59b1455eefedf79068/src/workerd/jsg/README.md?plain=1#L56-L82 + // https://github.com/cloudflare/workerd/blob/33e692f2216704b7226c8c59b1455eefedf79068/src/workerd/jsg/rtti.h#L157-L167 + const name = number.getName(); + return ( + name === "long" || + name === "unsigned long" || + name === "long long" || + name === "unsigned long long" + ); +} + +// Returns `true` iff this array type represents a pointer to an array +function isArrayPointer(array: ArrayType) { + return array.getName() === "kj::ArrayPtr"; +} + +// Returns `true` iff this array type represents an iterable +function isIterable(array: ArrayType) { + // https://github.com/cloudflare/workerd/blob/33e692f2216704b7226c8c59b1455eefedf79068/src/workerd/jsg/README.md?plain=1#L185-L186 + return array.getName() === "jsg::Sequence"; +} + +// Returns `true` iff `typeNode` is `never` +export function isUnsatisfiable(typeNode: ts.TypeNode) { + return ( + ts.isTypeReferenceNode(typeNode) && + ts.isIdentifier(typeNode.typeName) && + typeNode.typeName.escapedText === "never" + ); +} + +// Strings to replace in fully-qualified structure names with nothing +const replaceEmpty = + /^workerd::api::public_beta::|^workerd::api::|^workerd::jsg::|::|[ >]/g; +// Strings to replace in fully-qualified structure names with an underscore +const replaceUnderscore = /[<,]/g; +export function getTypeName(structure: Structure | StructureType): string { + let name = structure.getFullyQualifiedName(); + name = name.replace(replaceEmpty, ""); + name = name.replace(replaceUnderscore, "_"); + return name; +} + +export function createParamDeclarationNodes( + args: Type[] +): ts.ParameterDeclaration[] { + // Find the index of the last required parameter, all optional before this + // will use the `| undefined` syntax, as opposed to a `?` token. + const lastRequiredParameter = findLastIndex(args, (type) => { + // Could simplify this to a single return, but this reads clearer + if (type.isMaybe() && !isNullMaybe(type.getMaybe())) { + // `type` is `T | undefined` so optional + return false; + } + // noinspection RedundantIfStatementJS + if (type.isJsgImpl()) { + // `type` is varargs or internal implementation type so optional + return false; + } + return true; + }); + + // `args` may include internal implementation types that shouldn't appear + // in parameters. Therefore, we may end up with fewer params than args. + const params: ts.ParameterDeclaration[] = []; + // Index to use in the name of the next parameter + let paramIndex = 0; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + let typeNode = createTypeNode(arg, /* forParam */ true); + + let dotDotDotToken: ts.DotDotDotToken | undefined; + let questionToken: ts.QuestionToken | undefined; + + const which = arg.which(); + if (which === Type_Which.MAYBE) { + // If this is an optional type, and we don't have any required args + // left, use an optional parameter with a `?` + const unwrappedTypeNode = maybeUnwrapOptional(typeNode); + if (unwrappedTypeNode !== undefined && i > lastRequiredParameter) { + typeNode = unwrappedTypeNode; + questionToken = f.createToken(ts.SyntaxKind.QuestionToken); + } + } else if (which === Type_Which.JSG_IMPL) { + if (arg.getJsgImpl().getType() === JsgImplType_Type.JSG_VARARGS) { + // If this is a varargs type, make sure we include `...` + assert( + ts.isArrayTypeNode(typeNode), + `Expected "T[]", got "${printNode(typeNode)}"` + ); + dotDotDotToken = f.createToken(ts.SyntaxKind.DotDotDotToken); + } else { + // If this is an internal implementation type, omit it, and skip to + // the next arg + assert( + isUnsatisfiable(typeNode), + `Expected "never", got "${printNode(typeNode)}"` + ); + continue; + } + } + + const param = f.createParameterDeclaration( + /* decorators */ undefined, + /* modifiers */ undefined, + dotDotDotToken, + // TODO(soon): use actual parameter names here once extracted + `param${paramIndex++}`, + questionToken, + typeNode + ); + params.push(param); + } + + return params; +} + +export function createTypeNode(type: Type, forParam = false): ts.TypeNode { + // noinspection FallThroughInSwitchStatementJS + switch (type.which()) { + case Type_Which.UNKNOWN: + return f.createTypeReferenceNode("any"); + case Type_Which.VOIDT: + return f.createTypeReferenceNode("void"); + case Type_Which.BOOLT: + return f.createTypeReferenceNode("boolean"); + case Type_Which.NUMBER: + const number = type.getNumber(); + if (isBigNumber(number)) { + return f.createUnionTypeNode([ + f.createTypeReferenceNode("number"), + f.createTypeReferenceNode("bigint"), + ]); + } else { + return f.createTypeReferenceNode("number"); + } + case Type_Which.PROMISE: + return f.createTypeReferenceNode("Promise", [ + createTypeNode(type.getPromise().getValue(), forParam), + ]); + case Type_Which.STRUCTURE: + return f.createTypeReferenceNode(getTypeName(type.getStructure())); + case Type_Which.STRING: + return f.createTypeReferenceNode("string"); + case Type_Which.OBJECT: + return f.createTypeReferenceNode("any"); + case Type_Which.ARRAY: + const array = type.getArray(); + const element = array.getElement(); + if (element.isNumber() && isByteNumber(element.getNumber())) { + // If the array element is a `byte`... + if (forParam) { + // When used as a method parameter, `kj::Array` and + // `kj::ArrayPtr` both mean `ArrayBuffer | ArrayBufferView` + return f.createUnionTypeNode([ + f.createTypeReferenceNode("ArrayBuffer"), + f.createTypeReferenceNode("ArrayBufferView"), + ]); + } else { + // Outside of method parameters, `kj::ArrayPtr` corresponds to + // `ArrayBufferView`, whereas `kj::Array` is `ArrayBuffer` + return f.createTypeReferenceNode( + isArrayPointer(array) ? "ArrayBufferView" : "ArrayBuffer" + ); + } + } else if (isIterable(array) && forParam) { + // If this is a `jsg::Sequence` parameter, it should accept any iterable + return f.createTypeReferenceNode("Iterable", [ + createTypeNode(element, forParam), + ]); + } else { + // Otherwise, return a regular array + return f.createArrayTypeNode(createTypeNode(element, forParam)); + } + case Type_Which.MAYBE: + const maybe = type.getMaybe(); + const alternative = isNullMaybe(maybe) ? "null" : "undefined"; + return f.createUnionTypeNode([ + createTypeNode(maybe.getValue(), forParam), + f.createTypeReferenceNode(alternative), + ]); + case Type_Which.DICT: + const dict = type.getDict(); + return f.createTypeReferenceNode("Record", [ + createTypeNode(dict.getKey(), forParam), + createTypeNode(dict.getValue(), forParam), + ]); + case Type_Which.ONE_OF: + const variants = type + .getOneOf() + .getVariants() + .map((variant) => createTypeNode(variant, forParam)); + return f.createUnionTypeNode(variants); + case Type_Which.BUILTIN: + const builtin = type.getBuiltin().getType(); + switch (builtin) { + case BuiltinType_Type.V8UINT8ARRAY: + return f.createTypeReferenceNode("Uint8Array"); + case BuiltinType_Type.V8ARRAY_BUFFER_VIEW: + return f.createTypeReferenceNode("ArrayBufferView"); + case BuiltinType_Type.JSG_BUFFER_SOURCE: + return f.createUnionTypeNode([ + f.createTypeReferenceNode("ArrayBuffer"), + f.createTypeReferenceNode("ArrayBufferView"), + ]); + case BuiltinType_Type.KJ_DATE: + return f.createTypeReferenceNode("Date"); + case BuiltinType_Type.V8FUNCTION: + return f.createTypeReferenceNode("Function"); + default: + assert.fail(`Unknown builtin type: ${builtin}`); + } + case Type_Which.INTRINSIC: + const intrinsic = type.getIntrinsic().getName(); + switch (intrinsic) { + case "v8::kErrorPrototype": + return f.createTypeReferenceNode("Error"); + case "v8::kIteratorPrototype": + return f.createTypeReferenceNode("Iterator", [ + f.createTypeReferenceNode("unknown"), + ]); + case "v8::kAsyncIteratorPrototype": + return f.createTypeReferenceNode("AsyncIterator", [ + f.createTypeReferenceNode("unknown"), + ]); + default: + assert.fail(`Unknown intrinsic type: ${intrinsic}`); + } + case Type_Which.FUNCTION: + const func = type.getFunction(); + const params = createParamDeclarationNodes(func.getArgs().toArray()); + const result = createTypeNode(func.getReturnType()); + return f.createFunctionTypeNode( + /* typeParams */ undefined, + params, + result + ); + case Type_Which.JSG_IMPL: + const impl = type.getJsgImpl().getType(); + switch (impl) { + case JsgImplType_Type.CONFIGURATION: + case JsgImplType_Type.V8ISOLATE: + case JsgImplType_Type.JSG_LOCK: + case JsgImplType_Type.JSG_TYPE_HANDLER: + case JsgImplType_Type.JSG_UNIMPLEMENTED: + case JsgImplType_Type.JSG_SELF_REF: + case JsgImplType_Type.V8FUNCTION_CALLBACK_INFO: + case JsgImplType_Type.V8PROPERTY_CALLBACK_INFO: + // All these types should be omitted from function parameters + return f.createTypeReferenceNode("never"); + case JsgImplType_Type.JSG_VARARGS: + return f.createArrayTypeNode(f.createTypeReferenceNode("any")); + default: + assert.fail(`Unknown JSG implementation type: ${impl}`); + } + default: + assert.fail(`Unknown type: ${type.which()}`); + } +} diff --git a/types/src/index.ts b/types/src/index.ts new file mode 100644 index 00000000000..d349e8d76ca --- /dev/null +++ b/types/src/index.ts @@ -0,0 +1,76 @@ +#!/usr/bin/env node +import assert from "assert"; +import { mkdir, readFile, writeFile } from "fs/promises"; +import path from "path"; +import { arrayBuffer } from "stream/consumers"; +import util from "util"; +import { DefinitionGeneratorRequest } from "@workerd/jsg/rtti.capnp.js"; +import { Message } from "capnp-ts"; +import prettier from "prettier"; +import ts from "typescript"; +import { generateDefinitions } from "./generator"; +import { printNodeList, printer } from "./print"; +import { createMemoryProgram } from "./program"; + +const definitionsHeader = `/* eslint-disable */ +// noinspection JSUnusedGlobalSymbols +`; + +function printDefinitions(req: DefinitionGeneratorRequest): string { + // Generate TypeScript nodes from capnp request + const nodes = generateDefinitions(req); + + // Build TypeScript program from nodes + const source = printNodeList(nodes); + // TODO(soon): when we switch to outputting a separate file per group, we'll + // need to modify this function to accept multiple source files + // (will probably need `program.getSourceFiles()`) + const [program, sourcePath] = createMemoryProgram(source); + const checker = program.getTypeChecker(); + const sourceFile = program.getSourceFile(sourcePath); + assert(sourceFile !== undefined); + + // Run post-processing transforms on program + const result = ts.transform(sourceFile, []); + // TODO(polish): maybe log diagnostics with `ts.getPreEmitDiagnostics(program, sourceFile)`? + // (see https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler) + assert.strictEqual(result.transformed.length, 1); + + // Print program to string + return definitionsHeader + printer.printFile(result.transformed[0]); +} + +async function main() { + const { values: options, positionals } = util.parseArgs({ + options: { + output: { type: "string", short: "o" }, + format: { type: "boolean", short: "f" }, + }, + strict: true, + allowPositionals: true, + }); + const maybeInputPath = positionals[0]; + + const buffer = + maybeInputPath === undefined + ? await arrayBuffer(process.stdin) + : await readFile(maybeInputPath); + const message = new Message(buffer, /* packed */ false); + const req = message.getRoot(DefinitionGeneratorRequest); + + let definitions = printDefinitions(req); + if (options.format) { + definitions = prettier.format(definitions, { parser: "typescript" }); + } + if (options.output !== undefined) { + const output = path.resolve(options.output); + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, definitions); + } else { + // Write to stdout without extra newline + process.stdout.write(definitions); + } +} + +// Outputting to a CommonJS module so can't use top-level await +if (require.main === module) void main(); diff --git a/types/src/print.ts b/types/src/print.ts new file mode 100644 index 00000000000..04fb8a597a8 --- /dev/null +++ b/types/src/print.ts @@ -0,0 +1,22 @@ +import ts, { factory as f } from "typescript"; + +const placeholderFile = ts.createSourceFile( + "placeholder.ts", // File name doesn't matter here + "", + ts.ScriptTarget.ESNext, + false, + ts.ScriptKind.TS +); +export const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); + +export function printNode(node: ts.Node): string { + return printer.printNode(ts.EmitHint.Unspecified, node, placeholderFile); +} + +export function printNodeList(nodes: ts.Node[]): string { + return printer.printList( + ts.ListFormat.MultiLine, + f.createNodeArray(nodes), + placeholderFile + ); +} diff --git a/types/src/program.ts b/types/src/program.ts new file mode 100644 index 00000000000..d1282701381 --- /dev/null +++ b/types/src/program.ts @@ -0,0 +1,35 @@ +import path from "path"; +import ts from "typescript"; + +export function createMemoryProgram(source: string): [ts.Program, string] { + const options = ts.getDefaultCompilerOptions(); + const host = ts.createCompilerHost(options, true); + + const sourcePath = path.resolve(__dirname, "source.ts"); + const sourceFile = ts.createSourceFile( + sourcePath, + source, + ts.ScriptTarget.ESNext, + false, + ts.ScriptKind.TS + ); + + // Update compiler host to return in-memory source file + function patchHostMethod< + K extends "fileExists" | "readFile" | "getSourceFile" + >(key: K, placeholderResult: ReturnType) { + const originalMethod: (...args: any[]) => any = host[key]; + host[key] = (fileName: string, ...args: any[]) => { + if (path.resolve(fileName) === sourcePath) { + return placeholderResult; + } + return originalMethod.call(host, fileName, ...args); + }; + } + patchHostMethod("fileExists", true); + patchHostMethod("readFile", source); + patchHostMethod("getSourceFile", sourceFile); + + const program = ts.createProgram([sourcePath], options, host); + return [program, sourcePath]; +} diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 00000000000..1adc4056962 --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "esnext", + "lib": ["esnext"], + "strict": true, + "moduleResolution": "node", + "esModuleInterop": true, + "types": ["node"], + "outDir": "dist", + "sourceMap": true, + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@workerd/*": ["../bazel-bin/src/workerd/*"], + }, + "checkJs": true, + "allowJs": true + }, + "include": [ + "src/**/*.ts" + ] +} From b0db6e6241a1c16ad2ac9b966d066fcc0ef20b95 Mon Sep 17 00:00:00 2001 From: bcoll Date: Sat, 15 Oct 2022 19:45:12 +0100 Subject: [PATCH 4/5] Replace `Iterator`-like interfaces with built-in `Iterator`s --- types/src/index.ts | 3 +- types/src/transforms/index.ts | 1 + types/src/transforms/iterators.ts | 279 ++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 types/src/transforms/index.ts create mode 100644 types/src/transforms/iterators.ts diff --git a/types/src/index.ts b/types/src/index.ts index d349e8d76ca..eecfeba757a 100644 --- a/types/src/index.ts +++ b/types/src/index.ts @@ -11,6 +11,7 @@ import ts from "typescript"; import { generateDefinitions } from "./generator"; import { printNodeList, printer } from "./print"; import { createMemoryProgram } from "./program"; +import { createIteratorTransformer } from "./transforms"; const definitionsHeader = `/* eslint-disable */ // noinspection JSUnusedGlobalSymbols @@ -31,7 +32,7 @@ function printDefinitions(req: DefinitionGeneratorRequest): string { assert(sourceFile !== undefined); // Run post-processing transforms on program - const result = ts.transform(sourceFile, []); + const result = ts.transform(sourceFile, [createIteratorTransformer(checker)]); // TODO(polish): maybe log diagnostics with `ts.getPreEmitDiagnostics(program, sourceFile)`? // (see https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler) assert.strictEqual(result.transformed.length, 1); diff --git a/types/src/transforms/index.ts b/types/src/transforms/index.ts new file mode 100644 index 00000000000..0c73b73ed31 --- /dev/null +++ b/types/src/transforms/index.ts @@ -0,0 +1 @@ +export * from "./iterators"; diff --git a/types/src/transforms/iterators.ts b/types/src/transforms/iterators.ts new file mode 100644 index 00000000000..0f662b4543b --- /dev/null +++ b/types/src/transforms/iterators.ts @@ -0,0 +1,279 @@ +import assert from "assert"; +import ts from "typescript"; +import { printNode } from "../print"; + +// Replaces custom Iterator-like interfaces with built-in `Iterator` types: +// +// ```ts +// export class Thing { +// readonly things: ThingIterator; +// asyncThings(): AsyncThingIterator; +// } +// +// export interface ThingIterator extends Iterator { +// next(): ThingIteratorNext; +// [Symbol.iterator](): any; +// } +// export interface ThingIteratorNext { +// done: boolean; +// value?: string; +// } +// +// export interface AsyncThingIterator extends AsyncIterator { +// next(): Promise; +// return(value?: any): Promise; +// [Symbol.asyncIterator](): any; +// } +// export interface AsyncThingIteratorNext { +// done: boolean; +// value?: number; +// } +// ``` +// +// --- transforms to ---> +// +// ```ts +// export class Thing { +// readonly things: IterableIterator; +// asyncThings(): AsyncIterableIterator; +// } +// ``` +export function createIteratorTransformer( + checker: ts.TypeChecker +): ts.TransformerFactory { + return (ctx) => { + return (node) => { + const iteratorCtx: IteratorTransformContext = { + types: new Map(), + nextInterfaces: new Set(), + }; + const v1 = createIteratorDeclarationsVisitor(ctx, checker, iteratorCtx); + const v2 = createIteratorUsagesVisitor(ctx, checker, iteratorCtx); + node = ts.visitEachChild(node, v1, ctx); + return ts.visitEachChild(node, v2, ctx); + }; + }; +} + +interface IteratorTransformContext { + // Maps iterator-like interfaces to built-in `Iterator` types + types: Map; + // Set of iterator-next interfaces to remove + nextInterfaces: Set; +} + +// Find all interfaces extending `Iterator`, record their next value type, +// and remove them. Also record the names of next interfaces for removal. +function createIteratorDeclarationsVisitor( + ctx: ts.TransformationContext, + checker: ts.TypeChecker, + iteratorCtx: IteratorTransformContext +): ts.Visitor { + return (node) => { + if (ts.isInterfaceDeclaration(node)) { + // Check if interface extends `Iterator` + const extendsNode = node.heritageClauses?.[0]; + if ( + extendsNode?.token === ts.SyntaxKind.ExtendsKeyword && + extendsNode.types.length === 1 && + ts.isIdentifier(extendsNode.types[0].expression) && + (extendsNode.types[0].expression.text === "Iterator" || + extendsNode.types[0].expression.text === "AsyncIterator") + ) { + const isAsync = extendsNode.types[0].expression.text !== "Iterator"; + // Check `node` has one of the following shapes: + // ```ts + // export interface ThingIterator extends Iterator { + // next(): ThingIteratorNext; + // [Symbol.iterator](): any; + // } + // export interface AsyncThingIterator extends AsyncIterator { + // next(): Promise; + // return(value?: any): Promise; + // [Symbol.asyncIterator](): any; + // } + // ``` + assert( + node.members.length === (isAsync ? 3 : 2) && + node.members[0].name !== undefined && + ts.isMethodSignature(node.members[0]) && + ts.isIdentifier(node.members[0].name) && + node.members[0].name.text === "next" && + node.members[0].type !== undefined, + `Expected iterator-like interface, got "${printNode(node)}"` + ); + // Extract `IteratorBase_ThingIterator_...Next` type + let nextTypeNode = node.members[0].type; + if (isAsync) { + // Unwrap Promise type + assert( + ts.isTypeReferenceNode(nextTypeNode) && + ts.isIdentifier(nextTypeNode.typeName) && + nextTypeNode.typeName.text === "Promise" && + nextTypeNode.typeArguments?.length === 1, + `Expected Promise, got "${printNode(nextTypeNode)}"` + ); + nextTypeNode = nextTypeNode.typeArguments[0]; + } + + // Check `IteratorBase_ThingIterator_...Next` has the following shape, + // and extract the `value?: T` declaration + // ```ts + // export interface ThingIteratorNext { + // done: boolean; + // value?: string; + // } + // ``` + const nextType = checker.getTypeFromTypeNode(nextTypeNode); + const nextTypeSymbol = nextType.getSymbol(); + assert(nextTypeSymbol?.members !== undefined); + let nextValueSymbol: ts.Symbol | undefined; + nextTypeSymbol.members.forEach((value, key) => { + if (key === "value") nextValueSymbol = value; + }); + assert(nextValueSymbol !== undefined); + const nextValueDeclarations = nextValueSymbol.getDeclarations(); + assert.strictEqual(nextValueDeclarations?.length, 1); + const nextValueDeclaration = nextValueDeclarations[0]; + assert(ts.isPropertySignature(nextValueDeclaration)); + // Mark this interface for removal + iteratorCtx.nextInterfaces.add(nextTypeSymbol); + + // Extract `value`'s type + const nextValueType = nextValueDeclaration.type; + assert(nextValueType !== undefined); + + // Record this iterator type... + const nodeType = checker.getTypeAtLocation(node); + const nodeSymbol = nodeType.getSymbol(); + assert(nodeSymbol !== undefined); + const iteratorType = ctx.factory.createTypeReferenceNode( + isAsync ? "AsyncIterableIterator" : "IterableIterator", + [nextValueType] + ); + iteratorCtx.types.set(nodeSymbol, iteratorType); + // ...and remove the node by returning `undefined` + return; + } + } + + return node; + }; +} + +// Replace uses of iterator interfaces with built-in iterator type. +// Also remove all previously recorded next interfaces. +function createIteratorUsagesVisitor( + ctx: ts.TransformationContext, + checker: ts.TypeChecker, + iteratorCtx: IteratorTransformContext +): ts.Visitor { + // Find the built-in iterator type associated with a method's return type + // or property's type + function findIteratorType( + node: + | ts.MethodSignature + | ts.MethodDeclaration + | ts.PropertySignature + | ts.PropertyDeclaration + | ts.GetAccessorDeclaration + ): ts.TypeNode | undefined { + if (node.type === undefined) return; + const type = checker.getTypeFromTypeNode(node.type); + const typeSymbol = type.getSymbol(); + if (typeSymbol !== undefined) return iteratorCtx.types.get(typeSymbol); + } + + const visitor: ts.Visitor = (node) => { + // Remove all next interfaces by returning `undefined` + if (ts.isInterfaceDeclaration(node)) { + const type = checker.getTypeAtLocation(node); + const symbol = type.getSymbol(); + if (symbol !== undefined && iteratorCtx.nextInterfaces.has(symbol)) { + return; + } + } + + // Visit all interface/class declaration children + if (ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node)) { + return ts.visitEachChild(node, visitor, ctx); + } + + // Replace all method return types and property types referencing iterators + // with the built-in type + if (ts.isMethodSignature(node)) { + const iteratorType = findIteratorType(node); + if (iteratorType !== undefined) { + return ctx.factory.updateMethodSignature( + node, + node.modifiers, + node.name, + node.questionToken, + node.typeParameters, + node.parameters, + iteratorType + ); + } + } + if (ts.isMethodDeclaration(node)) { + const iteratorType = findIteratorType(node); + if (iteratorType !== undefined) { + return ctx.factory.updateMethodDeclaration( + node, + node.decorators, + node.modifiers, + node.asteriskToken, + node.name, + node.questionToken, + node.typeParameters, + node.parameters, + iteratorType, + node.body + ); + } + } + if (ts.isPropertySignature(node)) { + const iteratorType = findIteratorType(node); + if (iteratorType !== undefined) { + return ctx.factory.updatePropertySignature( + node, + node.modifiers, + node.name, + node.questionToken, + iteratorType + ); + } + } + if (ts.isPropertyDeclaration(node)) { + const iteratorType = findIteratorType(node); + if (iteratorType !== undefined) { + return ctx.factory.updatePropertyDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.questionToken ?? node.exclamationToken, + iteratorType, + node.initializer + ); + } + } + if (ts.isGetAccessorDeclaration(node)) { + const iteratorType = findIteratorType(node); + if (iteratorType !== undefined) { + return ctx.factory.updateGetAccessorDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.parameters, + iteratorType, + node.body + ); + } + } + + return node; + }; + return visitor; +} From f29773097a63586c023993743037fd541a8aa756 Mon Sep 17 00:00:00 2001 From: bcoll Date: Sat, 15 Oct 2022 19:46:11 +0100 Subject: [PATCH 5/5] Extract global types in global scope --- types/src/index.ts | 12 ++- types/src/transforms/globals.ts | 153 ++++++++++++++++++++++++++++++++ types/src/transforms/index.ts | 1 + 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 types/src/transforms/globals.ts diff --git a/types/src/index.ts b/types/src/index.ts index eecfeba757a..123c65abbff 100644 --- a/types/src/index.ts +++ b/types/src/index.ts @@ -11,7 +11,10 @@ import ts from "typescript"; import { generateDefinitions } from "./generator"; import { printNodeList, printer } from "./print"; import { createMemoryProgram } from "./program"; -import { createIteratorTransformer } from "./transforms"; +import { + createGlobalScopeTransformer, + createIteratorTransformer, +} from "./transforms"; const definitionsHeader = `/* eslint-disable */ // noinspection JSUnusedGlobalSymbols @@ -32,7 +35,12 @@ function printDefinitions(req: DefinitionGeneratorRequest): string { assert(sourceFile !== undefined); // Run post-processing transforms on program - const result = ts.transform(sourceFile, [createIteratorTransformer(checker)]); + const result = ts.transform(sourceFile, [ + // TODO(soon): when overrides are implemented, apply renames here + createIteratorTransformer(checker), + createGlobalScopeTransformer(checker), + // TODO(polish): maybe flatten union types? + ]); // TODO(polish): maybe log diagnostics with `ts.getPreEmitDiagnostics(program, sourceFile)`? // (see https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#a-minimal-compiler) assert.strictEqual(result.transformed.length, 1); diff --git a/types/src/transforms/globals.ts b/types/src/transforms/globals.ts new file mode 100644 index 00000000000..3279ac14b73 --- /dev/null +++ b/types/src/transforms/globals.ts @@ -0,0 +1,153 @@ +import assert from "assert"; +import ts from "typescript"; + +// Copies all properties of `ServiceWorkerGlobalScope` and its superclasses into +// the global scope: +// +// ```ts +// export declare class EventTarget { +// constructor(); +// addEventListener(...): ...; +// } +// export declare abstract WorkerGlobalScope extends EventTarget { +// ... +// } +// export interface ServiceWorkerGlobalScope extends WorkerGlobalScope { +// DOMException: typeof DOMException; +// btoa(value: string): string; +// crypto: Crypto; +// ... +// } +// ``` +// +// --- transforms to ---> +// +// ```ts +// export declare class EventTarget { ... } +// export declare abstract WorkerGlobalScope extends EventTarget { ... } +// export interface ServiceWorkerGlobalScope extends WorkerGlobalScope { ... } +// +// export declare function addEventListener(...): ...; +// export declare function btoa(value: string): string; +// export declare const crypto: Crypto; +// ``` +export function createGlobalScopeTransformer( + checker: ts.TypeChecker +): ts.TransformerFactory { + return (ctx) => { + return (node) => { + const visitor = createGlobalScopeVisitor(ctx, checker); + return ts.visitEachChild(node, visitor, ctx); + }; + }; +} + +export function createGlobalScopeVisitor( + ctx: ts.TransformationContext, + checker: ts.TypeChecker +) { + // Call with each potential method/property that could be extracted into a + // global function/const. + function maybeExtractGlobalNode(node: ts.Node): ts.Node | undefined { + if ( + (ts.isMethodSignature(node) || ts.isMethodDeclaration(node)) && + ts.isIdentifier(node.name) + ) { + const modifiers: ts.Modifier[] = [ + ctx.factory.createToken(ts.SyntaxKind.ExportKeyword), + ctx.factory.createToken(ts.SyntaxKind.DeclareKeyword), + ]; + return ctx.factory.createFunctionDeclaration( + /* decorators */ undefined, + modifiers, + /* asteriskToken */ undefined, + node.name, + node.typeParameters, + node.parameters, + node.type, + /* body */ undefined + ); + } + if ( + (ts.isPropertySignature(node) || + ts.isPropertyDeclaration(node) || + ts.isGetAccessorDeclaration(node)) && + ts.isIdentifier(node.name) + ) { + assert(node.type !== undefined); + if (!ts.isTypeQueryNode(node.type)) { + const modifiers: ts.Modifier[] = [ + ctx.factory.createToken(ts.SyntaxKind.ExportKeyword), + ctx.factory.createToken(ts.SyntaxKind.DeclareKeyword), + ]; + const varDeclaration = ctx.factory.createVariableDeclaration( + node.name, + /* exclamationToken */ undefined, + node.type + ); + const varDeclarationList = ctx.factory.createVariableDeclarationList( + [varDeclaration], + ts.NodeFlags.Const // Use `const` instead of `var` + ); + return ctx.factory.createVariableStatement( + modifiers, + varDeclarationList + ); + } + } + } + + // Called with each class/interface that should have its methods/properties + // extracted into global functions/consts. Recursively visits superclasses. + function extractGlobalNodes( + node: ts.InterfaceDeclaration | ts.ClassDeclaration + ): ts.Node[] { + const nodes: ts.Node[] = []; + + // Recursively extract from all superclasses + if (node.heritageClauses !== undefined) { + for (const clause of node.heritageClauses) { + for (const superType of clause.types) { + // TODO(soon): when overrides are implemented, superclasses may + // define type parameters (e.g. `EventTarget`). + // In these cases, we'll need to inline these type params in + // extracted definitions. Type parameters are in `superType.typeArguments`. + const superTypeSymbol = checker.getSymbolAtLocation( + superType.expression + ); + assert(superTypeSymbol !== undefined); + const superTypeDeclarations = superTypeSymbol.getDeclarations(); + assert.strictEqual(superTypeDeclarations?.length, 1); + const superTypeDeclaration = superTypeDeclarations[0]; + assert( + ts.isInterfaceDeclaration(superTypeDeclaration) || + ts.isClassDeclaration(superTypeDeclaration) + ); + nodes.push(...extractGlobalNodes(superTypeDeclaration)); + } + } + } + + // Extract methods/properties + for (const member of node.members) { + const maybeNode = maybeExtractGlobalNode(member); + if (maybeNode !== undefined) nodes.push(maybeNode); + } + + return nodes; + } + + // Finds the `ServiceWorkerGlobalScope` declaration, calls + // `extractGlobalNodes` with it, and inserts all extracted nodes. + const serviceWorkerGlobalScopeVisitor: ts.Visitor = (node) => { + if ( + (ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node)) && + node.name !== undefined && + node.name.text === "ServiceWorkerGlobalScope" + ) { + return [node, ...extractGlobalNodes(node)]; + } + return node; + }; + return serviceWorkerGlobalScopeVisitor; +} diff --git a/types/src/transforms/index.ts b/types/src/transforms/index.ts index 0c73b73ed31..4620cf07c1d 100644 --- a/types/src/transforms/index.ts +++ b/types/src/transforms/index.ts @@ -1 +1,2 @@ +export * from "./globals"; export * from "./iterators";